X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdbserver%2Fwin32-low.cc;h=a42fe25a2205a21f570fd80e07b887797f116669;hb=refs%2Fheads%2Fconcurrent-displaced-stepping-2020-04-01;hp=9e4e4c368ca19eb23464907ec05864ffb3b6b241;hpb=a5a4d4cd0c5fb2240d8157c98e2fe5078f9d3263;p=deliverable%2Fbinutils-gdb.git diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc index 9e4e4c368c..a42fe25a22 100644 --- a/gdbserver/win32-low.cc +++ b/gdbserver/win32-low.cc @@ -36,6 +36,8 @@ #include "gdbsupport/common-inferior.h" #include "gdbsupport/gdb_wait.h" +using namespace windows_nat; + #ifndef USE_WIN32API #include #endif @@ -72,14 +74,6 @@ int using_threads = 1; /* Globals. */ static int attaching = 0; -static HANDLE current_process_handle = NULL; -static DWORD current_process_id = 0; -static DWORD main_thread_id = 0; -static EXCEPTION_RECORD siginfo_er; /* Contents of $_siginfo */ -static enum gdb_signal last_sig = GDB_SIGNAL_0; - -/* The current debug event from WaitForDebugEvent. */ -static DEBUG_EVENT current_event; /* A status that hasn't been reported to the core yet, and so win32_wait should return it next, instead of fetching the next @@ -94,15 +88,33 @@ static int soft_interrupt_requested = 0; by suspending all the threads. */ static int faked_breakpoint = 0; +/* True if current_process_handle needs to be closed. */ +static bool open_process_used = false; + +#ifdef __x86_64__ +bool wow64_process = false; +#endif + const struct target_desc *win32_tdesc; +#ifdef __x86_64__ +const struct target_desc *wow64_win32_tdesc; +#endif -#define NUM_REGS (the_low_target.num_regs) +#define NUM_REGS (the_low_target.num_regs ()) typedef BOOL (WINAPI *winapi_DebugActiveProcessStop) (DWORD dwProcessId); typedef BOOL (WINAPI *winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit); typedef BOOL (WINAPI *winapi_DebugBreakProcess) (HANDLE); typedef BOOL (WINAPI *winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD); +#ifdef __x86_64__ +typedef BOOL (WINAPI *winapi_Wow64SetThreadContext) (HANDLE, + const WOW64_CONTEXT *); + +winapi_Wow64GetThreadContext win32_Wow64GetThreadContext; +static winapi_Wow64SetThreadContext win32_Wow64SetThreadContext; +#endif + #ifndef _WIN32_WCE static void win32_add_all_dlls (void); #endif @@ -125,9 +137,14 @@ debug_event_ptid (DEBUG_EVENT *event) /* Get the thread context of the thread associated with TH. */ static void -win32_get_thread_context (win32_thread_info *th) +win32_get_thread_context (windows_thread_info *th) { - memset (&th->context, 0, sizeof (CONTEXT)); +#ifdef __x86_64__ + if (wow64_process) + memset (&th->wow64_context, 0, sizeof (WOW64_CONTEXT)); + else +#endif + memset (&th->context, 0, sizeof (CONTEXT)); (*the_low_target.get_thread_context) (th); #ifdef _WIN32_WCE memcpy (&th->base_context, &th->context, sizeof (CONTEXT)); @@ -137,7 +154,7 @@ win32_get_thread_context (win32_thread_info *th) /* Set the thread context of the thread associated with TH. */ static void -win32_set_thread_context (win32_thread_info *th) +win32_set_thread_context (windows_thread_info *th) { #ifdef _WIN32_WCE /* Calling SuspendThread on a thread that is running kernel code @@ -152,13 +169,20 @@ win32_set_thread_context (win32_thread_info *th) it between stopping and resuming. */ if (memcmp (&th->context, &th->base_context, sizeof (CONTEXT)) != 0) #endif - SetThreadContext (th->h, &th->context); + { +#ifdef __x86_64__ + if (wow64_process) + win32_Wow64SetThreadContext (th->h, &th->wow64_context); + else +#endif + SetThreadContext (th->h, &th->context); + } } /* Set the thread context of the thread associated with TH. */ static void -win32_prepare_to_resume (win32_thread_info *th) +win32_prepare_to_resume (windows_thread_info *th) { if (the_low_target.prepare_to_resume != NULL) (*the_low_target.prepare_to_resume) (th); @@ -167,55 +191,55 @@ win32_prepare_to_resume (win32_thread_info *th) /* See win32-low.h. */ void -win32_require_context (win32_thread_info *th) +win32_require_context (windows_thread_info *th) { - if (th->context.ContextFlags == 0) + DWORD context_flags; +#ifdef __x86_64__ + if (wow64_process) + context_flags = th->wow64_context.ContextFlags; + else +#endif + context_flags = th->context.ContextFlags; + if (context_flags == 0) { - if (!th->suspended) - { - if (SuspendThread (th->h) == (DWORD) -1) - { - DWORD err = GetLastError (); - OUTMSG (("warning: SuspendThread failed in thread_rec, " - "(error %d): %s\n", (int) err, strwinerror (err))); - } - else - th->suspended = 1; - } - + th->suspend (); win32_get_thread_context (th); } } -/* Find a thread record given a thread id. If GET_CONTEXT is set then - also retrieve the context for this thread. */ -static win32_thread_info * -thread_rec (ptid_t ptid, int get_context) +/* See nat/windows-nat.h. */ + +windows_thread_info * +windows_nat::thread_rec (ptid_t ptid, thread_disposition_type disposition) { thread_info *thread = find_thread_ptid (ptid); if (thread == NULL) return NULL; - win32_thread_info *th = (win32_thread_info *) thread_target_data (thread); - if (get_context) + windows_thread_info *th = (windows_thread_info *) thread_target_data (thread); + if (disposition != DONT_INVALIDATE_CONTEXT) win32_require_context (th); return th; } /* Add a thread to the thread list. */ -static win32_thread_info * +static windows_thread_info * child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb) { - win32_thread_info *th; + windows_thread_info *th; ptid_t ptid = ptid_t (pid, tid, 0); - if ((th = thread_rec (ptid, FALSE))) + if ((th = thread_rec (ptid, DONT_INVALIDATE_CONTEXT))) return th; - th = XCNEW (win32_thread_info); - th->tid = tid; - th->h = h; - th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb; + 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 += 2 * 4096; /* page size = 4096 */ +#endif + th = new windows_thread_info (tid, h, base); add_thread (ptid, th); @@ -229,11 +253,10 @@ child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb) static void delete_thread_info (thread_info *thread) { - win32_thread_info *th = (win32_thread_info *) thread_target_data (thread); + windows_thread_info *th = (windows_thread_info *) thread_target_data (thread); remove_thread (thread); - CloseHandle (th->h); - free (th); + delete th; } /* Delete a thread from the list of threads. */ @@ -254,46 +277,51 @@ child_delete_thread (DWORD pid, DWORD tid) /* These watchpoint related wrapper functions simply pass on the function call if the low target has registered a corresponding function. */ -static int -win32_supports_z_point_type (char z_type) +bool +win32_process_target::supports_z_point_type (char z_type) { - return (the_low_target.supports_z_point_type != NULL - && the_low_target.supports_z_point_type (z_type)); + return (z_type == Z_PACKET_SW_BP + || (the_low_target.supports_z_point_type != NULL + && the_low_target.supports_z_point_type (z_type))); } -static int -win32_insert_point (enum raw_bkpt_type type, CORE_ADDR addr, - int size, struct raw_breakpoint *bp) +int +win32_process_target::insert_point (enum raw_bkpt_type type, CORE_ADDR addr, + int size, raw_breakpoint *bp) { - if (the_low_target.insert_point != NULL) + if (type == raw_bkpt_type_sw) + return insert_memory_breakpoint (bp); + else if (the_low_target.insert_point != NULL) return the_low_target.insert_point (type, addr, size, bp); else /* Unsupported (see target.h). */ return 1; } -static int -win32_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, - int size, struct raw_breakpoint *bp) +int +win32_process_target::remove_point (enum raw_bkpt_type type, CORE_ADDR addr, + int size, raw_breakpoint *bp) { - if (the_low_target.remove_point != NULL) + if (type == raw_bkpt_type_sw) + return remove_memory_breakpoint (bp); + else if (the_low_target.remove_point != NULL) return the_low_target.remove_point (type, addr, size, bp); else /* Unsupported (see target.h). */ return 1; } -static int -win32_stopped_by_watchpoint (void) +bool +win32_process_target::stopped_by_watchpoint () { if (the_low_target.stopped_by_watchpoint != NULL) return the_low_target.stopped_by_watchpoint (); else - return 0; + return false; } -static CORE_ADDR -win32_stopped_data_address (void) +CORE_ADDR +win32_process_target::stopped_data_address () { if (the_low_target.stopped_data_address != NULL) return the_low_target.stopped_data_address (); @@ -358,11 +386,35 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached) soft_interrupt_requested = 0; faked_breakpoint = 0; + open_process_used = true; memset (¤t_event, 0, sizeof (current_event)); +#ifdef __x86_64__ + BOOL wow64; + if (!IsWow64Process (proch, &wow64)) + { + DWORD err = GetLastError (); + error ("Check if WOW64 process failed (error %d): %s\n", + (int) err, strwinerror (err)); + } + wow64_process = wow64; + + if (wow64_process + && (win32_Wow64GetThreadContext == nullptr + || win32_Wow64SetThreadContext == nullptr)) + error ("WOW64 debugging is not supported on this system.\n"); + + ignore_first_breakpoint = !attached && wow64_process; +#endif + proc = add_process (pid, attached); - proc->tdesc = win32_tdesc; +#ifdef __x86_64__ + if (wow64_process) + proc->tdesc = wow64_win32_tdesc; + else +#endif + proc->tdesc = win32_tdesc; child_init_thread_list (); child_initialization_done = 0; @@ -377,7 +429,7 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached) { struct target_waitstatus status; - the_target->pt->wait (minus_one_ptid, &status, 0); + the_target->wait (minus_one_ptid, &status, 0); /* Note win32_wait doesn't return thread events. */ if (status.kind != TARGET_WAITKIND_LOADED) @@ -393,7 +445,7 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached) resume.kind = resume_continue; resume.sig = 0; - the_target->pt->resume (&resume, 1); + the_target->resume (&resume, 1); } } @@ -424,7 +476,7 @@ do_initial_child_stuff (HANDLE proch, DWORD pid, int attached) static void continue_one_thread (thread_info *thread, int thread_id) { - win32_thread_info *th = (win32_thread_info *) thread_target_data (thread); + windows_thread_info *th = (windows_thread_info *) thread_target_data (thread); if (thread_id == -1 || thread_id == th->tid) { @@ -432,19 +484,20 @@ continue_one_thread (thread_info *thread, int thread_id) if (th->suspended) { - if (th->context.ContextFlags) + DWORD *context_flags; +#ifdef __x86_64__ + if (wow64_process) + context_flags = &th->wow64_context.ContextFlags; + else +#endif + context_flags = &th->context.ContextFlags; + if (*context_flags) { win32_set_thread_context (th); - th->context.ContextFlags = 0; + *context_flags = 0; } - if (ResumeThread (th->h) == (DWORD) -1) - { - DWORD err = GetLastError (); - OUTMSG (("warning: ResumeThread failed in continue_one_thread, " - "(error %d): %s\n", (int) err, strwinerror (err))); - } - th->suspended = 0; + th->resume (); } } } @@ -452,6 +505,10 @@ continue_one_thread (thread_info *thread, int thread_id) static BOOL child_continue (DWORD continue_status, int thread_id) { + desired_stop_thread_id = thread_id; + if (matching_pending_stop (debug_threads)) + return TRUE; + /* The inferior will only continue after the ContinueDebugEvent call. */ for_each_thread ([&] (thread_info *thread) @@ -460,12 +517,7 @@ child_continue (DWORD continue_status, int thread_id) }); faked_breakpoint = 0; - if (!ContinueDebugEvent (current_event.dwProcessId, - current_event.dwThreadId, - continue_status)) - return FALSE; - - return TRUE; + return continue_last_debug_event (continue_status, debug_threads); } /* Fetch register(s) from the current thread context. */ @@ -473,7 +525,8 @@ static void child_fetch_inferior_registers (struct regcache *regcache, int r) { int regno; - win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE); + windows_thread_info *th = thread_rec (current_thread_ptid (), + INVALIDATE_CONTEXT); if (r == -1 || r > NUM_REGS) child_fetch_inferior_registers (regcache, NUM_REGS); else @@ -487,7 +540,8 @@ static void child_store_inferior_registers (struct regcache *regcache, int r) { int regno; - win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE); + windows_thread_info *th = thread_rec (current_thread_ptid (), + INVALIDATE_CONTEXT); if (r == -1 || r == 0 || r > NUM_REGS) child_store_inferior_registers (regcache, NUM_REGS); else @@ -643,7 +697,7 @@ win32_process_target::create_inferior (const char *program, DWORD flags; PROCESS_INFORMATION pi; DWORD err; - std::string str_program_args = stringify_argv (program_args); + std::string str_program_args = construct_inferior_arguments (program_args); char *args = (char *) str_program_args.c_str (); /* win32_wait needs to know we're not attaching. */ @@ -758,9 +812,10 @@ win32_process_target::attach (unsigned long pid) (int) err, strwinerror (err)); } -/* Handle OUTPUT_DEBUG_STRING_EVENT from child process. */ -static void -handle_output_debug_string (void) +/* See nat/windows-nat.h. */ + +int +windows_nat::handle_output_debug_string (struct target_waitstatus *ourstatus) { #define READ_BUFFER_LEN 1024 CORE_ADDR addr; @@ -768,7 +823,7 @@ handle_output_debug_string (void) DWORD nbytes = current_event.u.DebugString.nDebugStringLength; if (nbytes == 0) - return; + return 0; if (nbytes > READ_BUFFER_LEN) nbytes = READ_BUFFER_LEN; @@ -781,13 +836,13 @@ handle_output_debug_string (void) in Unicode. */ WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 }; if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0) - return; + return 0; wcstombs (s, buffer, (nbytes + 1) / sizeof (WCHAR)); } else { if (read_inferior_memory (addr, (unsigned char *) s, nbytes) != 0) - return; + return 0; } if (!startswith (s, "cYg")) @@ -795,19 +850,24 @@ handle_output_debug_string (void) if (!server_waiting) { OUTMSG2(("%s", s)); - return; + return 0; } monitor_output (s); } #undef READ_BUFFER_LEN + + return 0; } static void win32_clear_inferiors (void) { - if (current_process_handle != NULL) - CloseHandle (current_process_handle); + if (open_process_used) + { + CloseHandle (current_process_handle); + open_process_used = false; + } for_each_thread (delete_thread_info); siginfo_er.ExceptionCode = 0; @@ -824,12 +884,12 @@ win32_process_target::kill (process_info *process) { if (!child_continue (DBG_CONTINUE, -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; else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) - handle_output_debug_string (); + handle_output_debug_string (nullptr); } win32_clear_inferiors (); @@ -911,7 +971,7 @@ win32_process_target::resume (thread_resume *resume_info, size_t n) DWORD tid; enum gdb_signal sig; int step; - win32_thread_info *th; + windows_thread_info *th; DWORD continue_status = DBG_CONTINUE; ptid_t ptid; @@ -956,12 +1016,19 @@ win32_process_target::resume (thread_resume *resume_info, size_t n) /* Get context for the currently selected thread. */ ptid = debug_event_ptid (¤t_event); - th = thread_rec (ptid, FALSE); + th = thread_rec (ptid, DONT_INVALIDATE_CONTEXT); if (th) { win32_prepare_to_resume (th); - if (th->context.ContextFlags) + DWORD *context_flags; +#ifdef __x86_64__ + if (wow64_process) + context_flags = &th->wow64_context.ContextFlags; + else +#endif + context_flags = &th->context.ContextFlags; + if (*context_flags) { /* Move register values from the inferior into the thread context structure. */ @@ -977,7 +1044,7 @@ win32_process_target::resume (thread_resume *resume_info, size_t n) } win32_set_thread_context (th); - th->context.ContextFlags = 0; + *context_flags = 0; } } @@ -1048,63 +1115,21 @@ win32_add_one_solib (const char *name, CORE_ADDR load_addr) loaded_dll (buf2, load_addr); } -static char * -get_image_name (HANDLE h, void *address, int unicode) -{ - static char buf[(2 * MAX_PATH) + 1]; - 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 = XALLOCAVEC (WCHAR, len); - ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR), - &done); - - WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0); - } - - return buf; -} - typedef BOOL (WINAPI *winapi_EnumProcessModules) (HANDLE, HMODULE *, DWORD, LPDWORD); +#ifdef __x86_64__ +typedef BOOL (WINAPI *winapi_EnumProcessModulesEx) (HANDLE, HMODULE *, DWORD, + LPDWORD, DWORD); +#endif typedef BOOL (WINAPI *winapi_GetModuleInformation) (HANDLE, HMODULE, LPMODULEINFO, DWORD); typedef DWORD (WINAPI *winapi_GetModuleFileNameExA) (HANDLE, HMODULE, LPSTR, DWORD); static winapi_EnumProcessModules win32_EnumProcessModules; +#ifdef __x86_64__ +static winapi_EnumProcessModulesEx win32_EnumProcessModulesEx; +#endif static winapi_GetModuleInformation win32_GetModuleInformation; static winapi_GetModuleFileNameExA win32_GetModuleFileNameExA; @@ -1122,12 +1147,21 @@ load_psapi (void) return FALSE; win32_EnumProcessModules = GETPROCADDRESS (dll, EnumProcessModules); +#ifdef __x86_64__ + win32_EnumProcessModulesEx = + GETPROCADDRESS (dll, EnumProcessModulesEx); +#endif win32_GetModuleInformation = GETPROCADDRESS (dll, GetModuleInformation); win32_GetModuleFileNameExA = GETPROCADDRESS (dll, GetModuleFileNameExA); } +#ifdef __x86_64__ + if (wow64_process && win32_EnumProcessModulesEx == nullptr) + return FALSE; +#endif + return (win32_EnumProcessModules != NULL && win32_GetModuleInformation != NULL && win32_GetModuleFileNameExA != NULL); @@ -1151,10 +1185,19 @@ win32_add_all_dlls (void) return; cbNeeded = 0; - ok = (*win32_EnumProcessModules) (current_process_handle, - DllHandle, - sizeof (HMODULE), - &cbNeeded); +#ifdef __x86_64__ + if (wow64_process) + ok = (*win32_EnumProcessModulesEx) (current_process_handle, + DllHandle, + sizeof (HMODULE), + &cbNeeded, + LIST_MODULES_32BIT); + else +#endif + ok = (*win32_EnumProcessModules) (current_process_handle, + DllHandle, + sizeof (HMODULE), + &cbNeeded); if (!ok || !cbNeeded) return; @@ -1163,13 +1206,53 @@ win32_add_all_dlls (void) if (!DllHandle) return; - ok = (*win32_EnumProcessModules) (current_process_handle, - DllHandle, - cbNeeded, - &cbNeeded); +#ifdef __x86_64__ + if (wow64_process) + ok = (*win32_EnumProcessModulesEx) (current_process_handle, + DllHandle, + cbNeeded, + &cbNeeded, + LIST_MODULES_32BIT); + else +#endif + ok = (*win32_EnumProcessModules) (current_process_handle, + DllHandle, + cbNeeded, + &cbNeeded); if (!ok) return; + char system_dir[MAX_PATH]; + char syswow_dir[MAX_PATH]; + 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 < ((size_t) cbNeeded / sizeof (HMODULE)); i++) { MODULEINFO mi; @@ -1185,7 +1268,22 @@ win32_add_all_dlls (void) dll_name, MAX_PATH) == 0) continue; - win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) mi.lpBaseOfDll); + + const char *name = dll_name; + /* 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 (dll_name, system_dir, system_dir_len) == 0 + && strchr (dll_name + system_dir_len, '\\') == nullptr) + { + syswow_dll_path = syswow_dir; + syswow_dll_path += dll_name + system_dir_len; + name = syswow_dll_path.c_str(); + } + + win32_add_one_solib (name, (CORE_ADDR) (uintptr_t) mi.lpBaseOfDll); } } #endif @@ -1194,18 +1292,13 @@ typedef HANDLE (WINAPI *winapi_CreateToolhelp32Snapshot) (DWORD, DWORD); typedef BOOL (WINAPI *winapi_Module32First) (HANDLE, LPMODULEENTRY32); typedef BOOL (WINAPI *winapi_Module32Next) (HANDLE, LPMODULEENTRY32); -/* Handle a DLL load event. +/* See nat/windows-nat.h. */ - This function assumes that this event did not occur during inferior - initialization, where their event info may be incomplete (see - do_initial_child_stuff and win32_add_all_dlls for more info on - how we handle DLL loading during that phase). */ - -static void -handle_load_dll (void) +void +windows_nat::handle_load_dll () { LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll; - char *dll_name; + const char *dll_name; dll_name = get_image_name (current_process_handle, event->lpImageName, event->fUnicode); @@ -1215,15 +1308,10 @@ handle_load_dll (void) win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) event->lpBaseOfDll); } -/* Handle a DLL unload event. - - This function assumes that this event did not occur during inferior - initialization, where their event info may be incomplete (see - do_initial_child_stuff and win32_add_one_solib for more info - on how we handle DLL loading during that phase). */ +/* See nat/windows-nat.h. */ -static void -handle_unload_dll (void) +void +windows_nat::handle_unload_dll () { CORE_ADDR load_addr = (CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll; @@ -1235,133 +1323,12 @@ handle_unload_dll (void) unloaded_dll (NULL, load_addr); } -static void -handle_exception (struct target_waitstatus *ourstatus) -{ - DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode; - - memcpy (&siginfo_er, ¤t_event.u.Exception.ExceptionRecord, - sizeof siginfo_er); - - ourstatus->kind = TARGET_WAITKIND_STOPPED; - - switch (code) - { - case EXCEPTION_ACCESS_VIOLATION: - OUTMSG2 (("EXCEPTION_ACCESS_VIOLATION")); - ourstatus->value.sig = GDB_SIGNAL_SEGV; - break; - case STATUS_STACK_OVERFLOW: - OUTMSG2 (("STATUS_STACK_OVERFLOW")); - ourstatus->value.sig = GDB_SIGNAL_SEGV; - break; - case STATUS_FLOAT_DENORMAL_OPERAND: - OUTMSG2 (("STATUS_FLOAT_DENORMAL_OPERAND")); - ourstatus->value.sig = GDB_SIGNAL_FPE; - break; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - OUTMSG2 (("EXCEPTION_ARRAY_BOUNDS_EXCEEDED")); - ourstatus->value.sig = GDB_SIGNAL_FPE; - break; - case STATUS_FLOAT_INEXACT_RESULT: - OUTMSG2 (("STATUS_FLOAT_INEXACT_RESULT")); - ourstatus->value.sig = GDB_SIGNAL_FPE; - break; - case STATUS_FLOAT_INVALID_OPERATION: - OUTMSG2 (("STATUS_FLOAT_INVALID_OPERATION")); - ourstatus->value.sig = GDB_SIGNAL_FPE; - break; - case STATUS_FLOAT_OVERFLOW: - OUTMSG2 (("STATUS_FLOAT_OVERFLOW")); - ourstatus->value.sig = GDB_SIGNAL_FPE; - break; - case STATUS_FLOAT_STACK_CHECK: - OUTMSG2 (("STATUS_FLOAT_STACK_CHECK")); - ourstatus->value.sig = GDB_SIGNAL_FPE; - break; - case STATUS_FLOAT_UNDERFLOW: - OUTMSG2 (("STATUS_FLOAT_UNDERFLOW")); - ourstatus->value.sig = GDB_SIGNAL_FPE; - break; - case STATUS_FLOAT_DIVIDE_BY_ZERO: - OUTMSG2 (("STATUS_FLOAT_DIVIDE_BY_ZERO")); - ourstatus->value.sig = GDB_SIGNAL_FPE; - break; - case STATUS_INTEGER_DIVIDE_BY_ZERO: - OUTMSG2 (("STATUS_INTEGER_DIVIDE_BY_ZERO")); - ourstatus->value.sig = GDB_SIGNAL_FPE; - break; - case STATUS_INTEGER_OVERFLOW: - OUTMSG2 (("STATUS_INTEGER_OVERFLOW")); - ourstatus->value.sig = GDB_SIGNAL_FPE; - break; - case EXCEPTION_BREAKPOINT: - OUTMSG2 (("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: - OUTMSG2 (("DBG_CONTROL_C")); - ourstatus->value.sig = GDB_SIGNAL_INT; - break; - case DBG_CONTROL_BREAK: - OUTMSG2 (("DBG_CONTROL_BREAK")); - ourstatus->value.sig = GDB_SIGNAL_INT; - break; - case EXCEPTION_SINGLE_STEP: - OUTMSG2 (("EXCEPTION_SINGLE_STEP")); - ourstatus->value.sig = GDB_SIGNAL_TRAP; - break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - OUTMSG2 (("EXCEPTION_ILLEGAL_INSTRUCTION")); - ourstatus->value.sig = GDB_SIGNAL_ILL; - break; - case EXCEPTION_PRIV_INSTRUCTION: - OUTMSG2 (("EXCEPTION_PRIV_INSTRUCTION")); - ourstatus->value.sig = GDB_SIGNAL_ILL; - break; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - OUTMSG2 (("EXCEPTION_NONCONTINUABLE_EXCEPTION")); - ourstatus->value.sig = GDB_SIGNAL_ILL; - break; - default: - if (current_event.u.Exception.dwFirstChance) - { - ourstatus->kind = TARGET_WAITKIND_SPURIOUS; - return; - } - OUTMSG2 (("gdbserver: unknown target exception 0x%08x at 0x%s", - (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionCode, - phex_nz ((uintptr_t) current_event.u.Exception.ExceptionRecord. - ExceptionAddress, sizeof (uintptr_t)))); - ourstatus->value.sig = GDB_SIGNAL_UNKNOWN; - break; - } - OUTMSG2 (("\n")); - last_sig = ourstatus->value.sig; -} - - static void suspend_one_thread (thread_info *thread) { - win32_thread_info *th = (win32_thread_info *) thread_target_data (thread); + windows_thread_info *th = (windows_thread_info *) thread_target_data (thread); - if (!th->suspended) - { - if (SuspendThread (th->h) == (DWORD) -1) - { - DWORD err = GetLastError (); - OUTMSG (("warning: SuspendThread failed in suspend_one_thread, " - "(error %d): %s\n", (int) err, strwinerror (err))); - } - else - th->suspended = 1; - } + th->suspend (); } static void @@ -1388,15 +1355,61 @@ auto_delete_breakpoint (CORE_ADDR stop_pc) } #endif +/* See nat/windows-nat.h. */ + +bool +windows_nat::handle_ms_vc_exception (const EXCEPTION_RECORD *rec) +{ + return false; +} + +/* See nat/windows-nat.h. */ + +bool +windows_nat::handle_access_violation (const EXCEPTION_RECORD *rec) +{ + return false; +} + +/* A helper function that will, if needed, set + 'stopped_at_software_breakpoint' on the thread and adjust the + PC. */ + +static void +maybe_adjust_pc () +{ + struct regcache *regcache = get_thread_regcache (current_thread, 1); + child_fetch_inferior_registers (regcache, -1); + + windows_thread_info *th = thread_rec (current_thread_ptid (), + DONT_INVALIDATE_CONTEXT); + th->stopped_at_software_breakpoint = false; + + if (current_event.dwDebugEventCode == EXCEPTION_DEBUG_EVENT + && ((current_event.u.Exception.ExceptionRecord.ExceptionCode + == EXCEPTION_BREAKPOINT) + || (current_event.u.Exception.ExceptionRecord.ExceptionCode + == STATUS_WX86_BREAKPOINT)) + && child_initialization_done) + { + th->stopped_at_software_breakpoint = true; + CORE_ADDR pc = regcache_read_pc (regcache); + CORE_ADDR sw_breakpoint_pc = pc - the_low_target.decr_pc_after_break; + regcache_write_pc (regcache, sw_breakpoint_pc); + } +} + /* Get the next event from the child. */ static int -get_child_debug_event (struct target_waitstatus *ourstatus) +get_child_debug_event (DWORD *continue_status, + struct target_waitstatus *ourstatus) { ptid_t ptid; last_sig = GDB_SIGNAL_0; ourstatus->kind = TARGET_WAITKIND_SPURIOUS; + *continue_status = DBG_CONTINUE; /* Check if GDB sent us an interrupt request. */ check_remote_input_interrupt_request (); @@ -1428,7 +1441,7 @@ get_child_debug_event (struct target_waitstatus *ourstatus) happen is the user will see a spurious breakpoint. */ current_event.dwDebugEventCode = 0; - if (!WaitForDebugEvent (¤t_event, 0)) + if (!wait_for_debug_event (¤t_event, 0)) { OUTMSG2(("no attach events left\n")); fake_breakpoint_event (); @@ -1440,10 +1453,20 @@ get_child_debug_event (struct target_waitstatus *ourstatus) else #endif { + gdb::optional stop = fetch_pending_stop (debug_threads); + if (stop.has_value ()) + { + *ourstatus = stop->status; + current_event = stop->event; + ptid = debug_event_ptid (¤t_event); + current_thread = find_thread_ptid (ptid); + return 1; + } + /* Keep the wait time low enough for comfortable remote interruption, but high enough so gdbserver doesn't become a bottleneck. */ - if (!WaitForDebugEvent (¤t_event, 250)) + if (!wait_for_debug_event (¤t_event, 250)) { DWORD e = GetLastError(); @@ -1497,6 +1520,12 @@ get_child_debug_event (struct target_waitstatus *ourstatus) (unsigned) current_event.dwThreadId)); CloseHandle (current_event.u.CreateProcessInfo.hFile); + if (open_process_used) + { + CloseHandle (current_process_handle); + open_process_used = false; + } + current_process_handle = current_event.u.CreateProcessInfo.hProcess; main_thread_id = current_event.dwThreadId; @@ -1543,9 +1572,7 @@ get_child_debug_event (struct target_waitstatus *ourstatus) ourstatus->value.sig = gdb_signal_from_host (exit_signal); } } - child_continue (DBG_CONTINUE, -1); - CloseHandle (current_process_handle); - current_process_handle = NULL; + child_continue (DBG_CONTINUE, desired_stop_thread_id); break; case LOAD_DLL_DEBUG_EVENT: @@ -1579,7 +1606,9 @@ get_child_debug_event (struct target_waitstatus *ourstatus) "for pid=%u tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); - handle_exception (ourstatus); + if (handle_exception (ourstatus, debug_threads) + == HANDLE_EXCEPTION_UNHANDLED) + *continue_status = DBG_EXCEPTION_NOT_HANDLED; break; case OUTPUT_DEBUG_STRING_EVENT: @@ -1588,7 +1617,7 @@ get_child_debug_event (struct target_waitstatus *ourstatus) "for pid=%u tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); - handle_output_debug_string (); + handle_output_debug_string (nullptr); break; default: @@ -1601,7 +1630,21 @@ get_child_debug_event (struct target_waitstatus *ourstatus) } ptid = debug_event_ptid (¤t_event); - current_thread = find_thread_ptid (ptid); + + if (desired_stop_thread_id != -1 && desired_stop_thread_id != ptid.lwp ()) + { + /* Pending stop. See the comment by the definition of + "pending_stops" for details on why this is needed. */ + OUTMSG2 (("get_windows_debug_event - " + "unexpected stop in 0x%lx (expecting 0x%x)\n", + ptid.lwp (), desired_stop_thread_id)); + maybe_adjust_pc (); + pending_stops.push_back ({(DWORD) ptid.lwp (), *ourstatus, current_event}); + ourstatus->kind = TARGET_WAITKIND_SPURIOUS; + } + else + current_thread = find_thread_ptid (ptid); + return 1; } @@ -1612,8 +1655,6 @@ ptid_t win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus, int options) { - struct regcache *regcache; - if (cached_status.kind != TARGET_WAITKIND_IGNORE) { /* The core always does a wait after creating the inferior, and @@ -1627,7 +1668,8 @@ win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus, while (1) { - if (!get_child_debug_event (ourstatus)) + DWORD continue_status; + if (!get_child_debug_event (&continue_status, ourstatus)) continue; switch (ourstatus->kind) @@ -1640,18 +1682,18 @@ win32_process_target::wait (ptid_t ptid, target_waitstatus *ourstatus, case TARGET_WAITKIND_STOPPED: case TARGET_WAITKIND_SIGNALLED: case TARGET_WAITKIND_LOADED: - OUTMSG2 (("Child Stopped with signal = %d \n", - ourstatus->value.sig)); - - regcache = get_thread_regcache (current_thread, 1); - child_fetch_inferior_registers (regcache, -1); - return debug_event_ptid (¤t_event); + { + OUTMSG2 (("Child Stopped with signal = %d \n", + ourstatus->value.sig)); + maybe_adjust_pc (); + return debug_event_ptid (¤t_event); + } default: OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind)); /* fall-through */ case TARGET_WAITKIND_SPURIOUS: /* do nothing, just continue */ - child_continue (DBG_CONTINUE, -1); + child_continue (continue_status, desired_stop_thread_id); break; } } @@ -1676,8 +1718,9 @@ win32_process_target::store_registers (regcache *regcache, int regno) /* Read memory from the inferior process. This should generally be called through read_inferior_memory, which handles breakpoint shadowing. Read LEN bytes at MEMADDR into a buffer at MYADDR. */ -static int -win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +int +win32_process_target::read_memory (CORE_ADDR memaddr, unsigned char *myaddr, + int len) { return child_xfer_memory (memaddr, (char *) myaddr, len, 0, 0) != len; } @@ -1686,16 +1729,16 @@ win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) called through write_inferior_memory, which handles breakpoint shadowing. Write LEN bytes from the buffer at MYADDR to MEMADDR. Returns 0 on success and errno on failure. */ -static int -win32_write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, - int len) +int +win32_process_target::write_memory (CORE_ADDR memaddr, + const unsigned char *myaddr, int len) { return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len; } /* Send an interrupt request to the inferior process. */ -static void -win32_request_interrupt (void) +void +win32_process_target::request_interrupt () { winapi_DebugBreakProcess DebugBreakProcess; winapi_GenerateConsoleCtrlEvent GenerateConsoleCtrlEvent; @@ -1727,6 +1770,12 @@ win32_request_interrupt (void) soft_interrupt_requested = 1; } +bool +win32_process_target::supports_hardware_single_step () +{ + return true; +} + #ifdef _WIN32_WCE int win32_error_to_fileio_error (DWORD err) @@ -1777,8 +1826,8 @@ win32_error_to_fileio_error (DWORD err) return FILEIO_EUNKNOWN; } -static void -wince_hostio_last_error (char *buf) +void +win32_process_target::hostio_last_error (char *buf) { DWORD winerr = GetLastError (); int fileio_err = win32_error_to_fileio_error (winerr); @@ -1786,11 +1835,19 @@ wince_hostio_last_error (char *buf) } #endif +bool +win32_process_target::supports_qxfer_siginfo () +{ + return true; +} + /* Write Windows signal info. */ -static int -win32_xfer_siginfo (const char *annex, unsigned char *readbuf, - unsigned const char *writebuf, CORE_ADDR offset, int len) +int +win32_process_target::qxfer_siginfo (const char *annex, + unsigned char *readbuf, + unsigned const char *writebuf, + CORE_ADDR offset, int len) { if (siginfo_er.ExceptionCode == 0) return -1; @@ -1798,24 +1855,51 @@ win32_xfer_siginfo (const char *annex, unsigned char *readbuf, if (readbuf == nullptr) return -1; - if (offset > sizeof (siginfo_er)) + 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 (offset > bufsize) return -1; - if (offset + len > sizeof (siginfo_er)) - len = sizeof (siginfo_er) - offset; + if (offset + len > bufsize) + len = bufsize - offset; - memcpy (readbuf, (char *) &siginfo_er + offset, len); + memcpy (readbuf, buf + offset, len); return len; } +bool +win32_process_target::supports_get_tib_address () +{ + return true; +} + /* Write Windows OS Thread Information Block address. */ -static int -win32_get_tib_address (ptid_t ptid, CORE_ADDR *addr) +int +win32_process_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr) { - win32_thread_info *th; - th = thread_rec (ptid, 0); + windows_thread_info *th; + th = thread_rec (ptid, DONT_INVALIDATE_CONTEXT); if (th == NULL) return 0; if (addr != NULL) @@ -1825,94 +1909,55 @@ win32_get_tib_address (ptid_t ptid, CORE_ADDR *addr) /* Implementation of the target_ops method "sw_breakpoint_from_kind". */ -static const gdb_byte * -win32_sw_breakpoint_from_kind (int kind, int *size) +const gdb_byte * +win32_process_target::sw_breakpoint_from_kind (int kind, int *size) { *size = the_low_target.breakpoint_len; return the_low_target.breakpoint; } +bool +win32_process_target::stopped_by_sw_breakpoint () +{ + windows_thread_info *th = thread_rec (current_thread_ptid (), + DONT_INVALIDATE_CONTEXT); + return th == nullptr ? false : th->stopped_at_software_breakpoint; +} + +bool +win32_process_target::supports_stopped_by_sw_breakpoint () +{ + return true; +} + +CORE_ADDR +win32_process_target::read_pc (struct regcache *regcache) +{ + return (*the_low_target.get_pc) (regcache); +} + +void +win32_process_target::write_pc (struct regcache *regcache, CORE_ADDR pc) +{ + return (*the_low_target.set_pc) (regcache, pc); +} + /* The win32 target ops object. */ static win32_process_target the_win32_target; -static process_stratum_target win32_target_ops = { - NULL, /* prepare_to_access_memory */ - NULL, /* done_accessing_memory */ - win32_read_inferior_memory, - win32_write_inferior_memory, - NULL, /* lookup_symbols */ - win32_request_interrupt, - NULL, /* read_auxv */ - win32_supports_z_point_type, - win32_insert_point, - win32_remove_point, - NULL, /* stopped_by_sw_breakpoint */ - NULL, /* supports_stopped_by_sw_breakpoint */ - NULL, /* stopped_by_hw_breakpoint */ - NULL, /* supports_stopped_by_hw_breakpoint */ - target_can_do_hardware_single_step, - win32_stopped_by_watchpoint, - win32_stopped_data_address, - NULL, /* read_offsets */ - NULL, /* get_tls_address */ -#ifdef _WIN32_WCE - wince_hostio_last_error, -#else - hostio_last_error_from_errno, -#endif - NULL, /* qxfer_osdata */ - win32_xfer_siginfo, - NULL, /* supports_non_stop */ - NULL, /* async */ - NULL, /* start_non_stop */ - NULL, /* supports_multi_process */ - NULL, /* supports_fork_events */ - NULL, /* supports_vfork_events */ - NULL, /* supports_exec_events */ - NULL, /* handle_new_gdb_connection */ - NULL, /* handle_monitor_command */ - NULL, /* core_of_thread */ - NULL, /* read_loadmap */ - NULL, /* process_qsupported */ - NULL, /* supports_tracepoints */ - NULL, /* read_pc */ - NULL, /* write_pc */ - NULL, /* thread_stopped */ - win32_get_tib_address, - NULL, /* pause_all */ - NULL, /* unpause_all */ - NULL, /* stabilize_threads */ - NULL, /* install_fast_tracepoint_jump_pad */ - NULL, /* emit_ops */ - NULL, /* supports_disable_randomization */ - NULL, /* get_min_fast_tracepoint_insn_len */ - NULL, /* qxfer_libraries_svr4 */ - NULL, /* support_agent */ - NULL, /* enable_btrace */ - NULL, /* disable_btrace */ - NULL, /* read_btrace */ - NULL, /* read_btrace_conf */ - NULL, /* supports_range_stepping */ - NULL, /* pid_to_exec_file */ - NULL, /* multifs_open */ - NULL, /* multifs_unlink */ - NULL, /* multifs_readlink */ - NULL, /* breakpoint_kind_from_pc */ - win32_sw_breakpoint_from_kind, - NULL, /* thread_name */ - NULL, /* breakpoint_kind_from_current_state */ - NULL, /* supports_software_single_step */ - NULL, /* supports_catch_syscall */ - NULL, /* get_ipa_tdesc_idx */ - NULL, /* thread_handle */ - &the_win32_target, -}; - /* Initialize the Win32 backend. */ void initialize_low (void) { - set_target_ops (&win32_target_ops); + set_target_ops (&the_win32_target); the_low_target.arch_setup (); + +#ifdef __x86_64__ + /* These functions are loaded dynamically, because they are not available + on Windows XP. */ + HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL")); + win32_Wow64GetThreadContext = GETPROCADDRESS (dll, Wow64GetThreadContext); + win32_Wow64SetThreadContext = GETPROCADDRESS (dll, Wow64SetThreadContext); +#endif }