X-Git-Url: http://drtracing.org/?a=blobdiff_plain;ds=sidebyside;f=gdb%2Fgdbserver%2Fwin32-low.c;h=e60be5a6df1570f825002f32777fa1bff6daa779;hb=618f726fcb851883a0094aa7fa17003889b7189f;hp=ea83a5eac0749c39d544ac8904508f071d01a070;hpb=41093d818468d60d4ee2dec16fb62e54674c3011;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c index ea83a5eac0..e60be5a6df 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-2016 Free Software Foundation, Inc. Contributed by Leo Zayas. Based on "win32-nat.c" from GDB. @@ -7,7 +7,7 @@ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or + the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, @@ -16,322 +16,414 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program. If not, see . */ #include "server.h" #include "regcache.h" -#include "gdb/signals.h" - +#include "gdb/fileio.h" +#include "mem-break.h" +#include "win32-low.h" +#include "gdbthread.h" +#include "dll.h" +#include "hostio.h" #include +#include #include +#include #include -#include -#include #include #ifndef USE_WIN32API #include #endif -#define LOG 0 +#define OUTMSG(X) do { printf X; fflush (stderr); } while (0) + +#define OUTMSG2(X) \ + do \ + { \ + if (debug_threads) \ + { \ + printf X; \ + fflush (stderr); \ + } \ + } while (0) + +#ifndef _T +#define _T(x) TEXT (x) +#endif -#define OUTMSG(X) do { printf X; fflush (stdout); } while (0) -#if LOG -#define OUTMSG2(X) do { printf X; fflush (stdout); } while (0) +#ifndef COUNTOF +#define COUNTOF(STR) (sizeof (STR) / sizeof ((STR)[0])) +#endif + +#ifdef _WIN32_WCE +# define GETPROCADDRESS(DLL, PROC) \ + ((winapi_ ## PROC) GetProcAddress (DLL, TEXT (#PROC))) #else -#define OUTMSG2(X) +# define GETPROCADDRESS(DLL, PROC) \ + ((winapi_ ## PROC) GetProcAddress (DLL, #PROC)) #endif int using_threads = 1; /* Globals. */ +static int attaching = 0; static HANDLE current_process_handle = NULL; static DWORD current_process_id = 0; -static enum target_signal last_sig = TARGET_SIGNAL_0; +static DWORD main_thread_id = 0; +static enum gdb_signal last_sig = GDB_SIGNAL_0; /* The current debug event from WaitForDebugEvent. */ static DEBUG_EVENT current_event; -static int debug_registers_changed = 0; -static int debug_registers_used = 0; -static unsigned dr[8]; +/* A status that hasn't been reported to the core yet, and so + win32_wait should return it next, instead of fetching the next + debug event off the win32 API. */ +static struct target_waitstatus cached_status; -typedef BOOL winapi_DebugActiveProcessStop (DWORD dwProcessId); -typedef BOOL winapi_DebugSetProcessKillOnExit (BOOL KillOnExit); +/* Non zero if an interrupt request is to be satisfied by suspending + all threads. */ +static int soft_interrupt_requested = 0; -#define FLAG_TRACE_BIT 0x100 -#define CONTEXT_DEBUGGER (CONTEXT_FULL | CONTEXT_FLOATING_POINT) -#define CONTEXT_DEBUGGER_DR CONTEXT_DEBUGGER | CONTEXT_DEBUG_REGISTERS \ - | CONTEXT_EXTENDED_REGISTERS +/* Non zero if the inferior is stopped in a simulated breakpoint done + by suspending all the threads. */ +static int faked_breakpoint = 0; -/* Thread information structure used to track extra information about - each thread. */ -typedef struct win32_thread_info -{ - DWORD tid; - HANDLE h; - int suspend_count; - CONTEXT context; -} win32_thread_info; -static DWORD main_thread_id = 0; +const struct target_desc *win32_tdesc; + +#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); + +static ptid_t win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, + int options); +static void win32_resume (struct thread_resume *resume_info, size_t n); +#ifndef _WIN32_WCE +static void win32_add_all_dlls (void); +#endif /* Get the thread ID from the current selected inferior (the current thread). */ -static DWORD -current_inferior_tid (void) +static ptid_t +current_thread_ptid (void) { - win32_thread_info *th = inferior_target_data (current_inferior); - return th->tid; + return current_ptid; } -/* 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) +/* The current debug event from WaitForDebugEvent. */ +static ptid_t +debug_event_ptid (DEBUG_EVENT *event) { - struct thread_info *thread; - win32_thread_info *th; + return ptid_build (event->dwProcessId, event->dwThreadId, 0); +} - thread = (struct thread_info *) find_inferior_id (&all_threads, id); - if (thread == NULL) - return NULL; +/* Get the thread context of the thread associated with TH. */ - th = inferior_target_data (thread); - if (!th->suspend_count && get_context) - { - if (get_context > 0 && id != current_event.dwThreadId) - th->suspend_count = SuspendThread (th->h) + 1; - else if (get_context < 0) - th->suspend_count = -1; +static void +win32_get_thread_context (win32_thread_info *th) +{ + memset (&th->context, 0, sizeof (CONTEXT)); + (*the_low_target.get_thread_context) (th); +#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 + SetThreadContext (th->h, &th->context); +} - th->context.ContextFlags = CONTEXT_DEBUGGER_DR; +/* Set the thread context of the thread associated with TH. */ - GetThreadContext (th->h, &th->context); +static void +win32_prepare_to_resume (win32_thread_info *th) +{ + if (the_low_target.prepare_to_resume != NULL) + (*the_low_target.prepare_to_resume) (th); +} + +/* See win32-low.h. */ - if (id == current_event.dwThreadId) +void +win32_require_context (win32_thread_info *th) +{ + if (th->context.ContextFlags == 0) + { + if (!th->suspended) { - /* Copy dr values from that thread. */ - dr[0] = th->context.Dr0; - dr[1] = th->context.Dr1; - dr[2] = th->context.Dr2; - dr[3] = th->context.Dr3; - dr[6] = th->context.Dr6; - dr[7] = th->context.Dr7; + 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; } + + 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) +{ + struct thread_info *thread; + win32_thread_info *th; + + thread = (struct thread_info *) find_inferior_id (&all_threads, ptid); + if (thread == NULL) + return NULL; + th = (win32_thread_info *) inferior_target_data (thread); + if (get_context) + win32_require_context (th); return th; } /* 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 = XCNEW (win32_thread_info); th->tid = tid; th->h = h; + th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb; - add_thread (tid, th, (unsigned int) tid); - set_inferior_regcache_data ((struct thread_info *) - find_inferior_id (&all_threads, tid), - new_register_cache ()); + add_thread (ptid, th); - /* Set the debug registers for the new thread if they are used. */ - if (debug_registers_used) - { - /* Only change the value of the debug registers. */ - th->context.ContextFlags = CONTEXT_DEBUGGER_DR; - - GetThreadContext (th->h, &th->context); - - th->context.Dr0 = dr[0]; - th->context.Dr1 = dr[1]; - th->context.Dr2 = dr[2]; - th->context.Dr3 = dr[3]; - /* th->context.Dr6 = dr[6]; - FIXME: should we set dr6 also ?? */ - th->context.Dr7 = dr[7]; - SetThreadContext (th->h, &th->context); - th->context.ContextFlags = 0; - } + if (the_low_target.thread_added != NULL) + (*the_low_target.thread_added) (th); return th; } /* Delete a thread from the list of threads. */ static void -delete_thread_info (struct inferior_list_entry *thread) +delete_thread_info (struct inferior_list_entry *entry) { - win32_thread_info *th = inferior_target_data ((struct thread_info *) thread); + struct thread_info *thread = (struct thread_info *) entry; + win32_thread_info *th = (win32_thread_info *) inferior_target_data (thread); - remove_thread ((struct thread_info *) thread); + remove_thread (thread); CloseHandle (th->h); free (th); } /* 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) + if (one_inferior_p (&all_threads)) 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_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)); +} + +static int +win32_insert_point (enum raw_bkpt_type type, CORE_ADDR addr, + int size, struct raw_breakpoint *bp) +{ + 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) +{ + 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) +{ + 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; + BOOL success; + SIZE_T done = 0; + DWORD lasterror = 0; + uintptr_t addr = (uintptr_t) memaddr; if (write) { - WriteProcessMemory (current_process_handle, (LPVOID) addr, - (LPCVOID) our, len, &done); + success = WriteProcessMemory (current_process_handle, (LPVOID) addr, + (LPCVOID) our, len, &done); + if (!success) + lasterror = GetLastError (); FlushInstructionCache (current_process_handle, (LPCVOID) addr, len); } else { - ReadProcessMemory (current_process_handle, (LPCVOID) addr, (LPVOID) our, - len, &done); + success = ReadProcessMemory (current_process_handle, (LPCVOID) addr, + (LPVOID) our, len, &done); + if (!success) + lasterror = GetLastError (); } - return done; + if (!success && lasterror == ERROR_PARTIAL_COPY && done > 0) + return done; + else + return success ? done : -1; } -/* Generally, what has the program done? */ -enum target_waitkind +/* Clear out any old thread list and reinitialize it to a pristine + state. */ +static void +child_init_thread_list (void) { - /* The program has exited. The exit status is in value.integer. */ - TARGET_WAITKIND_EXITED, + for_each_inferior (&all_threads, delete_thread_info); +} - /* The program has stopped with a signal. Which signal is in - value.sig. */ - TARGET_WAITKIND_STOPPED, +/* Zero during the child initialization phase, and nonzero otherwise. */ - /* The program is letting us know that it dynamically loaded something - (e.g. it called load(2) on AIX). */ - TARGET_WAITKIND_LOADED, +static int child_initialization_done = 0; - /* The program has exec'ed a new executable file. The new file's - pathname is pointed to by value.execd_pathname. */ +static void +do_initial_child_stuff (HANDLE proch, DWORD pid, int attached) +{ + struct process_info *proc; - TARGET_WAITKIND_EXECD, + last_sig = GDB_SIGNAL_0; - /* Nothing happened, but we stopped anyway. This perhaps should be handled - within target_wait, but I'm not sure target_wait should be resuming the - inferior. */ - TARGET_WAITKIND_SPURIOUS, -}; + current_process_handle = proch; + current_process_id = pid; + main_thread_id = 0; -struct target_waitstatus -{ - enum target_waitkind kind; + soft_interrupt_requested = 0; + faked_breakpoint = 0; - /* 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; -}; + memset (¤t_event, 0, sizeof (current_event)); -#define NUM_REGS 41 -#define FCS_REGNUM 27 -#define FOP_REGNUM 31 - -#define context_offset(x) ((int)&(((CONTEXT *)NULL)->x)) -static const int mappings[] = { - context_offset (Eax), - context_offset (Ecx), - context_offset (Edx), - context_offset (Ebx), - context_offset (Esp), - context_offset (Ebp), - context_offset (Esi), - context_offset (Edi), - context_offset (Eip), - context_offset (EFlags), - context_offset (SegCs), - context_offset (SegSs), - context_offset (SegDs), - context_offset (SegEs), - context_offset (SegFs), - context_offset (SegGs), - context_offset (FloatSave.RegisterArea[0 * 10]), - context_offset (FloatSave.RegisterArea[1 * 10]), - context_offset (FloatSave.RegisterArea[2 * 10]), - context_offset (FloatSave.RegisterArea[3 * 10]), - context_offset (FloatSave.RegisterArea[4 * 10]), - context_offset (FloatSave.RegisterArea[5 * 10]), - context_offset (FloatSave.RegisterArea[6 * 10]), - context_offset (FloatSave.RegisterArea[7 * 10]), - context_offset (FloatSave.ControlWord), - context_offset (FloatSave.StatusWord), - context_offset (FloatSave.TagWord), - context_offset (FloatSave.ErrorSelector), - context_offset (FloatSave.ErrorOffset), - context_offset (FloatSave.DataSelector), - context_offset (FloatSave.DataOffset), - context_offset (FloatSave.ErrorSelector), - /* XMM0-7 */ - context_offset (ExtendedRegisters[10 * 16]), - context_offset (ExtendedRegisters[11 * 16]), - context_offset (ExtendedRegisters[12 * 16]), - context_offset (ExtendedRegisters[13 * 16]), - context_offset (ExtendedRegisters[14 * 16]), - context_offset (ExtendedRegisters[15 * 16]), - context_offset (ExtendedRegisters[16 * 16]), - context_offset (ExtendedRegisters[17 * 16]), - /* MXCSR */ - context_offset (ExtendedRegisters[24]) -}; + proc = add_process (pid, attached); + proc->tdesc = win32_tdesc; + child_init_thread_list (); + child_initialization_done = 0; -#undef context_offset + if (the_low_target.initial_stuff != NULL) + (*the_low_target.initial_stuff) (); -/* Clear out any old thread list and reintialize it to a pristine - state. */ -static void -child_init_thread_list (void) -{ - for_each_inferior (&all_threads, delete_thread_info); -} + cached_status.kind = TARGET_WAITKIND_IGNORE; -static void -do_initial_child_stuff (DWORD pid) -{ - int i; + /* Flush all currently pending debug events (thread and dll list) up + to the initial breakpoint. */ + while (1) + { + struct target_waitstatus status; - last_sig = TARGET_SIGNAL_0; + win32_wait (minus_one_ptid, &status, 0); - debug_registers_changed = 0; - debug_registers_used = 0; - for (i = 0; i < sizeof (dr) / sizeof (dr[0]); i++) - dr[i] = 0; - memset (¤t_event, 0, sizeof (current_event)); + /* Note win32_wait doesn't return thread events. */ + if (status.kind != TARGET_WAITKIND_LOADED) + { + cached_status = status; + break; + } - child_init_thread_list (); + { + struct thread_resume resume; + + resume.thread = minus_one_ptid; + resume.kind = resume_continue; + resume.sig = 0; + + win32_resume (&resume, 1); + } + } + +#ifndef _WIN32_WCE + /* Now that the inferior has been started and all DLLs have been mapped, + we can iterate over all DLLs and load them in. + + We avoid doing it any earlier because, on certain versions of Windows, + LOAD_DLL_DEBUG_EVENTs are sometimes not complete. In particular, + we have seen on Windows 8.1 that the ntdll.dll load event does not + include the DLL name, preventing us from creating an associated SO. + A possible explanation is that ntdll.dll might be mapped before + the SO info gets created by the Windows system -- ntdll.dll is + the first DLL to be reported via LOAD_DLL_DEBUG_EVENT and other DLLs + do not seem to suffer from that problem. + + Rather than try to work around this sort of issue, it is much + simpler to just ignore DLL load/unload events during the startup + phase, and then process them all in one batch now. */ + win32_add_all_dlls (); +#endif + + child_initialization_done = 1; } /* Resume all artificially suspended threads if we are continuing @@ -341,28 +433,27 @@ 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; + win32_thread_info *th = (win32_thread_info *) inferior_target_data (thread); - if ((thread_id == -1 || thread_id == th->tid) - && th->suspend_count) + if (thread_id == -1 || thread_id == th->tid) { - for (i = 0; i < th->suspend_count; i++) - (void) ResumeThread (th->h); - th->suspend_count = 0; - if (debug_registers_changed) + win32_prepare_to_resume (th); + + if (th->suspended) { - /* Only change the value of the debug registers. */ - th->context.ContextFlags = CONTEXT_DEBUG_REGISTERS; - th->context.Dr0 = dr[0]; - th->context.Dr1 = dr[1]; - th->context.Dr2 = dr[2]; - th->context.Dr3 = dr[3]; - /* th->context.Dr6 = dr[6]; - FIXME: should we set dr6 also ?? */ - th->context.Dr7 = dr[7]; - SetThreadContext (th->h, &th->context); - th->context.ContextFlags = 0; + if (th->context.ContextFlags) + { + win32_set_thread_context (th); + th->context.ContextFlags = 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; } } @@ -372,70 +463,148 @@ 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); - continue_status = 0; - if (res) - find_inferior (&all_threads, continue_one_thread, &thread_id); + if (!ContinueDebugEvent (current_event.dwProcessId, + current_event.dwThreadId, + continue_status)) + return FALSE; - debug_registers_changed = 0; - return res; + return TRUE; } -/* Fetch register(s) from gdbserver regcache data. */ +/* Fetch register(s) from the current thread context. */ static void -do_child_fetch_inferior_registers (win32_thread_info *th, int r) +child_fetch_inferior_registers (struct regcache *regcache, int r) { - char *context_offset = ((char *) &th->context) + mappings[r]; - long l; - if (r == FCS_REGNUM) - { - l = *((long *) context_offset) & 0xffff; - supply_register (r, (char *) &l); - } - else if (r == FOP_REGNUM) - { - l = (*((long *) context_offset) >> 16) & ((1 << 11) - 1); - supply_register (r, (char *) &l); - } + int regno; + win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE); + if (r == -1 || r > NUM_REGS) + child_fetch_inferior_registers (regcache, NUM_REGS); else - supply_register (r, context_offset); + for (regno = 0; regno < r; regno++) + (*the_low_target.fetch_inferior_register) (regcache, th, regno); } -/* Fetch register(s) from the current thread context. */ +/* 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_fetch_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_thread_ptid (), TRUE); if (r == -1 || r == 0 || r > NUM_REGS) - child_fetch_inferior_registers (NUM_REGS); + child_store_inferior_registers (regcache, NUM_REGS); else for (regno = 0; regno < r; regno++) - do_child_fetch_inferior_registers (th, regno); + (*the_low_target.store_inferior_register) (regcache, th, regno); } -/* Get register from gdbserver regcache data. */ -static void -do_child_store_inferior_registers (win32_thread_info *th, int r) +/* Map the Windows error number in ERROR to a locale-dependent error + message string and return a pointer to it. Typically, the values + for ERROR come from GetLastError. + + The string pointed to shall not be modified by the application, + but may be overwritten by a subsequent call to strwinerror + + The strwinerror function does not change the current setting + of GetLastError. */ + +char * +strwinerror (DWORD error) { - collect_register (r, ((char *) &th->context) + mappings[r]); + static char buf[1024]; + TCHAR *msgbuf; + DWORD lasterr = GetLastError (); + DWORD chars = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM + | FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, + error, + 0, /* Default language */ + (LPTSTR) &msgbuf, + 0, + NULL); + if (chars != 0) + { + /* If there is an \r\n appended, zap it. */ + if (chars >= 2 + && msgbuf[chars - 2] == '\r' + && msgbuf[chars - 1] == '\n') + { + chars -= 2; + msgbuf[chars] = 0; + } + + if (chars > ((COUNTOF (buf)) - 1)) + { + chars = COUNTOF (buf) - 1; + msgbuf [chars] = 0; + } + +#ifdef UNICODE + wcstombs (buf, msgbuf, chars + 1); +#else + strncpy (buf, msgbuf, chars + 1); +#endif + LocalFree (msgbuf); + } + else + sprintf (buf, "unknown win32 error (%u)", (unsigned) error); + + SetLastError (lasterr); + return buf; } -/* 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) +static BOOL +create_process (const char *program, char *args, + DWORD flags, PROCESS_INFORMATION *pi) { - int regno; - win32_thread_info *th = thread_rec (current_inferior_tid (), TRUE); - if (r == -1 || r == 0 || r > NUM_REGS) - child_store_inferior_registers (NUM_REGS); - else - for (regno = 0; regno < r; regno++) - do_child_store_inferior_registers (th, regno); + BOOL ret; + +#ifdef _WIN32_WCE + wchar_t *p, *wprogram, *wargs; + size_t argslen; + + wprogram = alloca ((strlen (program) + 1) * sizeof (wchar_t)); + mbstowcs (wprogram, program, strlen (program) + 1); + + for (p = wprogram; *p; ++p) + if (L'/' == *p) + *p = L'\\'; + + argslen = strlen (args); + wargs = alloca ((argslen + 1) * sizeof (wchar_t)); + 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 */ +#else + STARTUPINFOA si = { sizeof (STARTUPINFOA) }; + + ret = CreateProcessA (program, /* image name */ + args, /* command line */ + NULL, /* security */ + NULL, /* thread */ + TRUE, /* inherit handles */ + flags, /* start flags */ + NULL, /* environment */ + NULL, /* current directory */ + &si, /* start info */ + pi); /* proc info */ +#endif + + return ret; } /* Start a new process. @@ -448,24 +617,23 @@ static int win32_create_inferior (char *program, char **program_args) { #ifndef USE_WIN32API - char real_path[MAXPATHLEN]; + char real_path[PATH_MAX]; char *orig_path, *new_path, *path_ptr; #endif - char *winenv = NULL; - STARTUPINFO si; - PROCESS_INFORMATION pi; BOOL ret; DWORD flags; char *args; int argslen; int argc; + 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"); - memset (&si, 0, sizeof (si)); - si.cb = sizeof (si); - flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS; #ifndef USE_WIN32API @@ -473,39 +641,44 @@ win32_create_inferior (char *program, char **program_args) path_ptr = getenv ("PATH"); if (path_ptr) { + int size = cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, NULL, 0); orig_path = alloca (strlen (path_ptr) + 1); - new_path = alloca (cygwin_posix_to_win32_path_list_buf_size (path_ptr)); + new_path = alloca (size); strcpy (orig_path, path_ptr); - cygwin_posix_to_win32_path_list (path_ptr, new_path); + cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, new_path, size); setenv ("PATH", new_path, 1); - } - cygwin_conv_to_win32_path (program, real_path); + } + cygwin_conv_path (CCP_POSIX_TO_WIN_A, program, real_path, PATH_MAX); program = real_path; #endif - argslen = strlen (program) + 1; + argslen = 1; for (argc = 1; program_args[argc]; argc++) argslen += strlen (program_args[argc]) + 1; - args = alloca (argslen); - strcpy (args, program); + args = (char *) alloca (argslen); + args[0] = '\0'; 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]); } - OUTMSG2 (("Command line is %s\n", args)); + OUTMSG2 (("Command line is \"%s\"\n", args)); +#ifdef CREATE_NEW_PROCESS_GROUP flags |= CREATE_NEW_PROCESS_GROUP; +#endif - ret = CreateProcess (0, args, /* command line */ - NULL, /* Security */ - NULL, /* thread */ - TRUE, /* inherit handles */ - flags, /* start flags */ - winenv, NULL, /* current directory */ - &si, &pi); + ret = create_process (program, args, flags, &pi); + err = GetLastError (); + if (!ret && err == ERROR_FILE_NOT_FOUND) + { + char *exename = (char *) alloca (strlen (program) + 5); + strcat (strcpy (exename, program), ".exe"); + ret = create_process (exename, args, flags, &pi); + err = GetLastError (); + } #ifndef USE_WIN32API if (orig_path) @@ -514,20 +687,22 @@ win32_create_inferior (char *program, char **program_args) if (!ret) { - error ("Error creating process %s, (error %d): %s\n", args, - (int) GetLastError (), strerror (GetLastError ())); + error ("Error creating process \"%s%s\", (error %d): %s\n", + program, args, (int) err, strwinerror (err)); } else { OUTMSG2 (("Process created: %s\n", (char *) args)); } +#ifndef _WIN32_WCE + /* On Windows CE this handle can't be closed. The OS reuses + it in the debug events, while the 9x/NT versions of Windows + probably use a DuplicateHandle'd one. */ 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; } @@ -538,47 +713,41 @@ win32_create_inferior (char *program, char **program_args) static int win32_attach (unsigned long pid) { - int res = 0; - HMODULE kernel32 = LoadLibrary ("KERNEL32.DLL"); - winapi_DebugActiveProcessStop *DebugActiveProcessStop = NULL; - winapi_DebugSetProcessKillOnExit *DebugSetProcessKillOnExit = NULL; - - DebugActiveProcessStop = - (winapi_DebugActiveProcessStop *) GetProcAddress (kernel32, - "DebugActiveProcessStop"); - DebugSetProcessKillOnExit = - (winapi_DebugSetProcessKillOnExit *) GetProcAddress (kernel32, - "DebugSetProcessKillOnExit"); - - res = DebugActiveProcess (pid) ? 1 : 0; - - if (!res) - error ("Attach to process failed."); - - if (DebugSetProcessKillOnExit != NULL) - DebugSetProcessKillOnExit (FALSE); - - current_process_id = pid; - current_process_handle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); + 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 + DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit); - if (current_process_handle == NULL) + h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid); + if (h != NULL) { - res = 0; - if (DebugActiveProcessStop != NULL) - DebugActiveProcessStop (current_process_id); - } + if (DebugActiveProcess (pid)) + { + if (DebugSetProcessKillOnExit != NULL) + DebugSetProcessKillOnExit (FALSE); - if (res) - do_initial_child_stuff (pid); + /* win32_wait needs to know we're attaching. */ + attaching = 1; + do_initial_child_stuff (h, pid, 1); + return 0; + } - FreeLibrary (kernel32); + CloseHandle (h); + } - return res; + err = GetLastError (); + error ("Attach to process failed (error %d): %s\n", + (int) err, strwinerror (err)); } /* Handle OUTPUT_DEBUG_STRING_EVENT from child process. */ static void -handle_output_debug_string (struct target_waitstatus *ourstatus) +handle_output_debug_string (void) { #define READ_BUFFER_LEN 1024 CORE_ADDR addr; @@ -596,7 +765,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; @@ -608,17 +777,37 @@ handle_output_debug_string (struct target_waitstatus *ourstatus) return; } - if (strncmp (s, "cYg", 3) != 0) - monitor_output (s); + if (!startswith (s, "cYg")) + { + if (!server_waiting) + { + OUTMSG2(("%s", s)); + return; + } + + monitor_output (s); + } #undef READ_BUFFER_LEN } -/* Kill all inferiors. */ static void -win32_kill (void) +win32_clear_inferiors (void) +{ + 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 (;;) @@ -630,48 +819,81 @@ win32_kill (void) if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) break; else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT) - { - struct target_waitstatus our_status = { 0 }; - handle_output_debug_string (&our_status); - } + handle_output_debug_string (); } + + win32_clear_inferiors (); + + process = find_process_pid (pid); + remove_process (process); + return 0; +} + +/* Detach from inferior PID. */ +static int +win32_detach (int pid) +{ + struct process_info *process; + winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL; + winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL; +#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 (DebugSetProcessKillOnExit == NULL + || DebugActiveProcessStop == NULL) + return -1; + + { + struct thread_resume resume; + resume.thread = minus_one_ptid; + resume.kind = resume_continue; + resume.sig = 0; + win32_resume (&resume, 1); + } + + if (!DebugActiveProcessStop (current_process_id)) + return -1; + + DebugSetProcessKillOnExit (FALSE); + process = find_process_pid (pid); + remove_process (process); + + win32_clear_inferiors (); + return 0; } -/* Detach from all inferiors. */ static void -win32_detach (void) +win32_mourn (struct process_info *process) { - HMODULE kernel32 = LoadLibrary ("KERNEL32.DLL"); - winapi_DebugActiveProcessStop *DebugActiveProcessStop = NULL; - winapi_DebugSetProcessKillOnExit *DebugSetProcessKillOnExit = NULL; - - DebugActiveProcessStop = - (winapi_DebugActiveProcessStop *) GetProcAddress (kernel32, - "DebugActiveProcessStop"); - DebugSetProcessKillOnExit = - (winapi_DebugSetProcessKillOnExit *) GetProcAddress (kernel32, - "DebugSetProcessKillOnExit"); - - if (DebugSetProcessKillOnExit != NULL) - DebugSetProcessKillOnExit (FALSE); - - if (DebugActiveProcessStop != NULL) - DebugActiveProcessStop (current_process_id); - else - win32_kill (); + remove_process (process); +} - FreeLibrary (kernel32); +/* 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; @@ -681,76 +903,77 @@ 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; + enum gdb_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; + sig = gdb_signal_from_host (resume_info[0].sig); + step = resume_info[0].kind == resume_step; } else { - sig = 0; + sig = GDB_SIGNAL_0; step = 0; } - if (sig != TARGET_SIGNAL_0) + if (sig != GDB_SIGNAL_0) { if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) { - OUTMSG (("Cannot continue with signal %d here.\n", sig)); + OUTMSG (("Cannot continue with signal %s here.\n", + gdb_signal_to_string (sig))); } else if (sig == last_sig) continue_status = DBG_EXCEPTION_NOT_HANDLED; else - OUTMSG (("Can only continue with recieved signal %d.\n", last_sig)); + OUTMSG (("Can only continue with received signal %s.\n", + gdb_signal_to_string (last_sig))); } - last_sig = TARGET_SIGNAL_0; + last_sig = GDB_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) { + win32_prepare_to_resume (th); + if (th->context.ContextFlags) { - if (debug_registers_changed) - { - th->context.Dr0 = dr[0]; - th->context.Dr1 = dr[1]; - th->context.Dr2 = dr[2]; - th->context.Dr3 = dr[3]; - /* th->context.Dr6 = dr[6]; - FIXME: should we set dr6 also ?? */ - th->context.Dr7 = dr[7]; - } - /* Move register values from the inferior into the thread context structure. */ regcache_invalidate (); if (step) - th->context.EFlags |= FLAG_TRACE_BIT; + { + if (the_low_target.single_step != NULL) + (*the_low_target.single_step) (th); + else + error ("Single stepping is not supported " + "in this configuration.\n"); + } - SetThreadContext (th->h, &th->context); + win32_set_thread_context (th); th->context.ContextFlags = 0; } } @@ -761,163 +984,510 @@ win32_resume (struct thread_resume *resume_info) child_continue (continue_status, tid); } -static int +static void +win32_add_one_solib (const char *name, CORE_ADDR load_addr) +{ + char buf[MAX_PATH + 1]; + char buf2[MAX_PATH + 1]; + +#ifdef _WIN32_WCE + WIN32_FIND_DATA w32_fd; + WCHAR wname[MAX_PATH + 1]; + mbstowcs (wname, name, MAX_PATH); + HANDLE h = FindFirstFile (wname, &w32_fd); +#else + WIN32_FIND_DATAA w32_fd; + HANDLE h = FindFirstFileA (name, &w32_fd); +#endif + + /* The symbols in a dll are offset by 0x1000, which is the + offset from 0 of the first byte in an image - because + of the file header and the section alignment. */ + load_addr += 0x1000; + + if (h == INVALID_HANDLE_VALUE) + strcpy (buf, name); + else + { + FindClose (h); + strcpy (buf, name); +#ifndef _WIN32_WCE + { + char cwd[MAX_PATH + 1]; + char *p; + if (GetCurrentDirectoryA (MAX_PATH + 1, cwd)) + { + p = strrchr (buf, '\\'); + if (p) + p[1] = '\0'; + SetCurrentDirectoryA (buf); + GetFullPathNameA (w32_fd.cFileName, MAX_PATH, buf, &p); + SetCurrentDirectoryA (cwd); + } + } +#endif + } + +#ifndef _WIN32_WCE + if (strcasecmp (buf, "ntdll.dll") == 0) + { + GetSystemDirectoryA (buf, sizeof (buf)); + strcat (buf, "\\ntdll.dll"); + } +#endif + +#ifdef __CYGWIN__ + cygwin_conv_path (CCP_WIN_A_TO_POSIX, buf, buf2, sizeof (buf2)); +#else + strcpy (buf2, buf); +#endif + + 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); +typedef BOOL (WINAPI *winapi_GetModuleInformation) (HANDLE, HMODULE, + LPMODULEINFO, DWORD); +typedef DWORD (WINAPI *winapi_GetModuleFileNameExA) (HANDLE, HMODULE, + LPSTR, DWORD); + +static winapi_EnumProcessModules win32_EnumProcessModules; +static winapi_GetModuleInformation win32_GetModuleInformation; +static winapi_GetModuleFileNameExA win32_GetModuleFileNameExA; + +static BOOL +load_psapi (void) +{ + static int psapi_loaded = 0; + static HMODULE dll = NULL; + + if (!psapi_loaded) + { + psapi_loaded = 1; + dll = LoadLibrary (TEXT("psapi.dll")); + if (!dll) + return FALSE; + win32_EnumProcessModules = + GETPROCADDRESS (dll, EnumProcessModules); + win32_GetModuleInformation = + GETPROCADDRESS (dll, GetModuleInformation); + win32_GetModuleFileNameExA = + GETPROCADDRESS (dll, GetModuleFileNameExA); + } + + return (win32_EnumProcessModules != NULL + && win32_GetModuleInformation != NULL + && win32_GetModuleFileNameExA != NULL); +} + +#ifndef _WIN32_WCE + +/* Iterate over all DLLs currently mapped by our inferior, and + add them to our list of solibs. */ + +static void +win32_add_all_dlls (void) +{ + size_t i; + HMODULE dh_buf[1]; + HMODULE *DllHandle = dh_buf; + DWORD cbNeeded; + BOOL ok; + + if (!load_psapi ()) + return; + + cbNeeded = 0; + ok = (*win32_EnumProcessModules) (current_process_handle, + DllHandle, + sizeof (HMODULE), + &cbNeeded); + + if (!ok || !cbNeeded) + return; + + DllHandle = (HMODULE *) alloca (cbNeeded); + if (!DllHandle) + return; + + ok = (*win32_EnumProcessModules) (current_process_handle, + DllHandle, + cbNeeded, + &cbNeeded); + if (!ok) + return; + + for (i = 1; i < ((size_t) cbNeeded / sizeof (HMODULE)); i++) + { + MODULEINFO mi; + char dll_name[MAX_PATH]; + + if (!(*win32_GetModuleInformation) (current_process_handle, + DllHandle[i], + &mi, + sizeof (mi))) + continue; + if ((*win32_GetModuleFileNameExA) (current_process_handle, + DllHandle[i], + dll_name, + MAX_PATH) == 0) + continue; + win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) mi.lpBaseOfDll); + } +} +#endif + +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). */ + +static void +handle_load_dll (void) +{ + LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll; + char *dll_name; + + dll_name = get_image_name (current_process_handle, + event->lpImageName, event->fUnicode); + if (!dll_name) + return; + + 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). */ + +static void +handle_unload_dll (void) +{ + CORE_ADDR load_addr = + (CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll; + + /* The symbols in a dll are offset by 0x1000, which is the + offset from 0 of the first byte in an image - because + of the file header and the section alignment. */ + load_addr += 0x1000; + unloaded_dll (NULL, load_addr); +} + +static void handle_exception (struct target_waitstatus *ourstatus) { - win32_thread_info *th; DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode; ourstatus->kind = TARGET_WAITKIND_STOPPED; - /* Record the context of the current thread. */ - th = thread_rec (current_event.dwThreadId, -1); - switch (code) { case EXCEPTION_ACCESS_VIOLATION: OUTMSG2 (("EXCEPTION_ACCESS_VIOLATION")); - ourstatus->value.sig = TARGET_SIGNAL_SEGV; + ourstatus->value.sig = GDB_SIGNAL_SEGV; break; case STATUS_STACK_OVERFLOW: OUTMSG2 (("STATUS_STACK_OVERFLOW")); - ourstatus->value.sig = TARGET_SIGNAL_SEGV; + ourstatus->value.sig = GDB_SIGNAL_SEGV; break; case STATUS_FLOAT_DENORMAL_OPERAND: OUTMSG2 (("STATUS_FLOAT_DENORMAL_OPERAND")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; + ourstatus->value.sig = GDB_SIGNAL_FPE; break; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: OUTMSG2 (("EXCEPTION_ARRAY_BOUNDS_EXCEEDED")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; + ourstatus->value.sig = GDB_SIGNAL_FPE; break; case STATUS_FLOAT_INEXACT_RESULT: OUTMSG2 (("STATUS_FLOAT_INEXACT_RESULT")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; + ourstatus->value.sig = GDB_SIGNAL_FPE; break; case STATUS_FLOAT_INVALID_OPERATION: OUTMSG2 (("STATUS_FLOAT_INVALID_OPERATION")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; + ourstatus->value.sig = GDB_SIGNAL_FPE; break; case STATUS_FLOAT_OVERFLOW: OUTMSG2 (("STATUS_FLOAT_OVERFLOW")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; + ourstatus->value.sig = GDB_SIGNAL_FPE; break; case STATUS_FLOAT_STACK_CHECK: OUTMSG2 (("STATUS_FLOAT_STACK_CHECK")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; + ourstatus->value.sig = GDB_SIGNAL_FPE; break; case STATUS_FLOAT_UNDERFLOW: OUTMSG2 (("STATUS_FLOAT_UNDERFLOW")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; + ourstatus->value.sig = GDB_SIGNAL_FPE; break; case STATUS_FLOAT_DIVIDE_BY_ZERO: OUTMSG2 (("STATUS_FLOAT_DIVIDE_BY_ZERO")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; + ourstatus->value.sig = GDB_SIGNAL_FPE; break; case STATUS_INTEGER_DIVIDE_BY_ZERO: OUTMSG2 (("STATUS_INTEGER_DIVIDE_BY_ZERO")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; + ourstatus->value.sig = GDB_SIGNAL_FPE; break; case STATUS_INTEGER_OVERFLOW: OUTMSG2 (("STATUS_INTEGER_OVERFLOW")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; + ourstatus->value.sig = GDB_SIGNAL_FPE; break; case EXCEPTION_BREAKPOINT: OUTMSG2 (("EXCEPTION_BREAKPOINT")); - ourstatus->value.sig = TARGET_SIGNAL_TRAP; + 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 = TARGET_SIGNAL_INT; + ourstatus->value.sig = GDB_SIGNAL_INT; break; case DBG_CONTROL_BREAK: OUTMSG2 (("DBG_CONTROL_BREAK")); - ourstatus->value.sig = TARGET_SIGNAL_INT; + ourstatus->value.sig = GDB_SIGNAL_INT; break; case EXCEPTION_SINGLE_STEP: OUTMSG2 (("EXCEPTION_SINGLE_STEP")); - ourstatus->value.sig = TARGET_SIGNAL_TRAP; + ourstatus->value.sig = GDB_SIGNAL_TRAP; break; case EXCEPTION_ILLEGAL_INSTRUCTION: OUTMSG2 (("EXCEPTION_ILLEGAL_INSTRUCTION")); - ourstatus->value.sig = TARGET_SIGNAL_ILL; + ourstatus->value.sig = GDB_SIGNAL_ILL; break; case EXCEPTION_PRIV_INSTRUCTION: OUTMSG2 (("EXCEPTION_PRIV_INSTRUCTION")); - ourstatus->value.sig = TARGET_SIGNAL_ILL; + ourstatus->value.sig = GDB_SIGNAL_ILL; break; case EXCEPTION_NONCONTINUABLE_EXCEPTION: OUTMSG2 (("EXCEPTION_NONCONTINUABLE_EXCEPTION")); - ourstatus->value.sig = TARGET_SIGNAL_ILL; + ourstatus->value.sig = GDB_SIGNAL_ILL; break; default: if (current_event.u.Exception.dwFirstChance) - return 0; - OUTMSG2 (("gdbserver: unknown target exception 0x%08lx at 0x%08lx", - current_event.u.Exception.ExceptionRecord.ExceptionCode, - (DWORD) current_event.u.Exception.ExceptionRecord. - ExceptionAddress)); - ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; + { + 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 (struct inferior_list_entry *entry) +{ + struct thread_info *thread = (struct thread_info *) entry; + win32_thread_info *th = (win32_thread_info *) 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. */ -/* Get the next event from the child. Return 1 if the event requires - handling. */ static int get_child_debug_event (struct target_waitstatus *ourstatus) { - BOOL debug_event; - DWORD continue_status, event_code; - win32_thread_info *th = NULL; - static win32_thread_info dummy_thread_info; - int retval = 0; + ptid_t ptid; -in: - - last_sig = TARGET_SIGNAL_0; + last_sig = GDB_SIGNAL_0; ourstatus->kind = TARGET_WAITKIND_SPURIOUS; - if (!(debug_event = WaitForDebugEvent (¤t_event, 1000))) - goto out; + /* Check if GDB sent us an interrupt request. */ + check_remote_input_interrupt_request (); + + 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; + } - current_inferior = - (struct thread_info *) find_inferior_id (&all_threads, - current_event.dwThreadId); + return 0; + } + } - continue_status = DBG_CONTINUE; - event_code = current_event.dwDebugEventCode; + gotevent: - switch (event_code) + switch (current_event.dwDebugEventCode) { case CREATE_THREAD_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT " - "for pid=%d tid=%x)\n", + "for pid=%u tid=%x)\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); /* Record the existence of this thread. */ - th = child_add_thread (current_event.dwThreadId, - current_event.u.CreateThread.hThread); - - retval = current_event.dwThreadId; + 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: OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT " - "for pid=%d tid=%x\n", + "for pid=%u tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); - child_delete_thread (current_event.dwThreadId); - th = &dummy_thread_info; - break; + child_delete_thread (current_event.dwProcessId, + current_event.dwThreadId); + + current_thread = (struct thread_info *) all_threads.head; + return 1; case CREATE_PROCESS_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT " - "for pid=%d tid=%x\n", + "for pid=%u tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); CloseHandle (current_event.u.CreateProcessInfo.hFile); @@ -929,151 +1499,161 @@ in: ourstatus->value.execd_pathname = "Main executable"; /* Add the main thread. */ - th = - child_add_thread (main_thread_id, - current_event.u.CreateProcessInfo.hThread); - - retval = ourstatus->value.related_pid = current_event.dwThreadId; + child_add_thread (current_event.dwProcessId, + main_thread_id, + current_event.u.CreateProcessInfo.hThread, + current_event.u.CreateProcessInfo.lpThreadLocalBase); + + ourstatus->value.related_pid = debug_event_ptid (¤t_event); +#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: OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT " - "for pid=%d tid=%x\n", + "for pid=%u tid=%x\n", (unsigned) current_event.dwProcessId, (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; - retval = main_thread_id; break; case LOAD_DLL_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event LOAD_DLL_DEBUG_EVENT " - "for pid=%d tid=%x\n", + "for pid=%u tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); CloseHandle (current_event.u.LoadDll.hFile); + if (! child_initialization_done) + break; + handle_load_dll (); ourstatus->kind = TARGET_WAITKIND_LOADED; - ourstatus->value.integer = 0; - retval = main_thread_id; + ourstatus->value.sig = GDB_SIGNAL_TRAP; break; case UNLOAD_DLL_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT " - "for pid=%d tid=%x\n", + "for pid=%u tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); + if (! child_initialization_done) + break; + handle_unload_dll (); + ourstatus->kind = TARGET_WAITKIND_LOADED; + ourstatus->value.sig = GDB_SIGNAL_TRAP; break; case EXCEPTION_DEBUG_EVENT: OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT " - "for pid=%d tid=%x\n", + "for pid=%u tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); - retval = handle_exception (ourstatus); + handle_exception (ourstatus); break; case OUTPUT_DEBUG_STRING_EVENT: /* A message from the kernel (or Cygwin). */ OUTMSG2 (("gdbserver: kernel event OUTPUT_DEBUG_STRING_EVENT " - "for pid=%d tid=%x\n", + "for pid=%u tid=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId)); - handle_output_debug_string (ourstatus); + handle_output_debug_string (); break; default: OUTMSG2 (("gdbserver: kernel event unknown " - "for pid=%d tid=%x code=%ld\n", + "for pid=%u tid=%x code=%x\n", (unsigned) current_event.dwProcessId, (unsigned) current_event.dwThreadId, - current_event.dwDebugEventCode)); + (unsigned) current_event.dwDebugEventCode)); break; } - current_inferior = - (struct thread_info *) find_inferior_id (&all_threads, - current_event.dwThreadId); - - if (!retval || (event_code != EXCEPTION_DEBUG_EVENT && event_code != EXIT_PROCESS_DEBUG_EVENT)) - { - child_continue (continue_status, -1); - goto in; - } - - if (th == NULL) - thread_rec (current_event.dwThreadId, TRUE); - -out: - return retval; + ptid = debug_event_ptid (¤t_event); + current_thread = + (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; + struct regcache *regcache; - *status = 'T'; + if (cached_status.kind != TARGET_WAITKIND_IGNORE) + { + /* The core always does a wait after creating the inferior, and + do_initial_child_stuff already ran the inferior to the + initial breakpoint (or an exit, if creating the process + fails). Report it now. */ + *ourstatus = cached_status; + cached_status.kind = TARGET_WAITKIND_IGNORE; + return debug_event_ptid (¤t_event); + } while (1) { - get_child_debug_event (&our_status); + if (!get_child_debug_event (ourstatus)) + continue; - if (our_status.kind == TARGET_WAITKIND_EXITED) + 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; - } - else if (our_status.kind == TARGET_WAITKIND_STOPPED) - { - OUTMSG2 (("Child Stopped with signal = %x \n", - WSTOPSIG (our_status.value.sig))); - - *status = 'T'; - - child_fetch_inferior_registers (-1); - - return our_status.value.sig; + ourstatus->value.integer)); + win32_clear_inferiors (); + return pid_to_ptid (current_event.dwProcessId); + case TARGET_WAITKIND_STOPPED: + 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); + 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; } - else - OUTMSG (("Ignoring unknown internal event, %d\n", our_status.kind)); - - { - struct thread_resume resume; - resume.thread = -1; - resume.step = 0; - resume.sig = 0; - resume.leave_stopped = 0; - win32_resume (&resume); - } } } /* 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 @@ -1082,7 +1662,7 @@ win32_store_inferior_registers (int regno) static int win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) { - return child_xfer_memory (memaddr, myaddr, len, 0, 0) != len; + return child_xfer_memory (memaddr, (char *) myaddr, len, 0, 0) != len; } /* Write memory to the inferior process. This should generally be @@ -1096,34 +1676,200 @@ win32_write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len; } -static const char * -win32_arch_string (void) +/* Send an interrupt request to the inferior process. */ +static void +win32_request_interrupt (void) { - return "i386"; + winapi_DebugBreakProcess DebugBreakProcess; + winapi_GenerateConsoleCtrlEvent GenerateConsoleCtrlEvent; + +#ifdef _WIN32_WCE + HMODULE dll = GetModuleHandle (_T("COREDLL.DLL")); +#else + HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL")); +#endif + + GenerateConsoleCtrlEvent = GETPROCADDRESS (dll, GenerateConsoleCtrlEvent); + + if (GenerateConsoleCtrlEvent != NULL + && GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, current_process_id)) + return; + + /* GenerateConsoleCtrlEvent can fail if process id being debugged is + not a process group id. + Fallback to XP/Vista 'DebugBreakProcess', which generates a + breakpoint exception in the interior process. */ + + DebugBreakProcess = GETPROCADDRESS (dll, DebugBreakProcess); + + if (DebugBreakProcess != NULL + && DebugBreakProcess (current_process_handle)) + return; + + /* Last resort, suspend all threads manually. */ + soft_interrupt_requested = 1; +} + +#ifdef _WIN32_WCE +int +win32_error_to_fileio_error (DWORD err) +{ + 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; +} + +/* Implementation of the target_ops method "sw_breakpoint_from_kind". */ + +static const gdb_byte * +win32_sw_breakpoint_from_kind (int kind, int *size) +{ + *size = the_low_target.breakpoint_len; + return the_low_target.breakpoint; } static struct target_ops win32_target_ops = { win32_create_inferior, + NULL, /* post_create_inferior */ 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, /* done_accessing_memory */ win32_read_inferior_memory, win32_write_inferior_memory, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - win32_arch_string + 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 */ + 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, /* 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, /* support_btrace */ + 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, }; /* Initialize the Win32 backend. */ @@ -1131,6 +1877,5 @@ void initialize_low (void) { set_target_ops (&win32_target_ops); - - init_registers (); + the_low_target.arch_setup (); }