X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Fwin32-low.c;h=f64673849cbac0004cf2b15ccaa0ab5313d3cf35;hb=90d74c301f749fc06e859ab4424fd4cabc019bc2;hp=1d3c61766d62ca9b0a29cf31277c0b3030eec1f6;hpb=aec18585f04e9410cc3c965b1e33e8b5b056e018;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c index 1d3c61766d..f64673849c 100644 --- a/gdb/gdbserver/win32-low.c +++ b/gdb/gdbserver/win32-low.c @@ -1,5 +1,5 @@ /* Low level interface to Windows debugging, for gdbserver. - Copyright (C) 2006, 2007 Free Software Foundation, Inc. + Copyright (C) 2006, 2007, 2008, 2009, 2010 Free Software Foundation, Inc. Contributed by Leo Zayas. Based on "win32-nat.c" from GDB. @@ -21,6 +21,7 @@ #include "server.h" #include "regcache.h" #include "gdb/signals.h" +#include "gdb/fileio.h" #include "mem-break.h" #include "win32-low.h" @@ -37,14 +38,17 @@ #include #endif -#define LOG 0 +#define OUTMSG(X) do { printf X; fflush (stderr); } while (0) -#define OUTMSG(X) do { printf X; fflush (stdout); } while (0) -#if LOG -#define OUTMSG2(X) do { printf X; fflush (stdout); } while (0) -#else -#define OUTMSG2(X) do ; while (0) -#endif +#define OUTMSG2(X) \ + do \ + { \ + if (debug_threads) \ + { \ + printf X; \ + fflush (stderr); \ + } \ + } while (0) #ifndef _T #define _T(x) TEXT (x) @@ -65,13 +69,23 @@ 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 enum target_signal last_sig = TARGET_SIGNAL_0; /* The current debug event from WaitForDebugEvent. */ static DEBUG_EVENT current_event; +/* Non zero if an interrupt request is to be satisfied by suspending + all threads. */ +static int soft_interrupt_requested = 0; + +/* Non zero if the inferior is stopped in a simulated breakpoint done + by suspending all the threads. */ +static int faked_breakpoint = 0; + #define NUM_REGS (the_low_target.num_regs) typedef BOOL WINAPI (*winapi_DebugActiveProcessStop) (DWORD dwProcessId); @@ -79,38 +93,84 @@ typedef BOOL WINAPI (*winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit); typedef BOOL WINAPI (*winapi_DebugBreakProcess) (HANDLE); typedef BOOL WINAPI (*winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD); -static DWORD main_thread_id = 0; - -static void win32_resume (struct thread_resume *resume_info); +static void win32_resume (struct thread_resume *resume_info, size_t n); /* Get the thread ID from the current selected inferior (the current thread). */ -static DWORD -current_inferior_tid (void) +static ptid_t +current_inferior_ptid (void) +{ + return ((struct inferior_list_entry*) current_inferior)->id; +} + +/* The current debug event from WaitForDebugEvent. */ +static ptid_t +debug_event_ptid (DEBUG_EVENT *event) +{ + return ptid_build (event->dwProcessId, event->dwThreadId, 0); +} + +/* Get the thread context of the thread associated with TH. */ + +static void +win32_get_thread_context (win32_thread_info *th) { - win32_thread_info *th = inferior_target_data (current_inferior); - return th->tid; + memset (&th->context, 0, sizeof (CONTEXT)); + (*the_low_target.get_thread_context) (th, ¤t_event); +#ifdef _WIN32_WCE + memcpy (&th->base_context, &th->context, sizeof (CONTEXT)); +#endif +} + +/* Set the thread context of the thread associated with TH. */ + +static void +win32_set_thread_context (win32_thread_info *th) +{ +#ifdef _WIN32_WCE + /* Calling SuspendThread on a thread that is running kernel code + will report that the suspending was successful, but in fact, that + will often not be true. In those cases, the context returned by + GetThreadContext will not be correct by the time the thread + stops, hence we can't set that context back into the thread when + resuming - it will most likelly crash the inferior. + Unfortunately, there is no way to know when the thread will + really stop. To work around it, we'll only write the context + back to the thread when either the user or GDB explicitly change + it between stopping and resuming. */ + if (memcmp (&th->context, &th->base_context, sizeof (CONTEXT)) != 0) +#endif + (*the_low_target.set_thread_context) (th, ¤t_event); } /* 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 (DWORD id, int get_context) +thread_rec (ptid_t ptid, int get_context) { struct thread_info *thread; win32_thread_info *th; - thread = (struct thread_info *) find_inferior_id (&all_threads, id); + thread = (struct thread_info *) find_inferior_id (&all_threads, ptid); if (thread == NULL) return NULL; th = inferior_target_data (thread); - if (!th->suspend_count && get_context) + if (get_context && th->context.ContextFlags == 0) { - if (id != current_event.dwThreadId) - th->suspend_count = SuspendThread (th->h) + 1; + 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; + } - (*the_low_target.get_thread_context) (th, ¤t_event); + win32_get_thread_context (th); } return th; @@ -118,21 +178,22 @@ thread_rec (DWORD id, int get_context) /* Add a thread to the thread list. */ static win32_thread_info * -child_add_thread (DWORD tid, HANDLE h) +child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb) { win32_thread_info *th; + ptid_t ptid = ptid_build (pid, tid, 0); - if ((th = thread_rec (tid, FALSE))) + if ((th = thread_rec (ptid, FALSE))) return th; - th = (win32_thread_info *) malloc (sizeof (*th)); - memset (th, 0, sizeof (*th)); + th = xcalloc (1, sizeof (*th)); th->tid = tid; th->h = h; + th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb; - add_thread (tid, th, (unsigned int) tid); + add_thread (ptid, th); set_inferior_regcache_data ((struct thread_info *) - find_inferior_id (&all_threads, tid), + find_inferior_id (&all_threads, ptid), new_register_cache ()); if (the_low_target.thread_added != NULL) @@ -154,28 +215,72 @@ delete_thread_info (struct inferior_list_entry *thread) /* Delete a thread from the list of threads. */ static void -child_delete_thread (DWORD id) +child_delete_thread (DWORD pid, DWORD tid) { struct inferior_list_entry *thread; + ptid_t ptid; /* If the last thread is exiting, just return. */ if (all_threads.head == all_threads.tail) return; - thread = find_inferior_id (&all_threads, id); + ptid = ptid_build (pid, tid, 0); + thread = find_inferior_id (&all_threads, ptid); if (thread == NULL) return; delete_thread_info (thread); } +/* These watchpoint related wrapper functions simply pass on the function call + if the low target has registered a corresponding function. */ + +static int +win32_insert_point (char type, CORE_ADDR addr, int len) +{ + if (the_low_target.insert_point != NULL) + return the_low_target.insert_point (type, addr, len); + else + /* Unsupported (see target.h). */ + return 1; +} + +static int +win32_remove_point (char type, CORE_ADDR addr, int len) +{ + if (the_low_target.remove_point != NULL) + return the_low_target.remove_point (type, addr, len); + else + /* Unsupported (see target.h). */ + return 1; +} + +static int +win32_stopped_by_watchpoint (void) +{ + if (the_low_target.stopped_by_watchpoint != NULL) + return the_low_target.stopped_by_watchpoint (); + else + return 0; +} + +static CORE_ADDR +win32_stopped_data_address (void) +{ + if (the_low_target.stopped_data_address != NULL) + return the_low_target.stopped_data_address (); + else + return 0; +} + + /* Transfer memory from/to the debugged process. */ static int child_xfer_memory (CORE_ADDR memaddr, char *our, int len, int write, struct target_ops *target) { SIZE_T done; - long addr = (long) memaddr; + uintptr_t addr = (uintptr_t) memaddr; if (write) { @@ -191,45 +296,6 @@ child_xfer_memory (CORE_ADDR memaddr, char *our, int len, return done; } -/* Generally, what has the program done? */ -enum target_waitkind -{ - /* The program has exited. The exit status is in value.integer. */ - TARGET_WAITKIND_EXITED, - - /* The program has stopped with a signal. Which signal is in - value.sig. */ - TARGET_WAITKIND_STOPPED, - - /* The program is letting us know that it dynamically loaded - or unloaded something. */ - TARGET_WAITKIND_LOADED, - - /* The program has exec'ed a new executable file. The new file's - pathname is pointed to by value.execd_pathname. */ - TARGET_WAITKIND_EXECD, - - /* Nothing interesting happened, but we stopped anyway. We take the - chance to check if GDB requested an interrupt. */ - TARGET_WAITKIND_SPURIOUS, -}; - -struct target_waitstatus -{ - enum target_waitkind kind; - - /* Forked child pid, execd pathname, exit status or signal number. */ - union - { - int integer; - enum target_signal sig; - int related_pid; - char *execd_pathname; - int syscall_id; - } - value; -}; - /* Clear out any old thread list and reinitialize it to a pristine state. */ static void @@ -239,12 +305,20 @@ child_init_thread_list (void) } static void -do_initial_child_stuff (DWORD pid) +do_initial_child_stuff (HANDLE proch, DWORD pid, int attached) { last_sig = TARGET_SIGNAL_0; + current_process_handle = proch; + current_process_id = pid; + main_thread_id = 0; + + soft_interrupt_requested = 0; + faked_breakpoint = 0; + memset (¤t_event, 0, sizeof (current_event)); + add_process (pid, attached); child_init_thread_list (); if (the_low_target.initial_stuff != NULL) @@ -259,20 +333,23 @@ continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr) struct thread_info *thread = (struct thread_info *) this_thread; int thread_id = * (int *) id_ptr; win32_thread_info *th = inferior_target_data (thread); - int i; if ((thread_id == -1 || thread_id == th->tid) - && th->suspend_count) + && th->suspended) { if (th->context.ContextFlags) { - (*the_low_target.set_thread_context) (th, ¤t_event); + win32_set_thread_context (th); th->context.ContextFlags = 0; } - for (i = 0; i < th->suspend_count; i++) - (void) ResumeThread (th->h); - th->suspend_count = 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; } return 0; @@ -281,41 +358,44 @@ continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr) static BOOL child_continue (DWORD continue_status, int thread_id) { - BOOL res; + /* The inferior will only continue after the ContinueDebugEvent + call. */ + find_inferior (&all_threads, continue_one_thread, &thread_id); + faked_breakpoint = 0; - res = ContinueDebugEvent (current_event.dwProcessId, - current_event.dwThreadId, continue_status); - if (res) - find_inferior (&all_threads, continue_one_thread, &thread_id); + if (!ContinueDebugEvent (current_event.dwProcessId, + current_event.dwThreadId, + continue_status)) + return FALSE; - return res; + return TRUE; } /* Fetch register(s) from the current thread context. */ static void -child_fetch_inferior_registers (int r) +child_fetch_inferior_registers (struct regcache *regcache, int r) { int regno; - win32_thread_info *th = thread_rec (current_inferior_tid (), TRUE); - if (r == -1 || r == 0 || r > NUM_REGS) - child_fetch_inferior_registers (NUM_REGS); + win32_thread_info *th = thread_rec (current_inferior_ptid (), TRUE); + if (r == -1 || r > NUM_REGS) + child_fetch_inferior_registers (regcache, NUM_REGS); else for (regno = 0; regno < r; regno++) - (*the_low_target.fetch_inferior_register) (th, regno); + (*the_low_target.fetch_inferior_register) (regcache, th, regno); } /* Store a new register value into the current thread context. We don't change the program's context until later, when we resume it. */ static void -child_store_inferior_registers (int r) +child_store_inferior_registers (struct regcache *regcache, int r) { int regno; - win32_thread_info *th = thread_rec (current_inferior_tid (), TRUE); + win32_thread_info *th = thread_rec (current_inferior_ptid (), TRUE); if (r == -1 || r == 0 || r > NUM_REGS) - child_store_inferior_registers (NUM_REGS); + child_store_inferior_registers (regcache, NUM_REGS); else for (regno = 0; regno < r; regno++) - (*the_low_target.store_inferior_register) (th, regno); + (*the_low_target.store_inferior_register) (regcache, th, regno); } /* Map the Windows error number in ERROR to a locale-dependent error @@ -395,15 +475,15 @@ create_process (const char *program, char *args, mbstowcs (wargs, args, argslen + 1); ret = CreateProcessW (wprogram, /* image name */ - wargs, /* command line */ - NULL, /* security, not supported */ - NULL, /* thread, not supported */ - FALSE, /* inherit handles, not supported */ - flags, /* start flags */ - NULL, /* environment, not supported */ - NULL, /* current directory, not supported */ - NULL, /* start info, not supported */ - pi); /* proc info */ + wargs, /* command line */ + NULL, /* security, not supported */ + NULL, /* thread, not supported */ + FALSE, /* inherit handles, not supported */ + flags, /* start flags */ + NULL, /* environment, not supported */ + NULL, /* current directory, not supported */ + NULL, /* start info, not supported */ + pi); /* proc info */ #else STARTUPINFOA si = { sizeof (STARTUPINFOA) }; @@ -443,6 +523,9 @@ win32_create_inferior (char *program, char **program_args) PROCESS_INFORMATION pi; DWORD err; + /* win32_wait needs to know we're not attaching. */ + attaching = 0; + if (!program) error ("No executable specified, specify executable to debug.\n"); @@ -471,7 +554,7 @@ win32_create_inferior (char *program, char **program_args) for (argc = 1; program_args[argc]; argc++) { /* FIXME: Can we do better about quoting? How does Cygwin - handle this? */ + handle this? */ strcat (args, " "); strcat (args, program_args[argc]); } @@ -513,10 +596,7 @@ win32_create_inferior (char *program, char **program_args) CloseHandle (pi.hThread); #endif - current_process_handle = pi.hProcess; - current_process_id = pi.dwProcessId; - - do_initial_child_stuff (current_process_id); + do_initial_child_stuff (pi.hProcess, pi.dwProcessId, 0); return current_process_id; } @@ -527,34 +607,36 @@ win32_create_inferior (char *program, char **program_args) static int win32_attach (unsigned long pid) { - winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL; + HANDLE h; winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL; + DWORD err; #ifdef _WIN32_WCE HMODULE dll = GetModuleHandle (_T("COREDLL.DLL")); #else HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL")); #endif - DebugActiveProcessStop = GETPROCADDRESS (dll, DebugActiveProcessStop); DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit); - if (DebugActiveProcess (pid)) + h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); + if (h != NULL) { - if (DebugSetProcessKillOnExit != NULL) - DebugSetProcessKillOnExit (FALSE); - - current_process_handle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); - - if (current_process_handle != NULL) - { - current_process_id = pid; - do_initial_child_stuff (pid); - return 0; - } - if (DebugActiveProcessStop != NULL) - DebugActiveProcessStop (current_process_id); + if (DebugActiveProcess (pid)) + { + if (DebugSetProcessKillOnExit != NULL) + DebugSetProcessKillOnExit (FALSE); + + /* win32_wait needs to know we're attaching. */ + attaching = 1; + do_initial_child_stuff (h, pid, 1); + return 0; + } + + CloseHandle (h); } - error ("Attach to process failed."); + err = GetLastError (); + error ("Attach to process failed (error %d): %s\n", + (int) err, strwinerror (err)); } /* Handle OUTPUT_DEBUG_STRING_EVENT from child process. */ @@ -577,7 +659,7 @@ handle_output_debug_string (struct target_waitstatus *ourstatus) if (current_event.u.DebugString.fUnicode) { /* The event tells us how many bytes, not chars, even - in Unicode. */ + in Unicode. */ WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 }; if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0) return; @@ -602,14 +684,24 @@ handle_output_debug_string (struct target_waitstatus *ourstatus) #undef READ_BUFFER_LEN } -/* Kill all inferiors. */ static void -win32_kill (void) +win32_clear_inferiors (void) { - win32_thread_info *current_thread; + if (current_process_handle != NULL) + CloseHandle (current_process_handle); + + for_each_inferior (&all_threads, delete_thread_info); + clear_inferiors (); +} + +/* Kill all inferiors. */ +static int +win32_kill (int pid) +{ + struct process_info *process; if (current_process_handle == NULL) - return; + return -1; TerminateProcess (current_process_handle, 0); for (;;) @@ -622,27 +714,23 @@ win32_kill (void) break; else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) { - struct target_waitstatus our_status = { 0 }; + struct target_waitstatus our_status = { 0 }; handle_output_debug_string (&our_status); - } + } } - CloseHandle (current_process_handle); + win32_clear_inferiors (); - current_thread = inferior_target_data (current_inferior); - if (current_thread && current_thread->h) - { - /* This may fail in an attached process, so don't check. */ - (void) CloseHandle (current_thread->h); - } + process = find_process_pid (pid); + remove_process (process); + return 0; } -/* Detach from all inferiors. */ +/* Detach from inferior PID. */ static int -win32_detach (void) +win32_detach (int pid) { - HANDLE h; - + struct process_info *process; winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL; winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL; #ifdef _WIN32_WCE @@ -657,60 +745,52 @@ win32_detach (void) || DebugActiveProcessStop == NULL) return -1; - /* We need a new handle, since DebugActiveProcessStop - closes all the ones that came through the events. */ - if ((h = OpenProcess (PROCESS_ALL_ACCESS, - FALSE, - current_process_id)) == NULL) - { - /* The process died. */ - return -1; - } - { struct thread_resume resume; - resume.thread = -1; - resume.step = 0; + resume.thread = minus_one_ptid; + resume.kind = resume_continue; resume.sig = 0; - resume.leave_stopped = 0; - win32_resume (&resume); + win32_resume (&resume, 1); } if (!DebugActiveProcessStop (current_process_id)) - { - CloseHandle (h); - return -1; - } + return -1; + DebugSetProcessKillOnExit (FALSE); + process = find_process_pid (pid); + remove_process (process); - current_process_handle = h; + win32_clear_inferiors (); return 0; } -/* Wait for inferiors to end. */ static void -win32_join (void) +win32_mourn (struct process_info *process) { - if (current_process_id == 0 - || current_process_handle == NULL) - return; - - WaitForSingleObject (current_process_handle, INFINITE); - CloseHandle (current_process_handle); + remove_process (process); +} - current_process_handle = NULL; - current_process_id = 0; +/* Wait for inferiors to end. */ +static void +win32_join (int pid) +{ + HANDLE h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); + if (h != NULL) + { + WaitForSingleObject (h, INFINITE); + CloseHandle (h); + } } /* Return 1 iff the thread with thread ID TID is alive. */ static int -win32_thread_alive (unsigned long tid) +win32_thread_alive (ptid_t ptid) { int res; /* Our thread list is reliable; don't bother to poll target threads. */ - if (find_inferior_id (&all_threads, tid) != NULL) + if (find_inferior_id (&all_threads, ptid) != NULL) res = 1; else res = 0; @@ -720,30 +800,31 @@ win32_thread_alive (unsigned long tid) /* Resume the inferior process. RESUME_INFO describes how we want to resume. */ static void -win32_resume (struct thread_resume *resume_info) +win32_resume (struct thread_resume *resume_info, size_t n) { DWORD tid; enum target_signal sig; int step; win32_thread_info *th; DWORD continue_status = DBG_CONTINUE; + ptid_t ptid; /* This handles the very limited set of resume packets that GDB can currently produce. */ - if (resume_info[0].thread == -1) + if (n == 1 && ptid_equal (resume_info[0].thread, minus_one_ptid)) tid = -1; - else if (resume_info[1].thread == -1 && !resume_info[1].leave_stopped) + else if (n > 1) tid = -1; else /* Yes, we're ignoring resume_info[0].thread. It'd be tricky to make the Windows resume code do the right thing for thread switching. */ tid = current_event.dwThreadId; - if (resume_info[0].thread != -1) + if (!ptid_equal (resume_info[0].thread, minus_one_ptid)) { sig = resume_info[0].sig; - step = resume_info[0].step; + step = resume_info[0].kind == resume_step; } else { @@ -766,7 +847,8 @@ win32_resume (struct thread_resume *resume_info) last_sig = TARGET_SIGNAL_0; /* Get context for the currently selected thread. */ - th = thread_rec (current_event.dwThreadId, FALSE); + ptid = debug_event_ptid (¤t_event); + th = thread_rec (ptid, FALSE); if (th) { if (th->context.ContextFlags) @@ -784,7 +866,7 @@ win32_resume (struct thread_resume *resume_info) "in this configuration.\n"); } - (*the_low_target.set_thread_context) (th, ¤t_event); + win32_set_thread_context (th); th->context.ContextFlags = 0; } } @@ -834,6 +916,14 @@ win32_add_one_solib (const char *name, CORE_ADDR load_addr) #endif } +#ifndef _WIN32_WCE + if (strcasecmp (buf, "ntdll.dll") == 0) + { + GetSystemDirectoryA (buf, sizeof (buf)); + strcat (buf, "\\ntdll.dll"); + } +#endif + #ifdef __CYGWIN__ cygwin_conv_to_posix_path (buf, buf2); #else @@ -851,7 +941,7 @@ get_image_name (HANDLE h, void *address, int unicode) char *address_ptr; int len = 0; char b[2]; - DWORD done; + SIZE_T done; /* Attempt to read the name of the dll that was detected. This is documented to work only when actively debugging @@ -929,7 +1019,7 @@ load_psapi (void) } static int -psapi_get_dll_name (DWORD BaseAddress, char *dll_name_ret) +psapi_get_dll_name (LPVOID BaseAddress, char *dll_name_ret) { DWORD len; MODULEINFO mi; @@ -974,7 +1064,7 @@ psapi_get_dll_name (DWORD BaseAddress, char *dll_name_ret) (int) err, strwinerror (err)); } - if ((DWORD) (mi.lpBaseOfDll) == BaseAddress) + if (mi.lpBaseOfDll == BaseAddress) { len = (*win32_GetModuleFileNameExA) (current_process_handle, DllHandle[i], @@ -1044,7 +1134,7 @@ load_toolhelp (void) } static int -toolhelp_get_dll_name (DWORD BaseAddress, char *dll_name_ret) +toolhelp_get_dll_name (LPVOID BaseAddress, char *dll_name_ret) { HANDLE snapshot_module; MODULEENTRY32 modEntry = { sizeof (MODULEENTRY32) }; @@ -1061,7 +1151,7 @@ toolhelp_get_dll_name (DWORD BaseAddress, char *dll_name_ret) /* Ignore the first module, which is the exe. */ if (win32_Module32First (snapshot_module, &modEntry)) while (win32_Module32Next (snapshot_module, &modEntry)) - if ((DWORD) modEntry.modBaseAddr == BaseAddress) + if (modEntry.modBaseAddr == BaseAddress) { #ifdef UNICODE wcstombs (dll_name_ret, modEntry.szExePath, MAX_PATH + 1); @@ -1086,21 +1176,21 @@ handle_load_dll (void) LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll; char dll_buf[MAX_PATH + 1]; char *dll_name = NULL; - DWORD load_addr; + CORE_ADDR load_addr; dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0'; /* Windows does not report the image name of the dlls in the debug event on attaches. We resort to iterating over the list of loaded dlls looking for a match by image base. */ - if (!psapi_get_dll_name ((DWORD) event->lpBaseOfDll, dll_buf)) + if (!psapi_get_dll_name (event->lpBaseOfDll, dll_buf)) { if (!server_waiting) /* On some versions of Windows and Windows CE, we can't create toolhelp snapshots while the inferior is stopped in a LOAD_DLL_DEBUG_EVENT due to a dll load, but we can while Windows is reporting the already loaded dlls. */ - toolhelp_get_dll_name ((DWORD) event->lpBaseOfDll, dll_buf); + toolhelp_get_dll_name (event->lpBaseOfDll, dll_buf); } dll_name = dll_buf; @@ -1115,7 +1205,7 @@ handle_load_dll (void) the offset from 0 of the first byte in an image - because of the file header and the section alignment. */ - load_addr = (DWORD) event->lpBaseOfDll + 0x1000; + load_addr = (CORE_ADDR) (uintptr_t) event->lpBaseOfDll + 0x1000; win32_add_one_solib (dll_name, load_addr); } @@ -1123,7 +1213,7 @@ static void handle_unload_dll (void) { CORE_ADDR load_addr = - (CORE_ADDR) (DWORD) current_event.u.UnloadDll.lpBaseOfDll; + (CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll; load_addr += 0x1000; unloaded_dll (NULL, load_addr); } @@ -1191,7 +1281,7 @@ handle_exception (struct target_waitstatus *ourstatus) #ifdef _WIN32_WCE /* Remove the initial breakpoint. */ check_breakpoints ((CORE_ADDR) (long) current_event - .u.Exception.ExceptionRecord.ExceptionAddress); + .u.Exception.ExceptionRecord.ExceptionAddress); #endif break; case DBG_CONTROL_C: @@ -1224,10 +1314,10 @@ handle_exception (struct target_waitstatus *ourstatus) ourstatus->kind = TARGET_WAITKIND_SPURIOUS; return; } - OUTMSG2 (("gdbserver: unknown target exception 0x%08lx at 0x%08lx", + OUTMSG2 (("gdbserver: unknown target exception 0x%08lx at 0x%s", current_event.u.Exception.ExceptionRecord.ExceptionCode, - (DWORD) current_event.u.Exception.ExceptionRecord. - ExceptionAddress)); + phex_nz ((uintptr_t) current_event.u.Exception.ExceptionRecord. + ExceptionAddress, sizeof (uintptr_t)))); ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; break; } @@ -1235,23 +1325,125 @@ handle_exception (struct target_waitstatus *ourstatus) last_sig = ourstatus->value.sig; } -/* Get the next event from the child. */ + static void +suspend_one_thread (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + win32_thread_info *th = inferior_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; + } +} + +static void +fake_breakpoint_event (void) +{ + OUTMSG2(("fake_breakpoint_event\n")); + + faked_breakpoint = 1; + + memset (¤t_event, 0, sizeof (current_event)); + current_event.dwThreadId = main_thread_id; + current_event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT; + current_event.u.Exception.ExceptionRecord.ExceptionCode + = EXCEPTION_BREAKPOINT; + + for_each_inferior (&all_threads, suspend_one_thread); +} + +#ifdef _WIN32_WCE +static int +auto_delete_breakpoint (CORE_ADDR stop_pc) +{ + return 1; +} +#endif + +/* Get the next event from the child. */ + +static int get_child_debug_event (struct target_waitstatus *ourstatus) { - BOOL debug_event; + ptid_t ptid; last_sig = TARGET_SIGNAL_0; ourstatus->kind = TARGET_WAITKIND_SPURIOUS; - /* Keep the wait time low enough for confortable remote interruption, - but high enough so gdbserver doesn't become a bottleneck. */ - if (!(debug_event = WaitForDebugEvent (¤t_event, 250))) - return; + /* Check if GDB sent us an interrupt request. */ + check_remote_input_interrupt_request (); - current_inferior = - (struct thread_info *) find_inferior_id (&all_threads, - current_event.dwThreadId); + if (soft_interrupt_requested) + { + soft_interrupt_requested = 0; + fake_breakpoint_event (); + 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 confortable 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; + } + + return 0; + } + } + + gotevent: switch (current_event.dwDebugEventCode) { @@ -1262,8 +1454,10 @@ get_child_debug_event (struct target_waitstatus *ourstatus) (unsigned) current_event.dwThreadId)); /* Record the existence of this thread. */ - child_add_thread (current_event.dwThreadId, - current_event.u.CreateThread.hThread); + child_add_thread (current_event.dwProcessId, + current_event.dwThreadId, + current_event.u.CreateThread.hThread, + current_event.u.CreateThread.lpThreadLocalBase); break; case EXIT_THREAD_DEBUG_EVENT: @@ -1271,8 +1465,11 @@ get_child_debug_event (struct target_waitstatus *ourstatus) "for pid=%d tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); - child_delete_thread (current_event.dwThreadId); - break; + child_delete_thread (current_event.dwProcessId, + current_event.dwThreadId); + + current_inferior = (struct thread_info *) all_threads.head; + return 1; case CREATE_PROCESS_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT " @@ -1288,17 +1485,23 @@ get_child_debug_event (struct target_waitstatus *ourstatus) ourstatus->value.execd_pathname = "Main executable"; /* Add the main thread. */ - child_add_thread (main_thread_id, - current_event.u.CreateProcessInfo.hThread); + child_add_thread (current_event.dwProcessId, + main_thread_id, + current_event.u.CreateProcessInfo.hThread, + current_event.u.CreateProcessInfo.lpThreadLocalBase); - ourstatus->value.related_pid = current_event.dwThreadId; + ourstatus->value.related_pid = debug_event_ptid (¤t_event); #ifdef _WIN32_WCE - /* 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, - delete_breakpoint_at); + 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; @@ -1309,6 +1512,7 @@ get_child_debug_event (struct target_waitstatus *ourstatus) (unsigned) current_event.dwThreadId)); ourstatus->kind = TARGET_WAITKIND_EXITED; ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode; + child_continue (DBG_CONTINUE, -1); CloseHandle (current_process_handle); current_process_handle = NULL; break; @@ -1361,49 +1565,41 @@ get_child_debug_event (struct target_waitstatus *ourstatus) break; } + ptid = debug_event_ptid (¤t_event); current_inferior = - (struct thread_info *) find_inferior_id (&all_threads, - current_event.dwThreadId); + (struct thread_info *) find_inferior_id (&all_threads, ptid); + return 1; } /* Wait for the inferior process to change state. STATUS will be filled in with a response code to send to GDB. Returns the signal which caused the process to stop. */ -static unsigned char -win32_wait (char *status) +static ptid_t +win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options) { - struct target_waitstatus our_status; - - *status = 'T'; + struct regcache *regcache; while (1) { - /* Check if GDB sent us an interrupt request. */ - check_remote_input_interrupt_request (); - - get_child_debug_event (&our_status); + if (!get_child_debug_event (ourstatus)) + continue; - switch (our_status.kind) + switch (ourstatus->kind) { case TARGET_WAITKIND_EXITED: OUTMSG2 (("Child exited with retcode = %x\n", - our_status.value.integer)); - - *status = 'W'; - - child_fetch_inferior_registers (-1); - - return our_status.value.integer; + ourstatus->value.integer)); + win32_clear_inferiors (); + return pid_to_ptid (current_event.dwProcessId); case TARGET_WAITKIND_STOPPED: - case TARGET_WAITKIND_LOADED: + case TARGET_WAITKIND_LOADED: OUTMSG2 (("Child Stopped with signal = %d \n", - our_status.value.sig)); - - *status = 'T'; + ourstatus->value.sig)); - child_fetch_inferior_registers (-1); + regcache = get_thread_regcache (current_inferior, 1); + child_fetch_inferior_registers (regcache, -1); - if (our_status.kind == TARGET_WAITKIND_LOADED + if (ourstatus->kind == TARGET_WAITKIND_LOADED && !server_waiting) { /* When gdb connects, we want to be stopped at the @@ -1412,12 +1608,17 @@ win32_wait (char *status) break; } - return our_status.value.sig; - default: - OUTMSG (("Ignoring unknown internal event, %d\n", our_status.kind)); - /* fall-through */ - case TARGET_WAITKIND_SPURIOUS: - case TARGET_WAITKIND_EXECD: + /* We don't expose _LOADED events to gdbserver core. See + the `dlls_changed' global. */ + if (ourstatus->kind == TARGET_WAITKIND_LOADED) + ourstatus->kind = TARGET_WAITKIND_STOPPED; + + return debug_event_ptid (¤t_event); + default: + OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind)); + /* fall-through */ + case TARGET_WAITKIND_SPURIOUS: + case TARGET_WAITKIND_EXECD: /* do nothing, just continue */ child_continue (DBG_CONTINUE, -1); break; @@ -1428,17 +1629,17 @@ win32_wait (char *status) /* Fetch registers from the inferior process. If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */ static void -win32_fetch_inferior_registers (int regno) +win32_fetch_inferior_registers (struct regcache *regcache, int regno) { - child_fetch_inferior_registers (regno); + child_fetch_inferior_registers (regcache, regno); } /* Store registers to the inferior process. If REGNO is -1, store all registers; otherwise, store at least REGNO. */ static void -win32_store_inferior_registers (int regno) +win32_store_inferior_registers (struct regcache *regcache, int regno) { - child_store_inferior_registers (regno); + child_store_inferior_registers (regcache, regno); } /* Read memory from the inferior process. This should generally be @@ -1491,13 +1692,81 @@ win32_request_interrupt (void) && DebugBreakProcess (current_process_handle)) return; - OUTMSG (("Could not interrupt process.\n")); + /* Last resort, suspend all threads manually. */ + soft_interrupt_requested = 1; } -static const char * -win32_arch_string (void) +#ifdef _WIN32_WCE +int +win32_error_to_fileio_error (DWORD err) { - return the_low_target.arch_string; + switch (err) + { + case ERROR_BAD_PATHNAME: + case ERROR_FILE_NOT_FOUND: + case ERROR_INVALID_NAME: + case ERROR_PATH_NOT_FOUND: + return FILEIO_ENOENT; + case ERROR_CRC: + case ERROR_IO_DEVICE: + case ERROR_OPEN_FAILED: + return FILEIO_EIO; + case ERROR_INVALID_HANDLE: + return FILEIO_EBADF; + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + return FILEIO_EACCES; + case ERROR_NOACCESS: + return FILEIO_EFAULT; + case ERROR_BUSY: + return FILEIO_EBUSY; + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + return FILEIO_EEXIST; + case ERROR_BAD_DEVICE: + return FILEIO_ENODEV; + case ERROR_DIRECTORY: + return FILEIO_ENOTDIR; + case ERROR_FILENAME_EXCED_RANGE: + case ERROR_INVALID_DATA: + case ERROR_INVALID_PARAMETER: + case ERROR_NEGATIVE_SEEK: + return FILEIO_EINVAL; + case ERROR_TOO_MANY_OPEN_FILES: + return FILEIO_EMFILE; + case ERROR_HANDLE_DISK_FULL: + case ERROR_DISK_FULL: + return FILEIO_ENOSPC; + case ERROR_WRITE_PROTECT: + return FILEIO_EROFS; + case ERROR_NOT_SUPPORTED: + return FILEIO_ENOSYS; + } + + return FILEIO_EUNKNOWN; +} + +static void +wince_hostio_last_error (char *buf) +{ + DWORD winerr = GetLastError (); + int fileio_err = win32_error_to_fileio_error (winerr); + sprintf (buf, "F-1,%x", fileio_err); +} +#endif + +/* Write Windows OS Thread Information Block address. */ + +static int +win32_get_tib_address (ptid_t ptid, CORE_ADDR *addr) +{ + win32_thread_info *th; + th = thread_rec (ptid, 0); + if (th == NULL) + return 0; + if (addr != NULL) + *addr = th->thread_local_base; + return 1; } static struct target_ops win32_target_ops = { @@ -1505,24 +1774,46 @@ static struct target_ops win32_target_ops = { win32_attach, win32_kill, win32_detach, + win32_mourn, win32_join, win32_thread_alive, win32_resume, win32_wait, win32_fetch_inferior_registers, win32_store_inferior_registers, + NULL, /* prepare_to_access_memory */ + NULL, /* unprepare_to_access_memory */ win32_read_inferior_memory, win32_write_inferior_memory, - NULL, + NULL, /* lookup_symbols */ win32_request_interrupt, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - win32_arch_string + NULL, /* read_auxv */ + win32_insert_point, + win32_remove_point, + win32_stopped_by_watchpoint, + win32_stopped_data_address, + NULL, /* read_offsets */ + NULL, /* get_tls_address */ + NULL, /* qxfer_spu */ +#ifdef _WIN32_WCE + wince_hostio_last_error, +#else + hostio_last_error_from_errno, +#endif + NULL, /* qxfer_osdata */ + NULL, /* qxfer_siginfo */ + NULL, /* supports_non_stop */ + NULL, /* async */ + NULL, /* start_non_stop */ + NULL, /* supports_multi_process */ + NULL, /* handle_monitor_command */ + NULL, /* core_of_thread */ + NULL, /* process_qsupported */ + NULL, /* supports_tracepoints */ + NULL, /* read_pc */ + NULL, /* write_pc */ + NULL, /* thread_stopped */ + win32_get_tib_address }; /* Initialize the Win32 backend. */ @@ -1533,5 +1824,5 @@ initialize_low (void) if (the_low_target.breakpoint != NULL) set_breakpoint_data (the_low_target.breakpoint, the_low_target.breakpoint_len); - init_registers (); + the_low_target.arch_setup (); }