X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdbserver%2Fwin32-low.cc;h=a11cc740925a13edb7c2e689998cd215fc32abfa;hb=9a17a136439c200b13a69d453f38824b7edc522c;hp=8f8b6cedd28bf35917e05bb96f702dd9ad0cbda1;hpb=4834dad062658ef49ef86c9c48eb004c48a242a5;p=deliverable%2Fbinutils-gdb.git diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc index 8f8b6cedd2..a11cc74092 100644 --- a/gdbserver/win32-low.cc +++ b/gdbserver/win32-low.cc @@ -74,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 @@ -96,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 @@ -129,7 +139,12 @@ debug_event_ptid (DEBUG_EVENT *event) static void 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)); @@ -154,7 +169,14 @@ win32_set_thread_context (windows_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. */ @@ -171,24 +193,31 @@ win32_prepare_to_resume (windows_thread_info *th) void 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) { 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 windows_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; windows_thread_info *th = (windows_thread_info *) thread_target_data (thread); - if (get_context) + if (disposition != DONT_INVALIDATE_CONTEXT) win32_require_context (th); return th; } @@ -200,10 +229,17 @@ child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb) 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 = new windows_thread_info (tid, h, (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); @@ -244,15 +280,18 @@ child_delete_thread (DWORD pid, DWORD tid) 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))); } 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). */ @@ -263,7 +302,9 @@ 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). */ @@ -345,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; @@ -419,10 +484,17 @@ 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; } th->resume (); @@ -433,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) @@ -441,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. */ @@ -454,7 +525,8 @@ static void child_fetch_inferior_registers (struct regcache *regcache, int r) { int regno; - windows_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 @@ -468,7 +540,8 @@ static void child_store_inferior_registers (struct regcache *regcache, int r) { int regno; - windows_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 @@ -624,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. */ @@ -739,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; @@ -749,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; @@ -762,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")) @@ -776,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; @@ -805,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 (); @@ -937,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. */ @@ -958,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; } } @@ -1029,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; @@ -1103,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); @@ -1132,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; @@ -1144,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; @@ -1166,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 @@ -1175,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. - - 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). */ +/* See nat/windows-nat.h. */ -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); @@ -1196,15 +1308,10 @@ handle_load_dll (void) win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) event->lpBaseOfDll); } -/* Handle a DLL unload 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_one_solib for more info - on how we handle DLL loading during that phase). */ - -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; @@ -1216,117 +1323,6 @@ 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) { @@ -1359,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 (); @@ -1379,59 +1421,39 @@ get_child_debug_event (struct target_waitstatus *ourstatus) goto gotevent; } -#ifndef _WIN32_WCE attaching = 0; -#else - if (attaching) - { - /* WinCE doesn't set an initial breakpoint automatically. To - stop the inferior, we flush all currently pending debug - events -- the thread list and the dll list are always - reported immediatelly without delay, then, we suspend all - threads and pretend we saw a trap at the current PC of the - main thread. - - Contrary to desktop Windows, Windows CE *does* report the dll - names on LOAD_DLL_DEBUG_EVENTs resulting from a - DebugActiveProcess call. This limits the way we can detect - if all the dlls have already been reported. If we get a real - debug event before leaving attaching, the worst that will - happen is the user will see a spurious breakpoint. */ - - current_event.dwDebugEventCode = 0; - if (!WaitForDebugEvent (¤t_event, 0)) - { - OUTMSG2(("no attach events left\n")); - fake_breakpoint_event (); - attaching = 0; - } - else - OUTMSG2(("got attach event\n")); - } - else -#endif - { - /* 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)) - { - DWORD e = GetLastError(); - - if (e == ERROR_PIPE_NOT_CONNECTED) - { - /* This will happen if the loader fails to succesfully - load the application, e.g., if the main executable - tries to pull in a non-existing export from a - DLL. */ - ourstatus->kind = TARGET_WAITKIND_EXITED; - ourstatus->value.integer = 1; - return 1; - } + { + 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; + } - return 0; - } - } + /* Keep the wait time low enough for comfortable remote + interruption, but high enough so gdbserver doesn't become a + bottleneck. */ + if (!wait_for_debug_event (¤t_event, 250)) + { + DWORD e = GetLastError(); + + if (e == ERROR_PIPE_NOT_CONNECTED) + { + /* This will happen if the loader fails to succesfully + load the application, e.g., if the main executable + tries to pull in a non-existing export from a + DLL. */ + ourstatus->kind = TARGET_WAITKIND_EXITED; + ourstatus->value.integer = 1; + return 1; + } + + return 0; + } + } gotevent: @@ -1468,6 +1490,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; @@ -1476,19 +1504,6 @@ get_child_debug_event (struct target_waitstatus *ourstatus) main_thread_id, current_event.u.CreateProcessInfo.hThread, current_event.u.CreateProcessInfo.lpThreadLocalBase); - -#ifdef _WIN32_WCE - if (!attaching) - { - /* Windows CE doesn't set the initial breakpoint - automatically like the desktop versions of Windows do. - We add it explicitly here. It will be removed as soon as - it is hit. */ - set_breakpoint_at ((CORE_ADDR) (long) current_event.u - .CreateProcessInfo.lpStartAddress, - auto_delete_breakpoint); - } -#endif break; case EXIT_PROCESS_DEBUG_EVENT: @@ -1514,9 +1529,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: @@ -1550,7 +1563,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: @@ -1559,7 +1574,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: @@ -1572,7 +1587,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; } @@ -1583,8 +1612,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 @@ -1598,7 +1625,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) @@ -1611,18 +1639,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; } } @@ -1784,13 +1812,34 @@ win32_process_target::qxfer_siginfo (const char *annex, 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; } @@ -1807,7 +1856,7 @@ int win32_process_target::get_tib_address (ptid_t ptid, CORE_ADDR *addr) { windows_thread_info *th; - th = thread_rec (ptid, 0); + th = thread_rec (ptid, DONT_INVALIDATE_CONTEXT); if (th == NULL) return 0; if (addr != NULL) @@ -1824,6 +1873,32 @@ win32_process_target::sw_breakpoint_from_kind (int kind, int *size) 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; @@ -1834,4 +1909,12 @@ initialize_low (void) { 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 }