X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Fwindows-nat.c;h=d04dc06f3bf7cd7bfd31f0addff2827f0af05729;hb=e758e19c596f8d0683f6c8ca750eb4e79071523d;hp=c4fde913af33bae7affd26a6ecc16362a02f8a1a;hpb=491144b5e21bbfd41969c175aebb663976f59058;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c index c4fde913af..d04dc06f3b 100644 --- a/gdb/windows-nat.c +++ b/gdb/windows-nat.c @@ -1,6 +1,6 @@ /* Target-vector operations for controlling windows child processes, for GDB. - Copyright (C) 1995-2019 Free Software Foundation, Inc. + Copyright (C) 1995-2020 Free Software Foundation, Inc. Contributed by Cygnus Solutions, A Red Hat Company. @@ -43,8 +43,8 @@ #include #endif #include +#include -#include "buildsym-legacy.h" #include "filenames.h" #include "symfile.h" #include "objfiles.h" @@ -69,17 +69,26 @@ #include "inf-child.h" #include "gdbsupport/gdb_tilde_expand.h" #include "gdbsupport/pathstuff.h" +#include "gdbsupport/gdb_wait.h" +#include "nat/windows-nat.h" + +using namespace windows_nat; #define AdjustTokenPrivileges dyn_AdjustTokenPrivileges #define DebugActiveProcessStop dyn_DebugActiveProcessStop #define DebugBreakProcess dyn_DebugBreakProcess #define DebugSetProcessKillOnExit dyn_DebugSetProcessKillOnExit #define EnumProcessModules dyn_EnumProcessModules +#define EnumProcessModulesEx dyn_EnumProcessModulesEx #define GetModuleInformation dyn_GetModuleInformation #define LookupPrivilegeValueA dyn_LookupPrivilegeValueA #define OpenProcessToken dyn_OpenProcessToken #define GetConsoleFontSize dyn_GetConsoleFontSize #define GetCurrentConsoleFont dyn_GetCurrentConsoleFont +#define Wow64SuspendThread dyn_Wow64SuspendThread +#define Wow64GetThreadContext dyn_Wow64GetThreadContext +#define Wow64SetThreadContext dyn_Wow64SetThreadContext +#define Wow64GetThreadSelectorEntry dyn_Wow64GetThreadSelectorEntry typedef BOOL WINAPI (AdjustTokenPrivileges_ftype) (HANDLE, BOOL, PTOKEN_PRIVILEGES, @@ -100,6 +109,12 @@ typedef BOOL WINAPI (EnumProcessModules_ftype) (HANDLE, HMODULE *, DWORD, LPDWORD); static EnumProcessModules_ftype *EnumProcessModules; +#ifdef __x86_64__ +typedef BOOL WINAPI (EnumProcessModulesEx_ftype) (HANDLE, HMODULE *, DWORD, + LPDWORD, DWORD); +static EnumProcessModulesEx_ftype *EnumProcessModulesEx; +#endif + typedef BOOL WINAPI (GetModuleInformation_ftype) (HANDLE, HMODULE, LPMODULEINFO, DWORD); static GetModuleInformation_ftype *GetModuleInformation; @@ -117,6 +132,22 @@ static GetCurrentConsoleFont_ftype *GetCurrentConsoleFont; typedef COORD WINAPI (GetConsoleFontSize_ftype) (HANDLE, DWORD); static GetConsoleFontSize_ftype *GetConsoleFontSize; +#ifdef __x86_64__ +typedef DWORD WINAPI (Wow64SuspendThread_ftype) (HANDLE); +static Wow64SuspendThread_ftype *Wow64SuspendThread; + +typedef BOOL WINAPI (Wow64GetThreadContext_ftype) (HANDLE, PWOW64_CONTEXT); +static Wow64GetThreadContext_ftype *Wow64GetThreadContext; + +typedef BOOL WINAPI (Wow64SetThreadContext_ftype) (HANDLE, + const WOW64_CONTEXT *); +static Wow64SetThreadContext_ftype *Wow64SetThreadContext; + +typedef BOOL WINAPI (Wow64GetThreadSelectorEntry_ftype) (HANDLE, DWORD, + PLDT_ENTRY); +static Wow64GetThreadSelectorEntry_ftype *Wow64GetThreadSelectorEntry; +#endif + #undef STARTUPINFO #undef CreateProcess #undef GetModuleFileNameEx @@ -148,7 +179,7 @@ static GetConsoleFontSize_ftype *GetConsoleFontSize; static int have_saved_context; /* True if we've saved context from a cygwin signal. */ #ifdef __CYGWIN__ -static CONTEXT saved_context; /* Containes the saved context from a +static CONTEXT saved_context; /* Contains the saved context from a cygwin signal. */ #endif @@ -179,19 +210,6 @@ static int debug_registers_used; static int windows_initialization_done; #define DR6_CLEAR_VALUE 0xffff0ff0 -/* 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 - -typedef enum -{ - HANDLE_EXCEPTION_UNHANDLED = 0, - HANDLE_EXCEPTION_HANDLED, - HANDLE_EXCEPTION_IGNORED -} handle_exception_result; - /* The string sent by cygwin when it processes a signal. FIXME: This should be in a cygwin include file. */ #ifndef _CYGWIN_SIGNAL_STRING @@ -210,39 +228,15 @@ static CORE_ADDR cygwin_get_dr (int i); static unsigned long cygwin_get_dr6 (void); static unsigned long cygwin_get_dr7 (void); -static enum gdb_signal last_sig = GDB_SIGNAL_0; -/* Set if a signal was received from the debugged process. */ - -/* Thread information structure used to track information that is - not available in gdb's thread structure. */ -typedef struct windows_thread_info_struct - { - struct windows_thread_info_struct *next; - DWORD id; - HANDLE h; - CORE_ADDR thread_local_base; - char *name; - int suspended; - int reload_context; - CONTEXT context; - STACKFRAME sf; - } -windows_thread_info; - -static windows_thread_info thread_head; - -/* The process and thread handles for the above context. */ - -static DEBUG_EVENT current_event; /* The current debug event from - WaitForDebugEvent */ -static HANDLE current_process_handle; /* Currently executing process */ -static windows_thread_info *current_thread; /* Info on currently selected thread */ +static std::vector thread_list; /* Counts of things. */ -static int exception_count = 0; -static int event_count = 0; static int saw_create; static int open_process_used = 0; +#ifdef __x86_64__ +static bool wow64_process = false; +static bool ignore_first_breakpoint = false; +#endif /* User options. */ static bool new_console = false; @@ -322,6 +316,16 @@ struct windows_nat_target final : public x86_nat_target void fetch_registers (struct regcache *, int) override; void store_registers (struct regcache *, int) override; + bool stopped_by_sw_breakpoint () override + { + return current_windows_thread->stopped_at_software_breakpoint; + } + + bool supports_stopped_by_sw_breakpoint () override + { + return true; + } + enum target_xfer_status xfer_partial (enum target_object object, const char *annex, gdb_byte *readbuf, @@ -351,6 +355,8 @@ struct windows_nat_target final : public x86_nat_target bool get_tib_address (ptid_t ptid, CORE_ADDR *addr) override; const char *thread_name (struct thread_info *) override; + + int get_windows_debug_event (int pid, struct target_waitstatus *ourstatus); }; static windows_nat_target the_windows_nat_target; @@ -358,15 +364,16 @@ static windows_nat_target the_windows_nat_target; /* Set the MAPPINGS static global to OFFSETS. See the description of MAPPINGS for more details. */ -void +static void windows_set_context_register_offsets (const int *offsets) { mappings = offsets; } -/* See windows-nat.h. */ +/* Set the function that should be used by this module to determine + whether a given register is a segment register or not. */ -void +static void windows_set_segment_register_p (segment_register_p_ftype *fun) { segment_register_p = fun; @@ -380,44 +387,31 @@ check (BOOL ok, const char *file, int line) (unsigned) GetLastError ()); } -/* Find a thread record given a thread id. If GET_CONTEXT is not 0, - then also retrieve the context for this thread. If GET_CONTEXT is - negative, then don't suspend the thread. */ -static windows_thread_info * -thread_rec (DWORD id, int get_context) -{ - windows_thread_info *th; +/* See nat/windows-nat.h. */ - for (th = &thread_head; (th = th->next) != NULL;) - if (th->id == id) +windows_thread_info * +windows_nat::thread_rec (ptid_t ptid, thread_disposition_type disposition) +{ + for (windows_thread_info *th : thread_list) + if (th->tid == ptid.lwp ()) { - if (!th->suspended && get_context) + if (!th->suspended) { - if (get_context > 0 && id != current_event.dwThreadId) + switch (disposition) { - if (SuspendThread (th->h) == (DWORD) -1) - { - DWORD err = GetLastError (); - - /* We get Access Denied (5) when trying to suspend - threads that Windows started on behalf of the - debuggee, usually when those threads are just - about to exit. - We can get Invalid Handle (6) if the main thread - has exited. */ - if (err != ERROR_INVALID_HANDLE - && err != ERROR_ACCESS_DENIED) - warning (_("SuspendThread (tid=0x%x) failed." - " (winerr %u)"), - (unsigned) id, (unsigned) err); - th->suspended = -1; - } - else - th->suspended = 1; + case DONT_INVALIDATE_CONTEXT: + /* Nothing. */ + break; + case INVALIDATE_CONTEXT: + if (ptid.lwp () != current_event.dwThreadId) + th->suspend (); + th->reload_context = true; + break; + case DONT_SUSPEND: + th->reload_context = true; + th->suspended = -1; + break; } - else if (get_context < 0) - th->suspended = -1; - th->reload_context = 1; } return th; } @@ -437,21 +431,21 @@ static windows_thread_info * windows_add_thread (ptid_t ptid, HANDLE h, void *tlb, bool main_thread_p) { windows_thread_info *th; - DWORD id; - - gdb_assert (ptid.tid () != 0); - id = ptid.tid (); + gdb_assert (ptid.lwp () != 0); - if ((th = thread_rec (id, FALSE))) + if ((th = thread_rec (ptid, DONT_INVALIDATE_CONTEXT))) return th; - th = XCNEW (windows_thread_info); - th->id = id; - th->h = h; - th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb; - th->next = thread_head.next; - thread_head.next = th; + CORE_ADDR base = (CORE_ADDR) (uintptr_t) tlb; +#ifdef __x86_64__ + /* For WOW64 processes, this is actually the pointer to the 64bit TIB, + and the 32bit TIB is exactly 2 pages after it. */ + if (wow64_process) + base += 0x2000; +#endif + th = new windows_thread_info (ptid.lwp (), h, base); + thread_list.push_back (th); /* Add this new thread to the list of threads. @@ -459,24 +453,43 @@ windows_add_thread (ptid_t ptid, HANDLE h, void *tlb, bool main_thread_p) the main thread silently (in reality, this thread is really more of a process to the user than a thread). */ if (main_thread_p) - add_thread_silent (ptid); + add_thread_silent (&the_windows_nat_target, ptid); else - add_thread (ptid); + add_thread (&the_windows_nat_target, ptid); /* Set the debug registers for the new thread if they are used. */ if (debug_registers_used) { - /* Only change the value of the debug registers. */ - th->context.ContextFlags = CONTEXT_DEBUG_REGISTERS; - CHECK (GetThreadContext (th->h, &th->context)); - th->context.Dr0 = dr[0]; - th->context.Dr1 = dr[1]; - th->context.Dr2 = dr[2]; - th->context.Dr3 = dr[3]; - th->context.Dr6 = DR6_CLEAR_VALUE; - th->context.Dr7 = dr[7]; - CHECK (SetThreadContext (th->h, &th->context)); - th->context.ContextFlags = 0; +#ifdef __x86_64__ + if (wow64_process) + { + /* Only change the value of the debug registers. */ + th->wow64_context.ContextFlags = CONTEXT_DEBUG_REGISTERS; + CHECK (Wow64GetThreadContext (th->h, &th->wow64_context)); + th->wow64_context.Dr0 = dr[0]; + th->wow64_context.Dr1 = dr[1]; + th->wow64_context.Dr2 = dr[2]; + th->wow64_context.Dr3 = dr[3]; + th->wow64_context.Dr6 = DR6_CLEAR_VALUE; + th->wow64_context.Dr7 = dr[7]; + CHECK (Wow64SetThreadContext (th->h, &th->wow64_context)); + th->wow64_context.ContextFlags = 0; + } + else +#endif + { + /* Only change the value of the debug registers. */ + th->context.ContextFlags = CONTEXT_DEBUG_REGISTERS; + CHECK (GetThreadContext (th->h, &th->context)); + th->context.Dr0 = dr[0]; + th->context.Dr1 = dr[1]; + th->context.Dr2 = dr[2]; + th->context.Dr3 = dr[3]; + th->context.Dr6 = DR6_CLEAR_VALUE; + th->context.Dr7 = dr[7]; + CHECK (SetThreadContext (th->h, &th->context)); + th->context.ContextFlags = 0; + } } return th; } @@ -486,17 +499,13 @@ windows_add_thread (ptid_t ptid, HANDLE h, void *tlb, bool main_thread_p) static void windows_init_thread_list (void) { - windows_thread_info *th = &thread_head; - DEBUG_EVENTS (("gdb: windows_init_thread_list\n")); init_thread_list (); - while (th->next != NULL) - { - windows_thread_info *here = th->next; - th->next = here->next; - xfree (here); - } - thread_head.next = NULL; + + for (windows_thread_info *here : thread_list) + delete here; + + thread_list.clear (); } /* Delete a thread from the list of threads. @@ -509,18 +518,17 @@ windows_init_thread_list (void) static void windows_delete_thread (ptid_t ptid, DWORD exit_code, bool main_thread_p) { - windows_thread_info *th; DWORD id; - gdb_assert (ptid.tid () != 0); + gdb_assert (ptid.lwp () != 0); - id = ptid.tid (); + id = ptid.lwp (); /* Emit a notification about the thread being deleted. Note that no notification was printed when the main thread was created, and thus, unless in verbose mode, we should be - symetrical, and avoid that notification for the main thread + symmetrical, and avoid that notification for the main thread here as well. */ if (info_verbose) @@ -530,19 +538,18 @@ windows_delete_thread (ptid_t ptid, DWORD exit_code, bool main_thread_p) target_pid_to_str (ptid).c_str (), (unsigned) exit_code); - delete_thread (find_thread_ptid (ptid)); + delete_thread (find_thread_ptid (&the_windows_nat_target, ptid)); - for (th = &thread_head; - th->next != NULL && th->next->id != id; - th = th->next) - continue; + auto iter = std::find_if (thread_list.begin (), thread_list.end (), + [=] (windows_thread_info *th) + { + return th->tid == id; + }); - if (th->next != NULL) + if (iter != thread_list.end ()) { - windows_thread_info *here = th->next; - th->next = here->next; - xfree (here->name); - xfree (here); + delete *iter; + thread_list.erase (iter); } } @@ -563,10 +570,20 @@ windows_fetch_one_register (struct regcache *regcache, gdb_assert (r >= 0); gdb_assert (!th->reload_context); - char *context_offset = ((char *) &th->context) + mappings[r]; + char *context_ptr = (char *) &th->context; +#ifdef __x86_64__ + if (wow64_process) + context_ptr = (char *) &th->wow64_context; +#endif + + char *context_offset = context_ptr + mappings[r]; struct gdbarch *gdbarch = regcache->arch (); struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + gdb_assert (!gdbarch_read_pc_p (gdbarch)); + gdb_assert (gdbarch_pc_regnum (gdbarch) >= 0); + gdb_assert (!gdbarch_write_pc_p (gdbarch)); + if (r == I387_FISEG_REGNUM (tdep)) { long l = *((long *) context_offset) & 0xffff; @@ -586,14 +603,35 @@ windows_fetch_one_register (struct regcache *regcache, regcache->raw_supply (r, (char *) &l); } else - regcache->raw_supply (r, context_offset); + { + if (th->stopped_at_software_breakpoint + && r == gdbarch_pc_regnum (gdbarch)) + { + int size = register_size (gdbarch, r); + if (size == 4) + { + uint32_t value; + memcpy (&value, context_offset, size); + value -= gdbarch_decr_pc_after_break (gdbarch); + memcpy (context_offset, &value, size); + } + else + { + gdb_assert (size == 8); + uint64_t value; + memcpy (&value, context_offset, size); + value -= gdbarch_decr_pc_after_break (gdbarch); + memcpy (context_offset, &value, size); + } + } + regcache->raw_supply (r, context_offset); + } } void windows_nat_target::fetch_registers (struct regcache *regcache, int r) { - DWORD pid = regcache->ptid ().tid (); - windows_thread_info *th = thread_rec (pid, TRUE); + windows_thread_info *th = thread_rec (regcache->ptid (), INVALIDATE_CONTEXT); /* Check if TH exists. Windows sometimes uses a non-existent thread id in its events. */ @@ -614,6 +652,26 @@ windows_nat_target::fetch_registers (struct regcache *regcache, int r) have_saved_context = 0; } else +#endif +#ifdef __x86_64__ + if (wow64_process) + { + th->wow64_context.ContextFlags = CONTEXT_DEBUGGER_DR; + CHECK (Wow64GetThreadContext (th->h, &th->wow64_context)); + /* Copy dr values from that thread. + But only if there were not modified since last stop. + PR gdb/2388 */ + if (!debug_registers_changed) + { + dr[0] = th->wow64_context.Dr0; + dr[1] = th->wow64_context.Dr1; + dr[2] = th->wow64_context.Dr2; + dr[3] = th->wow64_context.Dr3; + dr[6] = th->wow64_context.Dr6; + dr[7] = th->wow64_context.Dr7; + } + } + else #endif { th->context.ContextFlags = CONTEXT_DEBUGGER_DR; @@ -631,7 +689,7 @@ windows_nat_target::fetch_registers (struct regcache *regcache, int r) dr[7] = th->context.Dr7; } } - th->reload_context = 0; + th->reload_context = false; } if (r < 0) @@ -653,7 +711,13 @@ windows_store_one_register (const struct regcache *regcache, { gdb_assert (r >= 0); - regcache->raw_collect (r, ((char *) &th->context) + mappings[r]); + char *context_ptr = (char *) &th->context; +#ifdef __x86_64__ + if (wow64_process) + context_ptr = (char *) &th->wow64_context; +#endif + + regcache->raw_collect (r, context_ptr + mappings[r]); } /* Store a new register value into the context of the thread tied to @@ -662,8 +726,7 @@ windows_store_one_register (const struct regcache *regcache, void windows_nat_target::store_registers (struct regcache *regcache, int r) { - DWORD pid = regcache->ptid ().tid (); - windows_thread_info *th = thread_rec (pid, TRUE); + windows_thread_info *th = thread_rec (regcache->ptid (), INVALIDATE_CONTEXT); /* Check if TH exists. Windows sometimes uses a non-existent thread id in its events. */ @@ -677,23 +740,11 @@ windows_nat_target::store_registers (struct regcache *regcache, int r) windows_store_one_register (regcache, th, r); } -/* Encapsulate the information required in a call to - symbol_file_add_args. */ -struct safe_symbol_file_add_args -{ - char *name; - int from_tty; - section_addr_info *addrs; - int mainline; - int flags; - struct ui_file *err, *out; - struct objfile *ret; -}; - /* Maintain a linked list of "so" information. */ struct lm_info_windows : public lm_info_base { LPVOID load_addr = 0; + CORE_ADDR text_offset = 0; }; static struct so_list solib_start, *solib_end; @@ -794,76 +845,20 @@ windows_make_so (const char *name, LPVOID load_addr) file header and the section alignment. */ cygwin_load_start = (CORE_ADDR) (uintptr_t) ((char *) load_addr + 0x1000); - cygwin_load_end = cygwin_load_start + bfd_section_size (abfd.get (), - text); + cygwin_load_end = cygwin_load_start + bfd_section_size (text); } #endif return so; } -static char * -get_image_name (HANDLE h, void *address, int unicode) -{ -#ifdef __CYGWIN__ - static char buf[__PMAX]; -#else - static char buf[(2 * __PMAX) + 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; - - /* 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; - - /* 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, __PMAX); -#else - WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, sizeof buf, - 0, 0); -#endif - } +/* See nat/windows-nat.h. */ - return buf; -} - -/* Handle a DLL load event, and return 1. - - This function assumes that this event did not occur during inferior - initialization, where their event info may be incomplete (see - do_initial_windows_stuff and windows_add_all_dlls for more info - on how we handle DLL loading during that phase). */ - -static void -handle_load_dll () +void +windows_nat::handle_load_dll () { LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll; - char *dll_name; + const char *dll_name; /* Try getting the DLL name via the lpImageName field of the event. Note that Microsoft documents this fields as strictly optional, @@ -893,16 +888,10 @@ windows_free_so (struct so_list *so) xfree (so); } -/* Handle a DLL unload event. - Return 1 if successful, or zero otherwise. - - This function assumes that this event did not occur during inferior - initialization, where their event info may be incomplete (see - do_initial_windows_stuff and windows_add_all_dlls for more info - on how we handle DLL loading during that phase). */ +/* See nat/windows-nat.h. */ -static void -handle_unload_dll () +void +windows_nat::handle_unload_dll () { LPVOID lpBaseOfDll = current_event.u.UnloadDll.lpBaseOfDll; struct so_list *so; @@ -956,7 +945,14 @@ catch_errors (void (*func) ()) static void windows_clear_solib (void) { - solib_start.next = NULL; + struct so_list *so; + + for (so = solib_start.next; so; so = solib_start.next) + { + solib_start.next = so->next; + windows_free_so (so); + } + solib_end = &solib_start; } @@ -979,11 +975,10 @@ signal_event_command (const char *args, int from_tty) CloseHandle ((HANDLE) event_id); } -/* Handle DEBUG_STRING output from child process. - Cygwin prepends its messages with a "cygwin:". Interpret this as - a Cygwin signal. Otherwise just print the string as a warning. */ -static int -handle_output_debug_string (struct target_waitstatus *ourstatus) +/* See nat/windows-nat.h. */ + +int +windows_nat::handle_output_debug_string (struct target_waitstatus *ourstatus) { gdb::unique_xmalloc_ptr s; int retval = 0; @@ -1047,7 +1042,14 @@ static int display_selector (HANDLE thread, DWORD sel) { LDT_ENTRY info; - if (GetThreadSelectorEntry (thread, sel, &info)) + BOOL ret; +#ifdef __x86_64__ + if (wow64_process) + ret = Wow64GetThreadSelectorEntry (thread, sel, &info); + else +#endif + ret = GetThreadSelectorEntry (thread, sel, &info); + if (ret) { int base, limit; printf_filtered ("0x%03x: ", (unsigned) sel); @@ -1124,208 +1126,106 @@ display_selector (HANDLE thread, DWORD sel) static void display_selectors (const char * args, int from_tty) { - if (!current_thread) + if (!current_windows_thread) { puts_filtered ("Impossible to display selectors now.\n"); return; } if (!args) { - - puts_filtered ("Selector $cs\n"); - display_selector (current_thread->h, - current_thread->context.SegCs); - puts_filtered ("Selector $ds\n"); - display_selector (current_thread->h, - current_thread->context.SegDs); - puts_filtered ("Selector $es\n"); - display_selector (current_thread->h, - current_thread->context.SegEs); - puts_filtered ("Selector $ss\n"); - display_selector (current_thread->h, - current_thread->context.SegSs); - puts_filtered ("Selector $fs\n"); - display_selector (current_thread->h, - current_thread->context.SegFs); - puts_filtered ("Selector $gs\n"); - display_selector (current_thread->h, - current_thread->context.SegGs); +#ifdef __x86_64__ + if (wow64_process) + { + puts_filtered ("Selector $cs\n"); + display_selector (current_windows_thread->h, + current_windows_thread->wow64_context.SegCs); + puts_filtered ("Selector $ds\n"); + display_selector (current_windows_thread->h, + current_windows_thread->wow64_context.SegDs); + puts_filtered ("Selector $es\n"); + display_selector (current_windows_thread->h, + current_windows_thread->wow64_context.SegEs); + puts_filtered ("Selector $ss\n"); + display_selector (current_windows_thread->h, + current_windows_thread->wow64_context.SegSs); + puts_filtered ("Selector $fs\n"); + display_selector (current_windows_thread->h, + current_windows_thread->wow64_context.SegFs); + puts_filtered ("Selector $gs\n"); + display_selector (current_windows_thread->h, + current_windows_thread->wow64_context.SegGs); + } + else +#endif + { + puts_filtered ("Selector $cs\n"); + display_selector (current_windows_thread->h, + current_windows_thread->context.SegCs); + puts_filtered ("Selector $ds\n"); + display_selector (current_windows_thread->h, + current_windows_thread->context.SegDs); + puts_filtered ("Selector $es\n"); + display_selector (current_windows_thread->h, + current_windows_thread->context.SegEs); + puts_filtered ("Selector $ss\n"); + display_selector (current_windows_thread->h, + current_windows_thread->context.SegSs); + puts_filtered ("Selector $fs\n"); + display_selector (current_windows_thread->h, + current_windows_thread->context.SegFs); + puts_filtered ("Selector $gs\n"); + display_selector (current_windows_thread->h, + current_windows_thread->context.SegGs); + } } else { int sel; sel = parse_and_eval_long (args); printf_filtered ("Selector \"%s\"\n",args); - display_selector (current_thread->h, sel); + display_selector (current_windows_thread->h, sel); } } -#define DEBUG_EXCEPTION_SIMPLE(x) if (debug_exceptions) \ - printf_unfiltered ("gdb: Target exception %s at %s\n", x, \ - host_address_to_string (\ - current_event.u.Exception.ExceptionRecord.ExceptionAddress)) +/* See nat/windows-nat.h. */ -static handle_exception_result -handle_exception (struct target_waitstatus *ourstatus) +bool +windows_nat::handle_ms_vc_exception (const EXCEPTION_RECORD *rec) { - EXCEPTION_RECORD *rec = ¤t_event.u.Exception.ExceptionRecord; - DWORD code = rec->ExceptionCode; - handle_exception_result result = HANDLE_EXCEPTION_HANDLED; - - ourstatus->kind = TARGET_WAITKIND_STOPPED; - - /* Record the context of the current thread. */ - thread_rec (current_event.dwThreadId, -1); - - switch (code) + if (rec->NumberParameters >= 3 + && (rec->ExceptionInformation[0] & 0xffffffff) == 0x1000) { - case EXCEPTION_ACCESS_VIOLATION: - DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ACCESS_VIOLATION"); - ourstatus->value.sig = GDB_SIGNAL_SEGV; -#ifdef __CYGWIN__ - { - /* See if the access violation happened within the cygwin DLL - itself. Cygwin uses a kind of exception handling to deal - with passed-in invalid addresses. gdb should not treat - these as real SEGVs since they will be silently handled by - cygwin. A real SEGV will (theoretically) be caught by - cygwin later in the process and will be sent as a - cygwin-specific-signal. So, ignore SEGVs if they show up - within the text segment of the DLL itself. */ - const char *fn; - CORE_ADDR addr = (CORE_ADDR) (uintptr_t) rec->ExceptionAddress; - - if ((!cygwin_exceptions && (addr >= cygwin_load_start - && addr < cygwin_load_end)) - || (find_pc_partial_function (addr, &fn, NULL, NULL) - && startswith (fn, "KERNEL32!IsBad"))) - return HANDLE_EXCEPTION_UNHANDLED; - } -#endif - 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: - DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_BREAKPOINT"); - ourstatus->value.sig = GDB_SIGNAL_TRAP; - 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: - 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: - if (rec->NumberParameters >= 3 - && (rec->ExceptionInformation[0] & 0xffffffff) == 0x1000) - { - DWORD named_thread_id; - windows_thread_info *named_thread; - CORE_ADDR thread_name_target; + DWORD named_thread_id; + windows_thread_info *named_thread; + CORE_ADDR thread_name_target; - DEBUG_EXCEPTION_SIMPLE ("MS_VC_EXCEPTION"); + thread_name_target = rec->ExceptionInformation[1]; + named_thread_id = (DWORD) (0xffffffff & rec->ExceptionInformation[2]); - thread_name_target = rec->ExceptionInformation[1]; - named_thread_id = (DWORD) (0xffffffff & rec->ExceptionInformation[2]); + if (named_thread_id == (DWORD) -1) + named_thread_id = current_event.dwThreadId; - if (named_thread_id == (DWORD) -1) - named_thread_id = current_event.dwThreadId; + named_thread = thread_rec (ptid_t (current_event.dwProcessId, + named_thread_id, 0), + DONT_INVALIDATE_CONTEXT); + if (named_thread != NULL) + { + int thread_name_len; + gdb::unique_xmalloc_ptr thread_name; - named_thread = thread_rec (named_thread_id, 0); - if (named_thread != NULL) + thread_name_len = target_read_string (thread_name_target, + &thread_name, 1025, NULL); + if (thread_name_len > 0) { - int thread_name_len; - gdb::unique_xmalloc_ptr thread_name; - - thread_name_len = target_read_string (thread_name_target, - &thread_name, 1025, NULL); - if (thread_name_len > 0) - { - thread_name.get ()[thread_name_len - 1] = '\0'; - xfree (named_thread->name); - named_thread->name = thread_name.release (); - } + thread_name.get ()[thread_name_len - 1] = '\0'; + named_thread->name = std::move (thread_name); } - 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; - printf_unfiltered ("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; + + return true; } - exception_count++; - last_sig = ourstatus->value.sig; - return result; + + return false; } /* Resume thread specified by ID, or all artificially suspended @@ -1335,51 +1235,85 @@ handle_exception (struct target_waitstatus *ourstatus) static BOOL windows_continue (DWORD continue_status, int id, int killed) { - windows_thread_info *th; BOOL res; - DEBUG_EVENTS (("ContinueDebugEvent (cpid=%d, ctid=0x%x, %s);\n", - (unsigned) current_event.dwProcessId, - (unsigned) current_event.dwThreadId, - continue_status == DBG_CONTINUE ? - "DBG_CONTINUE" : "DBG_EXCEPTION_NOT_HANDLED")); + desired_stop_thread_id = id; + + if (matching_pending_stop (debug_events)) + return TRUE; - for (th = &thread_head; (th = th->next) != NULL;) - if ((id == -1 || id == (int) th->id) - && th->suspended) + for (windows_thread_info *th : thread_list) + if (id == -1 || id == (int) th->tid) { - if (debug_registers_changed) + if (!th->suspended) + continue; +#ifdef __x86_64__ + if (wow64_process) { - th->context.ContextFlags |= CONTEXT_DEBUG_REGISTERS; - th->context.Dr0 = dr[0]; - th->context.Dr1 = dr[1]; - th->context.Dr2 = dr[2]; - th->context.Dr3 = dr[3]; - th->context.Dr6 = DR6_CLEAR_VALUE; - th->context.Dr7 = dr[7]; + if (debug_registers_changed) + { + th->wow64_context.ContextFlags |= CONTEXT_DEBUG_REGISTERS; + th->wow64_context.Dr0 = dr[0]; + th->wow64_context.Dr1 = dr[1]; + th->wow64_context.Dr2 = dr[2]; + th->wow64_context.Dr3 = dr[3]; + th->wow64_context.Dr6 = DR6_CLEAR_VALUE; + th->wow64_context.Dr7 = dr[7]; + } + if (th->wow64_context.ContextFlags) + { + DWORD ec = 0; + + if (GetExitCodeThread (th->h, &ec) + && ec == STILL_ACTIVE) + { + BOOL status = Wow64SetThreadContext (th->h, + &th->wow64_context); + + if (!killed) + CHECK (status); + } + th->wow64_context.ContextFlags = 0; + } } - if (th->context.ContextFlags) + else +#endif { - DWORD ec = 0; - - if (GetExitCodeThread (th->h, &ec) - && ec == STILL_ACTIVE) + if (debug_registers_changed) { - BOOL status = SetThreadContext (th->h, &th->context); + th->context.ContextFlags |= CONTEXT_DEBUG_REGISTERS; + th->context.Dr0 = dr[0]; + th->context.Dr1 = dr[1]; + th->context.Dr2 = dr[2]; + th->context.Dr3 = dr[3]; + th->context.Dr6 = DR6_CLEAR_VALUE; + th->context.Dr7 = dr[7]; + } + if (th->context.ContextFlags) + { + DWORD ec = 0; + + if (GetExitCodeThread (th->h, &ec) + && ec == STILL_ACTIVE) + { + BOOL status = SetThreadContext (th->h, &th->context); - if (!killed) - CHECK (status); + if (!killed) + CHECK (status); + } + th->context.ContextFlags = 0; } - th->context.ContextFlags = 0; } - if (th->suspended > 0) - (void) ResumeThread (th->h); - th->suspended = 0; + th->resume (); + } + else + { + /* When single-stepping a specific thread, other threads must + be suspended. */ + th->suspend (); } - res = ContinueDebugEvent (current_event.dwProcessId, - current_event.dwThreadId, - continue_status); + res = continue_last_debug_event (continue_status, debug_events); if (!res) error (_("Failed to resume program execution" @@ -1405,9 +1339,9 @@ fake_create_process (void) (unsigned) GetLastError ()); /* We can not debug anything in that case. */ } - current_thread - = windows_add_thread (ptid_t (current_event.dwProcessId, 0, - current_event.dwThreadId), + current_windows_thread + = windows_add_thread (ptid_t (current_event.dwProcessId, + current_event.dwThreadId, 0), current_event.u.CreateThread.hThread, current_event.u.CreateThread.lpThreadLocalBase, true /* main_thread_p */); @@ -1463,35 +1397,66 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig) last_sig = GDB_SIGNAL_0; - DEBUG_EXEC (("gdb: windows_resume (pid=%d, tid=%ld, step=%d, sig=%d);\n", - ptid.pid (), ptid.tid (), step, sig)); + DEBUG_EXEC (("gdb: windows_resume (pid=%d, tid=0x%x, step=%d, sig=%d);\n", + ptid.pid (), (unsigned) ptid.lwp (), step, sig)); /* Get context for currently selected thread. */ - th = thread_rec (inferior_ptid.tid (), FALSE); + th = thread_rec (inferior_ptid, DONT_INVALIDATE_CONTEXT); if (th) { - if (step) +#ifdef __x86_64__ + if (wow64_process) { - /* Single step by setting t bit. */ - struct regcache *regcache = get_current_regcache (); - struct gdbarch *gdbarch = regcache->arch (); - fetch_registers (regcache, gdbarch_ps_regnum (gdbarch)); - th->context.EFlags |= FLAG_TRACE_BIT; - } + if (step) + { + /* Single step by setting t bit. */ + struct regcache *regcache = get_current_regcache (); + struct gdbarch *gdbarch = regcache->arch (); + fetch_registers (regcache, gdbarch_ps_regnum (gdbarch)); + th->wow64_context.EFlags |= FLAG_TRACE_BIT; + } - if (th->context.ContextFlags) + if (th->wow64_context.ContextFlags) + { + if (debug_registers_changed) + { + th->wow64_context.Dr0 = dr[0]; + th->wow64_context.Dr1 = dr[1]; + th->wow64_context.Dr2 = dr[2]; + th->wow64_context.Dr3 = dr[3]; + th->wow64_context.Dr6 = DR6_CLEAR_VALUE; + th->wow64_context.Dr7 = dr[7]; + } + CHECK (Wow64SetThreadContext (th->h, &th->wow64_context)); + th->wow64_context.ContextFlags = 0; + } + } + else +#endif { - if (debug_registers_changed) + if (step) { - th->context.Dr0 = dr[0]; - th->context.Dr1 = dr[1]; - th->context.Dr2 = dr[2]; - th->context.Dr3 = dr[3]; - th->context.Dr6 = DR6_CLEAR_VALUE; - th->context.Dr7 = dr[7]; + /* Single step by setting t bit. */ + struct regcache *regcache = get_current_regcache (); + struct gdbarch *gdbarch = regcache->arch (); + fetch_registers (regcache, gdbarch_ps_regnum (gdbarch)); + th->context.EFlags |= FLAG_TRACE_BIT; + } + + if (th->context.ContextFlags) + { + if (debug_registers_changed) + { + th->context.Dr0 = dr[0]; + th->context.Dr1 = dr[1]; + th->context.Dr2 = dr[2]; + th->context.Dr3 = dr[3]; + th->context.Dr6 = DR6_CLEAR_VALUE; + th->context.Dr7 = dr[7]; + } + CHECK (SetThreadContext (th->h, &th->context)); + th->context.ContextFlags = 0; } - CHECK (SetThreadContext (th->h, &th->context)); - th->context.ContextFlags = 0; } } @@ -1501,7 +1466,7 @@ windows_nat_target::resume (ptid_t ptid, int step, enum gdb_signal sig) if (resume_all) windows_continue (continue_status, -1, 0); else - windows_continue (continue_status, ptid.tid (), 0); + windows_continue (continue_status, ptid.lwp (), 0); } /* Ctrl-C handler used when the inferior is not run in the same console. The @@ -1530,24 +1495,63 @@ ctrl_c_handler (DWORD event_type) return TRUE; } +/* A wrapper for WaitForDebugEvent that sets "last_wait_event" + appropriately. */ +static BOOL +wait_for_debug_event (DEBUG_EVENT *event, DWORD timeout) +{ + BOOL result = WaitForDebugEvent (event, timeout); + if (result) + last_wait_event = *event; + return result; +} + /* Get the next event from the child. Returns a non-zero thread id if the event requires handling by WFI (or whatever). */ -static int -get_windows_debug_event (struct target_ops *ops, - int pid, struct target_waitstatus *ourstatus) + +int +windows_nat_target::get_windows_debug_event (int pid, + struct target_waitstatus *ourstatus) { BOOL debug_event; DWORD continue_status, event_code; windows_thread_info *th; - static windows_thread_info dummy_thread_info; + static windows_thread_info dummy_thread_info (0, 0, 0); DWORD thread_id = 0; + /* If there is a relevant pending stop, report it now. See the + comment by the definition of "pending_stops" for details on why + this is needed. */ + for (auto iter = pending_stops.begin (); + iter != pending_stops.end (); + ++iter) + { + if (desired_stop_thread_id == -1 + || desired_stop_thread_id == iter->thread_id) + { + thread_id = iter->thread_id; + *ourstatus = iter->status; + current_event = iter->event; + + inferior_ptid = ptid_t (current_event.dwProcessId, thread_id, 0); + current_windows_thread = thread_rec (inferior_ptid, + INVALIDATE_CONTEXT); + current_windows_thread->reload_context = 1; + + DEBUG_EVENTS (("get_windows_debug_event - " + "pending stop found in 0x%x (desired=0x%x)\n", + thread_id, desired_stop_thread_id)); + + pending_stops.erase (iter); + return thread_id; + } + } + last_sig = GDB_SIGNAL_0; - if (!(debug_event = WaitForDebugEvent (¤t_event, 1000))) + if (!(debug_event = wait_for_debug_event (¤t_event, 1000))) goto out; - event_count++; continue_status = DBG_CONTINUE; event_code = current_event.dwDebugEventCode; @@ -1564,8 +1568,7 @@ get_windows_debug_event (struct target_ops *ops, "CREATE_THREAD_DEBUG_EVENT")); if (saw_create != 1) { - struct inferior *inf; - inf = find_inferior_pid (current_event.dwProcessId); + inferior *inf = find_inferior_pid (this, current_event.dwProcessId); if (!saw_create && inf->attach_flag) { /* Kludge around a Windows bug where first event is a create @@ -1580,7 +1583,7 @@ get_windows_debug_event (struct target_ops *ops, /* Record the existence of this thread. */ thread_id = current_event.dwThreadId; th = windows_add_thread - (ptid_t (current_event.dwProcessId, 0, current_event.dwThreadId), + (ptid_t (current_event.dwProcessId, current_event.dwThreadId, 0), current_event.u.CreateThread.hThread, current_event.u.CreateThread.lpThreadLocalBase, false /* main_thread_p */); @@ -1592,8 +1595,8 @@ get_windows_debug_event (struct target_ops *ops, (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId, "EXIT_THREAD_DEBUG_EVENT")); - windows_delete_thread (ptid_t (current_event.dwProcessId, 0, - current_event.dwThreadId), + windows_delete_thread (ptid_t (current_event.dwProcessId, + current_event.dwThreadId, 0), current_event.u.ExitThread.dwExitCode, false /* main_thread_p */); th = &dummy_thread_info; @@ -1611,8 +1614,8 @@ get_windows_debug_event (struct target_ops *ops, current_process_handle = current_event.u.CreateProcessInfo.hProcess; /* Add the main thread. */ th = windows_add_thread - (ptid_t (current_event.dwProcessId, 0, - current_event.dwThreadId), + (ptid_t (current_event.dwProcessId, + current_event.dwThreadId, 0), current_event.u.CreateProcessInfo.hThread, current_event.u.CreateProcessInfo.lpThreadLocalBase, true /* main_thread_p */); @@ -1633,11 +1636,26 @@ get_windows_debug_event (struct target_ops *ops, } else if (saw_create == 1) { - windows_delete_thread (ptid_t (current_event.dwProcessId, 0, - current_event.dwThreadId), + windows_delete_thread (ptid_t (current_event.dwProcessId, + current_event.dwThreadId, 0), 0, true /* main_thread_p */); - ourstatus->kind = TARGET_WAITKIND_EXITED; - ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode; + DWORD exit_status = current_event.u.ExitProcess.dwExitCode; + /* If the exit status looks like a fatal exception, but we + don't recognize the exception's code, make the original + exit status value available, to avoid losing + information. */ + int exit_signal + = WIFSIGNALED (exit_status) ? WTERMSIG (exit_status) : -1; + if (exit_signal == -1) + { + ourstatus->kind = TARGET_WAITKIND_EXITED; + ourstatus->value.integer = exit_status; + } + else + { + ourstatus->kind = TARGET_WAITKIND_SIGNALLED; + ourstatus->value.sig = gdb_signal_from_host (exit_signal); + } thread_id = current_event.dwThreadId; } break; @@ -1676,7 +1694,7 @@ get_windows_debug_event (struct target_ops *ops, "EXCEPTION_DEBUG_EVENT")); if (saw_create != 1) break; - switch (handle_exception (ourstatus)) + switch (handle_exception (ourstatus, debug_exceptions)) { case HANDLE_EXCEPTION_UNHANDLED: default: @@ -1714,14 +1732,36 @@ get_windows_debug_event (struct target_ops *ops, if (!thread_id || saw_create != 1) { - CHECK (windows_continue (continue_status, -1, 0)); + CHECK (windows_continue (continue_status, desired_stop_thread_id, 0)); + } + else if (desired_stop_thread_id != -1 && desired_stop_thread_id != thread_id) + { + /* Pending stop. See the comment by the definition of + "pending_stops" for details on why this is needed. */ + DEBUG_EVENTS (("get_windows_debug_event - " + "unexpected stop in 0x%x (expecting 0x%x)\n", + thread_id, desired_stop_thread_id)); + + if (current_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT + && (current_event.u.Exception.ExceptionRecord.ExceptionCode + == EXCEPTION_BREAKPOINT) + && windows_initialization_done) + { + ptid_t ptid = ptid_t (current_event.dwProcessId, thread_id, 0); + th = thread_rec (ptid, INVALIDATE_CONTEXT); + th->stopped_at_software_breakpoint = true; + } + pending_stops.push_back ({thread_id, *ourstatus, current_event}); + thread_id = 0; + CHECK (windows_continue (continue_status, desired_stop_thread_id, 0)); } else { - inferior_ptid = ptid_t (current_event.dwProcessId, 0, thread_id); - current_thread = th; - if (!current_thread) - current_thread = thread_rec (thread_id, TRUE); + inferior_ptid = ptid_t (current_event.dwProcessId, thread_id, 0); + current_windows_thread = th; + if (!current_windows_thread) + current_windows_thread = thread_rec (inferior_ptid, + INVALIDATE_CONTEXT); } out: @@ -1772,11 +1812,25 @@ windows_nat_target::wait (ptid_t ptid, struct target_waitstatus *ourstatus, the user tries to resume the execution in the inferior. This is a classic race that we should try to fix one day. */ SetConsoleCtrlHandler (&ctrl_c_handler, TRUE); - retval = get_windows_debug_event (this, pid, ourstatus); + retval = get_windows_debug_event (pid, ourstatus); SetConsoleCtrlHandler (&ctrl_c_handler, FALSE); if (retval) - return ptid_t (current_event.dwProcessId, 0, retval); + { + ptid_t result = ptid_t (current_event.dwProcessId, retval, 0); + + if (current_windows_thread != nullptr) + { + current_windows_thread->stopped_at_software_breakpoint = false; + if (current_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT + && (current_event.u.Exception.ExceptionRecord.ExceptionCode + == EXCEPTION_BREAKPOINT) + && windows_initialization_done) + current_windows_thread->stopped_at_software_breakpoint = true; + } + + return result; + } else { int detach = 0; @@ -1801,28 +1855,82 @@ windows_add_all_dlls (void) HMODULE *hmodules; int i; - if (EnumProcessModules (current_process_handle, &dummy_hmodule, - sizeof (HMODULE), &cb_needed) == 0) - return; +#ifdef __x86_64__ + if (wow64_process) + { + if (EnumProcessModulesEx (current_process_handle, &dummy_hmodule, + sizeof (HMODULE), &cb_needed, + LIST_MODULES_32BIT) == 0) + return; + } + else +#endif + { + if (EnumProcessModules (current_process_handle, &dummy_hmodule, + sizeof (HMODULE), &cb_needed) == 0) + return; + } if (cb_needed < 1) return; hmodules = (HMODULE *) alloca (cb_needed); - if (EnumProcessModules (current_process_handle, hmodules, - cb_needed, &cb_needed) == 0) - return; +#ifdef __x86_64__ + if (wow64_process) + { + if (EnumProcessModulesEx (current_process_handle, hmodules, + cb_needed, &cb_needed, + LIST_MODULES_32BIT) == 0) + return; + } + else +#endif + { + if (EnumProcessModules (current_process_handle, hmodules, + cb_needed, &cb_needed) == 0) + return; + } + char system_dir[__PMAX]; + char syswow_dir[__PMAX]; + size_t system_dir_len = 0; + bool convert_syswow_dir = false; +#ifdef __x86_64__ + if (wow64_process) +#endif + { + /* This fails on 32bit Windows because it has no SysWOW64 directory, + and in this case a path conversion isn't necessary. */ + UINT len = GetSystemWow64DirectoryA (syswow_dir, sizeof (syswow_dir)); + if (len > 0) + { + /* Check that we have passed a large enough buffer. */ + gdb_assert (len < sizeof (syswow_dir)); + + len = GetSystemDirectoryA (system_dir, sizeof (system_dir)); + /* Error check. */ + gdb_assert (len != 0); + /* Check that we have passed a large enough buffer. */ + gdb_assert (len < sizeof (system_dir)); + + strcat (system_dir, "\\"); + strcat (syswow_dir, "\\"); + system_dir_len = strlen (system_dir); + + convert_syswow_dir = true; + } + + } for (i = 1; i < (int) (cb_needed / sizeof (HMODULE)); i++) { MODULEINFO mi; #ifdef __USEWIDE wchar_t dll_name[__PMAX]; - char name[__PMAX]; + char dll_name_mb[__PMAX]; #else char dll_name[__PMAX]; - char *name; #endif + const char *name; if (GetModuleInformation (current_process_handle, hmodules[i], &mi, sizeof (mi)) == 0) continue; @@ -1830,10 +1938,23 @@ windows_add_all_dlls (void) dll_name, sizeof (dll_name)) == 0) continue; #ifdef __USEWIDE - wcstombs (name, dll_name, __PMAX); + wcstombs (dll_name_mb, dll_name, __PMAX); + name = dll_name_mb; #else name = dll_name; #endif + /* Convert the DLL path of 32bit processes returned by + GetModuleFileNameEx from the 64bit system directory to the + 32bit syswow64 directory if necessary. */ + std::string syswow_dll_path; + if (convert_syswow_dir + && strncasecmp (name, system_dir, system_dir_len) == 0 + && strchr (name + system_dir_len, '\\') == nullptr) + { + syswow_dll_path = syswow_dir; + syswow_dll_path += name + system_dir_len; + name = syswow_dll_path.c_str(); + } solib_end->next = windows_make_so (name, mi.lpBaseOfDll); solib_end = solib_end->next; @@ -1847,8 +1968,6 @@ do_initial_windows_stuff (struct target_ops *ops, DWORD pid, int attaching) struct inferior *inf; last_sig = GDB_SIGNAL_0; - event_count = 0; - exception_count = 0; open_process_used = 0; debug_registers_changed = 0; debug_registers_used = 0; @@ -1866,6 +1985,21 @@ do_initial_windows_stuff (struct target_ops *ops, DWORD pid, int attaching) clear_proceed_status (0); init_wait_for_inferior (); +#ifdef __x86_64__ + ignore_first_breakpoint = !attaching && wow64_process; + + if (!wow64_process) + { + windows_set_context_register_offsets (amd64_mappings); + windows_set_segment_register_p (amd64_windows_segment_register_p); + } + else +#endif + { + windows_set_context_register_offsets (i386_mappings); + windows_set_segment_register_p (i386_windows_segment_register_p); + } + inf = current_inferior (); inferior_appeared (inf, pid); inf->attach_flag = attaching; @@ -1999,13 +2133,14 @@ windows_nat_target::attach (const char *args, int from_tty) #endif if (!ok) - error (_("Can't attach to process.")); + error (_("Can't attach to process %u (error %u)"), + (unsigned) pid, (unsigned) GetLastError ()); DebugSetProcessKillOnExit (FALSE); if (from_tty) { - char *exec_file = (char *) get_exec_file (0); + const char *exec_file = get_exec_file (0); if (exec_file) printf_unfiltered ("Attaching to program `%s', %s\n", exec_file, @@ -2015,6 +2150,17 @@ windows_nat_target::attach (const char *args, int from_tty) target_pid_to_str (ptid_t (pid)).c_str ()); } +#ifdef __x86_64__ + HANDLE h = OpenProcess (PROCESS_QUERY_INFORMATION, FALSE, pid); + if (h != NULL) + { + BOOL wow64; + if (IsWow64Process (h, &wow64)) + wow64_process = wow64; + CloseHandle (h); + } +#endif + do_initial_windows_stuff (this, pid, 1); target_terminal::ours (); } @@ -2069,9 +2215,21 @@ windows_get_exec_module_filename (char *exe_name_ret, size_t exe_name_max_len) DWORD cbNeeded; cbNeeded = 0; - if (!EnumProcessModules (current_process_handle, &dh_buf, - sizeof (HMODULE), &cbNeeded) || !cbNeeded) - return 0; +#ifdef __x86_64__ + if (wow64_process) + { + if (!EnumProcessModulesEx (current_process_handle, &dh_buf, + sizeof (HMODULE), &cbNeeded, + LIST_MODULES_32BIT) || !cbNeeded) + return 0; + } + else +#endif + { + if (!EnumProcessModules (current_process_handle, &dh_buf, + sizeof (HMODULE), &cbNeeded) || !cbNeeded) + return 0; + } /* We know the executable is always first in the list of modules, which we just fetched. So no need to fetch more. */ @@ -2725,7 +2883,7 @@ windows_nat_target::create_inferior (const char *exec_file, redirect_inferior_handles (allargs, allargs_copy, &fd_inp, &fd_out, &fd_err); if (errno) - warning (_("Error in redirection: %s."), strerror (errno)); + warning (_("Error in redirection: %s."), safe_strerror (errno)); else errno = e; allargs_len = strlen (allargs_copy); @@ -2829,6 +2987,12 @@ windows_nat_target::create_inferior (const char *exec_file, error (_("Error creating process %s, (error %u)."), exec_file, (unsigned) GetLastError ()); +#ifdef __x86_64__ + BOOL wow64; + if (IsWow64Process (pi.hProcess, &wow64)) + wow64_process = wow64; +#endif + CloseHandle (pi.hThread); CloseHandle (pi.hProcess); @@ -2852,6 +3016,7 @@ windows_nat_target::mourn_inferior () CHECK (CloseHandle (current_process_handle)); open_process_used = 0; } + siginfo_er.ExceptionCode = 0; inf_child_target::mourn_inferior (); } @@ -2915,7 +3080,7 @@ windows_nat_target::kill () { if (!windows_continue (DBG_CONTINUE, -1, 1)) break; - if (!WaitForDebugEvent (¤t_event, INFINITE)) + if (!wait_for_debug_event (¤t_event, INFINITE)) break; if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; @@ -2935,8 +3100,8 @@ windows_nat_target::close () std::string windows_nat_target::pid_to_str (ptid_t ptid) { - if (ptid.tid () != 0) - return string_printf ("Thread %d.0x%lx", ptid.pid (), ptid.tid ()); + if (ptid.lwp () != 0) + return string_printf ("Thread %d.0x%lx", ptid.pid (), ptid.lwp ()); return normal_pid_to_str (ptid); } @@ -2964,6 +3129,7 @@ windows_xfer_shared_libraries (struct target_ops *ops, windows_xfer_shared_library (so->so_name, (CORE_ADDR) (uintptr_t) li->load_addr, + &li->text_offset, target_gdbarch (), &obstack); } obstack_grow_str0 (&obstack, "\n"); @@ -2984,6 +3150,51 @@ windows_xfer_shared_libraries (struct target_ops *ops, return len != 0 ? TARGET_XFER_OK : TARGET_XFER_EOF; } +/* Helper for windows_nat_target::xfer_partial that handles signal info. */ + +static enum target_xfer_status +windows_xfer_siginfo (gdb_byte *readbuf, ULONGEST offset, ULONGEST len, + ULONGEST *xfered_len) +{ + char *buf = (char *) &siginfo_er; + size_t bufsize = sizeof (siginfo_er); + +#ifdef __x86_64__ + EXCEPTION_RECORD32 er32; + if (wow64_process) + { + buf = (char *) &er32; + bufsize = sizeof (er32); + + er32.ExceptionCode = siginfo_er.ExceptionCode; + er32.ExceptionFlags = siginfo_er.ExceptionFlags; + er32.ExceptionRecord = (uintptr_t) siginfo_er.ExceptionRecord; + er32.ExceptionAddress = (uintptr_t) siginfo_er.ExceptionAddress; + er32.NumberParameters = siginfo_er.NumberParameters; + int i; + for (i = 0; i < EXCEPTION_MAXIMUM_PARAMETERS; i++) + er32.ExceptionInformation[i] = siginfo_er.ExceptionInformation[i]; + } +#endif + + if (siginfo_er.ExceptionCode == 0) + return TARGET_XFER_E_IO; + + if (readbuf == nullptr) + return TARGET_XFER_E_IO; + + if (offset > bufsize) + return TARGET_XFER_E_IO; + + if (offset + len > bufsize) + len = bufsize - offset; + + memcpy (readbuf, buf + offset, len); + *xfered_len = len; + + return TARGET_XFER_OK; +} + enum target_xfer_status windows_nat_target::xfer_partial (enum target_object object, const char *annex, gdb_byte *readbuf, @@ -2999,6 +3210,9 @@ windows_nat_target::xfer_partial (enum target_object object, return windows_xfer_shared_libraries (this, object, annex, readbuf, writebuf, offset, len, xfered_len); + case TARGET_OBJECT_SIGNAL_INFO: + return windows_xfer_siginfo (readbuf, offset, len, xfered_len); + default: if (beneath () == NULL) { @@ -3021,7 +3235,7 @@ windows_nat_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr) { windows_thread_info *th; - th = thread_rec (ptid.tid (), 0); + th = thread_rec (ptid, DONT_INVALIDATE_CONTEXT); if (th == NULL) return false; @@ -3034,7 +3248,7 @@ windows_nat_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr) ptid_t windows_nat_target::get_ada_task_ptid (long lwp, long thread) { - return ptid_t (inferior_ptid.pid (), 0, lwp); + return ptid_t (inferior_ptid.pid (), lwp, 0); } /* Implementation of the to_thread_name method. */ @@ -3042,12 +3256,13 @@ windows_nat_target::get_ada_task_ptid (long lwp, long thread) const char * windows_nat_target::thread_name (struct thread_info *thr) { - return thread_rec (thr->ptid.tid (), 0)->name; + return thread_rec (thr->ptid, DONT_INVALIDATE_CONTEXT)->name.get (); } +void _initialize_windows_nat (); void -_initialize_windows_nat (void) +_initialize_windows_nat () { x86_dr_low.set_control = cygwin_set_dr7; x86_dr_low.set_addr = cygwin_set_dr; @@ -3200,16 +3415,15 @@ cygwin_get_dr7 (void) bool windows_nat_target::thread_alive (ptid_t ptid) { - int tid; - - gdb_assert (ptid.tid () != 0); - tid = ptid.tid (); + gdb_assert (ptid.lwp () != 0); - return WaitForSingleObject (thread_rec (tid, FALSE)->h, 0) != WAIT_OBJECT_0; + return (WaitForSingleObject (thread_rec (ptid, DONT_INVALIDATE_CONTEXT)->h, 0) + != WAIT_OBJECT_0); } +void _initialize_check_for_gdb_ini (); void -_initialize_check_for_gdb_ini (void) +_initialize_check_for_gdb_ini () { char *homedir; if (inhibit_gdbinit) @@ -3305,8 +3519,9 @@ bad_GetConsoleFontSize (HANDLE w, DWORD nFont) /* Load any functions which may not be available in ancient versions of Windows. */ +void _initialize_loadable (); void -_initialize_loadable (void) +_initialize_loadable () { HMODULE hm = NULL; @@ -3322,6 +3537,12 @@ _initialize_loadable (void) GPA (hm, GetConsoleFontSize); GPA (hm, DebugActiveProcessStop); GPA (hm, GetCurrentConsoleFont); +#ifdef __x86_64__ + GPA (hm, Wow64SuspendThread); + GPA (hm, Wow64GetThreadContext); + GPA (hm, Wow64SetThreadContext); + GPA (hm, Wow64GetThreadSelectorEntry); +#endif } /* Set variables to dummy versions of these processes if the function @@ -3344,6 +3565,9 @@ _initialize_loadable (void) if (hm) { GPA (hm, EnumProcessModules); +#ifdef __x86_64__ + GPA (hm, EnumProcessModulesEx); +#endif GPA (hm, GetModuleInformation); GetModuleFileNameEx = (GetModuleFileNameEx_ftype *) GetProcAddress (hm, GetModuleFileNameEx_name);