X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fnat%2Fwindows-nat.c;h=709a9d3a31b802893b34991e0e0f0cecd0102f5b;hb=1ee1a363454d88a87ad2ade7530b2a7fb670021e;hp=767ed8c192fa84534ebd22286e4772757291a295;hpb=0a4afda3c63687cc5cbbdae78850ee66967cd648;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c index 767ed8c192..709a9d3a31 100644 --- a/gdb/nat/windows-nat.c +++ b/gdb/nat/windows-nat.c @@ -1,5 +1,5 @@ /* Internal interfaces for the Windows code - Copyright (C) 1995-2019 Free Software Foundation, Inc. + Copyright (C) 1995-2020 Free Software Foundation, Inc. This file is part of GDB. @@ -18,6 +18,40 @@ #include "gdbsupport/common-defs.h" #include "nat/windows-nat.h" +#include "gdbsupport/common-debug.h" + +namespace windows_nat +{ + +HANDLE current_process_handle; +DWORD current_process_id; +DWORD main_thread_id; +enum gdb_signal last_sig = GDB_SIGNAL_0; +DEBUG_EVENT current_event; + +/* The most recent event from WaitForDebugEvent. Unlike + current_event, this is guaranteed never to come from a pending + stop. This is important because only data from the most recent + event from WaitForDebugEvent can be used when calling + ContinueDebugEvent. */ +static DEBUG_EVENT last_wait_event; + +windows_thread_info *current_windows_thread; +DWORD desired_stop_thread_id = -1; +std::vector pending_stops; +EXCEPTION_RECORD siginfo_er; + +#ifdef __x86_64__ +bool ignore_first_breakpoint = false; +#endif + +/* Note that 'debug_events' must be locally defined in the relevant + functions. */ +#define DEBUG_EVENTS(x) if (debug_events) debug_printf x + +windows_thread_info::~windows_thread_info () +{ +} void windows_thread_info::suspend () @@ -60,3 +94,295 @@ windows_thread_info::resume () } suspended = 0; } + +const char * +get_image_name (HANDLE h, void *address, int unicode) +{ +#ifdef __CYGWIN__ + static char buf[MAX_PATH]; +#else + static char buf[(2 * MAX_PATH) + 1]; +#endif + DWORD size = unicode ? sizeof (WCHAR) : sizeof (char); + char *address_ptr; + int len = 0; + char b[2]; + SIZE_T done; + + /* Attempt to read the name of the dll that was detected. + This is documented to work only when actively debugging + a program. It will not work for attached processes. */ + if (address == NULL) + return NULL; + +#ifdef _WIN32_WCE + /* Windows CE reports the address of the image name, + instead of an address of a pointer into the image name. */ + address_ptr = address; +#else + /* See if we could read the address of a string, and that the + address isn't null. */ + if (!ReadProcessMemory (h, address, &address_ptr, + sizeof (address_ptr), &done) + || done != sizeof (address_ptr) + || !address_ptr) + return NULL; +#endif + + /* Find the length of the string. */ + while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done) + && (b[0] != 0 || b[size - 1] != 0) && done == size) + continue; + + if (!unicode) + ReadProcessMemory (h, address_ptr, buf, len, &done); + else + { + WCHAR *unicode_address = (WCHAR *) alloca (len * sizeof (WCHAR)); + ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR), + &done); +#ifdef __CYGWIN__ + wcstombs (buf, unicode_address, MAX_PATH); +#else + WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, sizeof buf, + 0, 0); +#endif + } + + return buf; +} + +/* The exception thrown by a program to tell the debugger the name of + a thread. The exception record contains an ID of a thread and a + name to give it. This exception has no documented name, but MSDN + dubs it "MS_VC_EXCEPTION" in one code example. */ +#define MS_VC_EXCEPTION 0x406d1388 + +handle_exception_result +handle_exception (struct target_waitstatus *ourstatus, bool debug_exceptions) +{ +#define DEBUG_EXCEPTION_SIMPLE(x) if (debug_exceptions) \ + debug_printf ("gdb: Target exception %s at %s\n", x, \ + host_address_to_string (\ + current_event.u.Exception.ExceptionRecord.ExceptionAddress)) + + EXCEPTION_RECORD *rec = ¤t_event.u.Exception.ExceptionRecord; + DWORD code = rec->ExceptionCode; + handle_exception_result result = HANDLE_EXCEPTION_HANDLED; + + memcpy (&siginfo_er, rec, sizeof siginfo_er); + + ourstatus->kind = TARGET_WAITKIND_STOPPED; + + /* Record the context of the current thread. */ + thread_rec (ptid_t (current_event.dwProcessId, current_event.dwThreadId, 0), + DONT_SUSPEND); + + switch (code) + { + case EXCEPTION_ACCESS_VIOLATION: + DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ACCESS_VIOLATION"); + ourstatus->value.sig = GDB_SIGNAL_SEGV; + if (handle_access_violation (rec)) + return HANDLE_EXCEPTION_UNHANDLED; + break; + case STATUS_STACK_OVERFLOW: + DEBUG_EXCEPTION_SIMPLE ("STATUS_STACK_OVERFLOW"); + ourstatus->value.sig = GDB_SIGNAL_SEGV; + break; + case STATUS_FLOAT_DENORMAL_OPERAND: + DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_DENORMAL_OPERAND"); + ourstatus->value.sig = GDB_SIGNAL_FPE; + break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); + ourstatus->value.sig = GDB_SIGNAL_FPE; + break; + case STATUS_FLOAT_INEXACT_RESULT: + DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_INEXACT_RESULT"); + ourstatus->value.sig = GDB_SIGNAL_FPE; + break; + case STATUS_FLOAT_INVALID_OPERATION: + DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_INVALID_OPERATION"); + ourstatus->value.sig = GDB_SIGNAL_FPE; + break; + case STATUS_FLOAT_OVERFLOW: + DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_OVERFLOW"); + ourstatus->value.sig = GDB_SIGNAL_FPE; + break; + case STATUS_FLOAT_STACK_CHECK: + DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_STACK_CHECK"); + ourstatus->value.sig = GDB_SIGNAL_FPE; + break; + case STATUS_FLOAT_UNDERFLOW: + DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_UNDERFLOW"); + ourstatus->value.sig = GDB_SIGNAL_FPE; + break; + case STATUS_FLOAT_DIVIDE_BY_ZERO: + DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_DIVIDE_BY_ZERO"); + ourstatus->value.sig = GDB_SIGNAL_FPE; + break; + case STATUS_INTEGER_DIVIDE_BY_ZERO: + DEBUG_EXCEPTION_SIMPLE ("STATUS_INTEGER_DIVIDE_BY_ZERO"); + ourstatus->value.sig = GDB_SIGNAL_FPE; + break; + case STATUS_INTEGER_OVERFLOW: + DEBUG_EXCEPTION_SIMPLE ("STATUS_INTEGER_OVERFLOW"); + ourstatus->value.sig = GDB_SIGNAL_FPE; + break; + case EXCEPTION_BREAKPOINT: +#ifdef __x86_64__ + if (ignore_first_breakpoint) + { + /* For WOW64 processes, there are always 2 breakpoint exceptions + on startup, first a BREAKPOINT for the 64bit ntdll.dll, + then a WX86_BREAKPOINT for the 32bit ntdll.dll. + Here we only care about the WX86_BREAKPOINT's. */ + ourstatus->kind = TARGET_WAITKIND_SPURIOUS; + ignore_first_breakpoint = false; + } +#endif + /* FALLTHROUGH */ + case STATUS_WX86_BREAKPOINT: + DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_BREAKPOINT"); + ourstatus->value.sig = GDB_SIGNAL_TRAP; +#ifdef _WIN32_WCE + /* Remove the initial breakpoint. */ + check_breakpoints ((CORE_ADDR) (long) current_event + .u.Exception.ExceptionRecord.ExceptionAddress); +#endif + break; + case DBG_CONTROL_C: + DEBUG_EXCEPTION_SIMPLE ("DBG_CONTROL_C"); + ourstatus->value.sig = GDB_SIGNAL_INT; + break; + case DBG_CONTROL_BREAK: + DEBUG_EXCEPTION_SIMPLE ("DBG_CONTROL_BREAK"); + ourstatus->value.sig = GDB_SIGNAL_INT; + break; + case EXCEPTION_SINGLE_STEP: + case STATUS_WX86_SINGLE_STEP: + DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_SINGLE_STEP"); + ourstatus->value.sig = GDB_SIGNAL_TRAP; + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ILLEGAL_INSTRUCTION"); + ourstatus->value.sig = GDB_SIGNAL_ILL; + break; + case EXCEPTION_PRIV_INSTRUCTION: + DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_PRIV_INSTRUCTION"); + ourstatus->value.sig = GDB_SIGNAL_ILL; + break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_NONCONTINUABLE_EXCEPTION"); + ourstatus->value.sig = GDB_SIGNAL_ILL; + break; + case MS_VC_EXCEPTION: + DEBUG_EXCEPTION_SIMPLE ("MS_VC_EXCEPTION"); + if (handle_ms_vc_exception (rec)) + { + ourstatus->value.sig = GDB_SIGNAL_TRAP; + result = HANDLE_EXCEPTION_IGNORED; + break; + } + /* treat improperly formed exception as unknown */ + /* FALLTHROUGH */ + default: + /* Treat unhandled first chance exceptions specially. */ + if (current_event.u.Exception.dwFirstChance) + return HANDLE_EXCEPTION_UNHANDLED; + debug_printf ("gdb: unknown target exception 0x%08x at %s\n", + (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionCode, + host_address_to_string ( + current_event.u.Exception.ExceptionRecord.ExceptionAddress)); + ourstatus->value.sig = GDB_SIGNAL_UNKNOWN; + break; + } + + last_sig = ourstatus->value.sig; + return result; + +#undef DEBUG_EXCEPTION_SIMPLE +} + +/* See nat/windows-nat.h. */ + +bool +matching_pending_stop (bool debug_events) +{ + /* If there are pending stops, and we might plausibly hit one of + them, we don't want to actually continue the inferior -- we just + want to report the stop. In this case, we just pretend to + continue. See the comment by the definition of "pending_stops" + for details on why this is needed. */ + for (const auto &item : pending_stops) + { + if (desired_stop_thread_id == -1 + || desired_stop_thread_id == item.thread_id) + { + DEBUG_EVENTS (("windows_continue - pending stop anticipated, " + "desired=0x%x, item=0x%x\n", + desired_stop_thread_id, item.thread_id)); + return true; + } + } + + return false; +} + +/* See nat/windows-nat.h. */ + +gdb::optional +fetch_pending_stop (bool debug_events) +{ + gdb::optional result; + for (auto iter = pending_stops.begin (); + iter != pending_stops.end (); + ++iter) + { + if (desired_stop_thread_id == -1 + || desired_stop_thread_id == iter->thread_id) + { + result = *iter; + current_event = iter->event; + + DEBUG_EVENTS (("get_windows_debug_event - " + "pending stop found in 0x%x (desired=0x%x)\n", + iter->thread_id, desired_stop_thread_id)); + + pending_stops.erase (iter); + break; + } + } + + return result; +} + +/* See nat/windows-nat.h. */ + +BOOL +continue_last_debug_event (DWORD continue_status, bool debug_events) +{ + DEBUG_EVENTS (("ContinueDebugEvent (cpid=%d, ctid=0x%x, %s);\n", + (unsigned) last_wait_event.dwProcessId, + (unsigned) last_wait_event.dwThreadId, + continue_status == DBG_CONTINUE ? + "DBG_CONTINUE" : "DBG_EXCEPTION_NOT_HANDLED")); + + return ContinueDebugEvent (last_wait_event.dwProcessId, + last_wait_event.dwThreadId, + continue_status); +} + +/* See nat/windows-nat.h. */ + +BOOL +wait_for_debug_event (DEBUG_EVENT *event, DWORD timeout) +{ + BOOL result = WaitForDebugEvent (event, timeout); + if (result) + last_wait_event = *event; + return result; +} + +}