1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*++
6
7
8
9Module Name:
10
11 debug.c
12
13Abstract:
14
15 Implementation of Win32 debugging API functions.
16
17Revision History:
18
19
20
21--*/
22
23#ifndef BIT64
24#undef _LARGEFILE64_SOURCE
25#undef _FILE_OFFSET_BITS
26#endif
27
28#include "pal/dbgmsg.h"
29SET_DEFAULT_DEBUG_CHANNEL(DEBUG); // some headers have code with asserts, so do this first
30
31#include "pal/thread.hpp"
32#include "pal/procobj.hpp"
33#include "pal/file.hpp"
34
35#include "pal/palinternal.h"
36#include "pal/process.h"
37#include "pal/context.h"
38#include "pal/debug.h"
39#include "pal/environ.h"
40#include "pal/malloc.hpp"
41#include "pal/module.h"
42#include "pal/stackstring.hpp"
43#include "pal/virtual.h"
44#include "pal/utils.h"
45
46#include <signal.h>
47#include <unistd.h>
48#if HAVE_PROCFS_CTL
49#include <unistd.h>
50#elif HAVE_TTRACE // HAVE_PROCFS_CTL
51#include <sys/ttrace.h>
52#else // HAVE_TTRACE
53#include <sys/ptrace.h>
54#endif // HAVE_PROCFS_CTL
55#if HAVE_VM_READ
56#include <mach/mach.h>
57#endif // HAVE_VM_READ
58#include <errno.h>
59#include <sys/types.h>
60#include <sys/wait.h>
61
62#if HAVE_PROCFS_H
63#include <procfs.h>
64#endif // HAVE_PROCFS_H
65
66#if HAVE_MACH_EXCEPTIONS
67#include "../exception/machexception.h"
68#endif // HAVE_MACH_EXCEPTIONS
69
70using namespace CorUnix;
71
72extern "C" void DBG_DebugBreak_End();
73
74#if HAVE_PROCFS_CTL
75#define CTL_ATTACH "attach"
76#define CTL_DETACH "detach"
77#define CTL_WAIT "wait"
78#endif // HAVE_PROCFS_CTL
79
80/* ------------------- Constant definitions ----------------------------------*/
81
82#if !HAVE_VM_READ && !HAVE_PROCFS_CTL
83const BOOL DBG_ATTACH = TRUE;
84const BOOL DBG_DETACH = FALSE;
85#endif
86static const char PAL_OUTPUTDEBUGSTRING[] = "PAL_OUTPUTDEBUGSTRING";
87
88#ifdef _DEBUG
89#define ENABLE_RUN_ON_DEBUG_BREAK 1
90#endif // _DEBUG
91
92#ifdef ENABLE_RUN_ON_DEBUG_BREAK
93static const char PAL_RUN_ON_DEBUG_BREAK[] = "PAL_RUN_ON_DEBUG_BREAK";
94#endif // ENABLE_RUN_ON_DEBUG_BREAK
95
96extern "C" {
97
98/*++
99Function:
100 FlushInstructionCache
101
102The FlushInstructionCache function flushes the instruction cache for
103the specified process.
104
105Remarks
106
107This is a no-op for x86 architectures where the instruction and data
108caches are coherent in hardware. For non-X86 architectures, this call
109usually maps to a kernel API to flush the D-caches on all processors.
110
111--*/
112BOOL
113PALAPI
114FlushInstructionCache(
115 IN HANDLE hProcess,
116 IN LPCVOID lpBaseAddress,
117 IN SIZE_T dwSize)
118{
119 BOOL Ret;
120
121 PERF_ENTRY(FlushInstructionCache);
122 ENTRY("FlushInstructionCache (hProcess=%p, lpBaseAddress=%p dwSize=%d)\
123 \n", hProcess, lpBaseAddress, dwSize);
124
125 if (lpBaseAddress != NULL)
126 {
127 Ret = DBG_FlushInstructionCache(lpBaseAddress, dwSize);
128 }
129 else
130 {
131 Ret = TRUE;
132 }
133
134 LOGEXIT("FlushInstructionCache returns BOOL %d\n", Ret);
135 PERF_EXIT(FlushInstructionCache);
136 return Ret;
137}
138
139
140/*++
141Function:
142 OutputDebugStringA
143
144See MSDN doc.
145--*/
146VOID
147PALAPI
148OutputDebugStringA(
149 IN LPCSTR lpOutputString)
150{
151 PERF_ENTRY(OutputDebugStringA);
152 ENTRY("OutputDebugStringA (lpOutputString=%p (%s))\n",
153 lpOutputString ? lpOutputString : "NULL",
154 lpOutputString ? lpOutputString : "NULL");
155
156 // As we don't support debug events, we are going to output the debug string
157 // to stderr instead of generating OUT_DEBUG_STRING_EVENT. It's safe to tell
158 // EnvironGetenv not to make a copy of the value here since we only want to
159 // check whether it exists, not actually use it.
160 if ((lpOutputString != NULL) &&
161 (NULL != EnvironGetenv(PAL_OUTPUTDEBUGSTRING, /* copyValue */ FALSE)))
162 {
163 fprintf(stderr, "%s", lpOutputString);
164 }
165
166 LOGEXIT("OutputDebugStringA returns\n");
167 PERF_EXIT(OutputDebugStringA);
168}
169
170/*++
171Function:
172 OutputDebugStringW
173
174See MSDN doc.
175--*/
176VOID
177PALAPI
178OutputDebugStringW(
179 IN LPCWSTR lpOutputString)
180{
181 CHAR *lpOutputStringA;
182 int strLen;
183
184 PERF_ENTRY(OutputDebugStringW);
185 ENTRY("OutputDebugStringW (lpOutputString=%p (%S))\n",
186 lpOutputString ? lpOutputString: W16_NULLSTRING,
187 lpOutputString ? lpOutputString: W16_NULLSTRING);
188
189 if (lpOutputString == NULL)
190 {
191 OutputDebugStringA("");
192 goto EXIT;
193 }
194
195 if ((strLen = WideCharToMultiByte(CP_ACP, 0, lpOutputString, -1, NULL, 0,
196 NULL, NULL))
197 == 0)
198 {
199 ASSERT("failed to get wide chars length\n");
200 SetLastError(ERROR_INTERNAL_ERROR);
201 goto EXIT;
202 }
203
204 /* strLen includes the null terminator */
205 if ((lpOutputStringA = (LPSTR) InternalMalloc((strLen * sizeof(CHAR)))) == NULL)
206 {
207 ERROR("Insufficient memory available !\n");
208 SetLastError(ERROR_NOT_ENOUGH_MEMORY);
209 goto EXIT;
210 }
211
212 if(! WideCharToMultiByte(CP_ACP, 0, lpOutputString, -1,
213 lpOutputStringA, strLen, NULL, NULL))
214 {
215 ASSERT("failed to convert wide chars to multibytes\n");
216 SetLastError(ERROR_INTERNAL_ERROR);
217 free(lpOutputStringA);
218 goto EXIT;
219 }
220
221 OutputDebugStringA(lpOutputStringA);
222 free(lpOutputStringA);
223
224EXIT:
225 LOGEXIT("OutputDebugStringW returns\n");
226 PERF_EXIT(OutputDebugStringW);
227}
228
229#ifdef ENABLE_RUN_ON_DEBUG_BREAK
230/*
231 When DebugBreak() is called, if PAL_RUN_ON_DEBUG_BREAK is set,
232 DebugBreak() will execute whatever command is in there.
233
234 PAL_RUN_ON_DEBUG_BREAK must be no longer than 255 characters.
235
236 This command string inherits the current process's environment,
237 with two additions:
238 PAL_EXE_PID - the process ID of the current process
239 PAL_EXE_NAME - the name of the executable of the current process
240
241 When DebugBreak() runs this string, it periodically polls the child process
242 and blocks until it finishes. If you use this mechanism to start a
243 debugger, you can break this poll loop by setting the "spin" variable in
244 run_debug_command()'s frame to 0, and then the parent process can
245 continue.
246
247 suggested values for PAL_RUN_ON_DEBUG_BREAK:
248 to halt the process for later inspection:
249 'echo stopping $PAL_EXE_PID; kill -STOP $PAL_EXE_PID; sleep 10'
250
251 to print out the stack trace:
252 'pstack $PAL_EXE_PID'
253
254 to invoke the gdb debugger on the process:
255 'set -x; gdb $PAL_EXE_NAME $PAL_EXE_PID'
256
257 to invoke the ddd debugger on the process (requires X11):
258 'set -x; ddd $PAL_EXE_NAME $PAL_EXE_PID'
259*/
260
261static
262int
263run_debug_command (const char *command)
264{
265 int pid;
266 Volatile<int> spin = 1;
267
268 if (!command) {
269 return 1;
270 }
271
272 printf("Spawning command: %s\n", command);
273
274 pid = fork();
275 if (pid == -1) {
276 return -1;
277 }
278 if (pid == 0) {
279 const char *argv[4] = { "sh", "-c", command, 0 };
280 execv("/bin/sh", (char **)argv);
281 exit(127);
282 }
283
284 /* We continue either when the spawned process has stopped, or when
285 an attached debugger sets spin to 0 */
286 while (spin != 0) {
287 int status = 0;
288 int ret = waitpid(pid, &status, WNOHANG);
289 if (ret == 0) {
290 int i;
291 /* I tried to use sleep for this, and that works everywhere except
292 FreeBSD. The problem on FreeBSD is that if the process gets a
293 signal while blocked in sleep(), gdb is confused by the stack */
294 for (i = 0; i < 1000000; i++)
295 ;
296 }
297 else if (ret == -1) {
298 if (errno != EINTR) {
299 return -1;
300 }
301 }
302 else if (WIFEXITED(status)) {
303 return WEXITSTATUS(status);
304 }
305 else {
306 fprintf (stderr, "unexpected return from waitpid\n");
307 return -1;
308 }
309 };
310 return 0;
311}
312#endif // ENABLE_RUN_ON_DEBUG_BREAK
313
314#define PID_TEXT "PAL_EXE_PID="
315#define EXE_TEXT "PAL_EXE_NAME="
316
317static
318int
319DebugBreakCommand()
320{
321#ifdef ENABLE_RUN_ON_DEBUG_BREAK
322 extern MODSTRUCT exe_module;
323
324 char *command_string = EnvironGetenv(PAL_RUN_ON_DEBUG_BREAK);
325 if (command_string)
326 {
327 char pid_buf[sizeof (PID_TEXT) + 32];
328 PathCharString exe_bufString;
329 int libNameLength = 10;
330
331 if (exe_module.lib_name != NULL)
332 {
333 libNameLength = PAL_wcslen(exe_module.lib_name);
334 }
335
336 SIZE_T dwexe_buf = strlen(EXE_TEXT) + libNameLength + 1;
337 CHAR * exe_buf = exe_bufString.OpenStringBuffer(dwexe_buf);
338
339 if (NULL == exe_buf)
340 {
341 goto FAILED;
342 }
343
344 if (snprintf (pid_buf, sizeof (pid_buf), PID_TEXT "%d", getpid()) <= 0)
345 {
346 goto FAILED;
347 }
348
349 if (snprintf (exe_buf, sizeof (CHAR) * (dwexe_buf + 1), EXE_TEXT "%ls", (wchar_t *)exe_module.lib_name) <= 0)
350 {
351 goto FAILED;
352 }
353
354 exe_bufString.CloseBuffer(dwexe_buf);
355 /* strictly speaking, we might want to only set these environment
356 variables in the child process, but if we do that we can't check
357 for errors. putenv/setenv can fail when out of memory */
358
359 if (!EnvironPutenv (pid_buf, FALSE) || !EnvironPutenv (exe_buf, FALSE))
360 {
361 goto FAILED;
362 }
363
364 if (run_debug_command (command_string))
365 {
366 goto FAILED;
367 }
368
369 free(command_string);
370 return 1;
371 }
372
373 return 0;
374
375FAILED:
376 if (command_string)
377 {
378 free(command_string);
379 }
380
381 fprintf (stderr, "Failed to execute command: '%s'\n", command_string);
382 return -1;
383#else // ENABLE_RUN_ON_DEBUG_BREAK
384 return 0;
385#endif // ENABLE_RUN_ON_DEBUG_BREAK
386}
387
388/*++
389Function:
390 DebugBreak
391
392See MSDN doc.
393--*/
394VOID
395PALAPI
396DebugBreak(
397 VOID)
398{
399 PERF_ENTRY(DebugBreak);
400 ENTRY("DebugBreak()\n");
401
402 if (DebugBreakCommand() <= 0) {
403 // either didn't do anything, or failed
404 TRACE("Calling DBG_DebugBreak\n");
405 DBG_DebugBreak();
406 }
407
408 LOGEXIT("DebugBreak returns\n");
409 PERF_EXIT(DebugBreak);
410}
411
412/*++
413Function:
414 IsInDebugBreak(addr)
415
416 Returns true if the address is in DBG_DebugBreak.
417
418--*/
419BOOL
420IsInDebugBreak(void *addr)
421{
422 return (addr >= (void *)DBG_DebugBreak) && (addr <= (void *)DBG_DebugBreak_End);
423}
424
425/*++
426Function:
427 GetThreadContext
428
429See MSDN doc.
430--*/
431BOOL
432PALAPI
433GetThreadContext(
434 IN HANDLE hThread,
435 IN OUT LPCONTEXT lpContext)
436{
437 PAL_ERROR palError;
438 CPalThread *pThread;
439 CPalThread *pTargetThread;
440 IPalObject *pobjThread = NULL;
441 BOOL ret = FALSE;
442
443 PERF_ENTRY(GetThreadContext);
444 ENTRY("GetThreadContext (hThread=%p, lpContext=%p)\n",hThread,lpContext);
445
446 pThread = InternalGetCurrentThread();
447
448 palError = InternalGetThreadDataFromHandle(
449 pThread,
450 hThread,
451 0, // THREAD_GET_CONTEXT
452 &pTargetThread,
453 &pobjThread
454 );
455
456 if (NO_ERROR == palError)
457 {
458 if (!pTargetThread->IsDummy())
459 {
460 ret = CONTEXT_GetThreadContext(
461 GetCurrentProcessId(),
462 pTargetThread->GetPThreadSelf(),
463 lpContext
464 );
465 }
466 else
467 {
468 ASSERT("Dummy thread handle passed to GetThreadContext\n");
469 pThread->SetLastError(ERROR_INVALID_HANDLE);
470 }
471 }
472 else
473 {
474 pThread->SetLastError(palError);
475 }
476
477 if (NULL != pobjThread)
478 {
479 pobjThread->ReleaseReference(pThread);
480 }
481
482 LOGEXIT("GetThreadContext returns ret:%d\n", ret);
483 PERF_EXIT(GetThreadContext);
484 return ret;
485}
486
487/*++
488Function:
489 SetThreadContext
490
491See MSDN doc.
492--*/
493BOOL
494PALAPI
495SetThreadContext(
496 IN HANDLE hThread,
497 IN CONST CONTEXT *lpContext)
498{
499 PAL_ERROR palError;
500 CPalThread *pThread;
501 CPalThread *pTargetThread;
502 IPalObject *pobjThread = NULL;
503 BOOL ret = FALSE;
504
505 PERF_ENTRY(SetThreadContext);
506 ENTRY("SetThreadContext (hThread=%p, lpContext=%p)\n",hThread,lpContext);
507
508 pThread = InternalGetCurrentThread();
509
510 palError = InternalGetThreadDataFromHandle(
511 pThread,
512 hThread,
513 0, // THREAD_SET_CONTEXT
514 &pTargetThread,
515 &pobjThread
516 );
517
518 if (NO_ERROR == palError)
519 {
520 if (!pTargetThread->IsDummy())
521 {
522 ret = CONTEXT_SetThreadContext(
523 GetCurrentProcessId(),
524 pTargetThread->GetPThreadSelf(),
525 lpContext
526 );
527 }
528 else
529 {
530 ASSERT("Dummy thread handle passed to SetThreadContext\n");
531 pThread->SetLastError(ERROR_INVALID_HANDLE);
532 }
533 }
534 else
535 {
536 pThread->SetLastError(palError);
537 }
538
539 if (NULL != pobjThread)
540 {
541 pobjThread->ReleaseReference(pThread);
542 }
543
544 return ret;
545}
546
547/*++
548Function:
549 PAL_ProbeMemory
550
551Abstract
552
553Parameter
554 pBuffer : address of memory to validate
555 cbBuffer : size of memory region to validate
556 fWriteAccess : if true, validate writable access, else just readable.
557
558Return
559 true if memory is valid, false if not.
560--*/
561BOOL
562PALAPI
563PAL_ProbeMemory(
564 PVOID pBuffer,
565 DWORD cbBuffer,
566 BOOL fWriteAccess)
567{
568 int fds[2];
569
570 if (pipe(fds) != 0)
571 {
572 ASSERT("pipe failed: errno is %d (%s)\n", errno, strerror(errno));
573 return FALSE;
574 }
575
576 fcntl(fds[0], O_NONBLOCK);
577 fcntl(fds[1], O_NONBLOCK);
578
579 PVOID pEnd = (PBYTE)pBuffer + cbBuffer;
580 BOOL result = TRUE;
581
582 // Validate the first byte in the buffer, then validate the first byte on each page after that.
583 while (pBuffer < pEnd)
584 {
585 int written = write(fds[1], pBuffer, 1);
586 if (written == -1)
587 {
588 if (errno != EFAULT)
589 {
590 ASSERT("write failed: errno is %d (%s)\n", errno, strerror(errno));
591 }
592 result = FALSE;
593 break;
594 }
595 else
596 {
597 if (fWriteAccess)
598 {
599 int rd = read(fds[0], pBuffer, 1);
600 if (rd == -1)
601 {
602 if (errno != EFAULT)
603 {
604 ASSERT("read failed: errno is %d (%s)\n", errno, strerror(errno));
605 }
606 result = FALSE;
607 break;
608 }
609 }
610 }
611
612 // Round to the beginning of the next page
613 pBuffer = PVOID(ALIGN_DOWN((SIZE_T)pBuffer, GetVirtualPageSize()) + GetVirtualPageSize());
614 }
615
616 close(fds[0]);
617 close(fds[1]);
618
619 return result;
620}
621
622} // extern "C"
623