From: Pedro Alves Date: Wed, 28 Mar 2007 22:00:15 +0000 (+0000) Subject: * win32-i386-low.c: Rename to ... X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=544afa546f1b9674088b7b309f85c5309fccf6a6;p=binutils-gdb.git * win32-i386-low.c: Rename to ... * win32-low.c: ... this. * configure.srv: Replace win32-i386-low.o with win32-low.o. * Makefile.in: Likewise. --- diff --git a/gdb/gdbserver/ChangeLog b/gdb/gdbserver/ChangeLog index eed968d7627..3910a638773 100644 --- a/gdb/gdbserver/ChangeLog +++ b/gdb/gdbserver/ChangeLog @@ -1,3 +1,10 @@ +2007-03-28 Pedro Alves + + * win32-i386-low.c: Rename to ... + * win32-low.c: ... this. + * configure.srv: Replace win32-i386-low.o with win32-low.o. + * Makefile.in: Likewise. + 2007-03-27 Pedro Alves * remote-utils.c (monitor_output): Constify msg parameter. diff --git a/gdb/gdbserver/Makefile.in b/gdb/gdbserver/Makefile.in index 5b2a3ed0e2e..4292c4492a0 100644 --- a/gdb/gdbserver/Makefile.in +++ b/gdb/gdbserver/Makefile.in @@ -311,7 +311,7 @@ linux-sh-low.o: linux-sh-low.c $(linux_low_h) $(server_h) linux-x86-64-low.o: linux-x86-64-low.c $(linux_low_h) $(server_h) \ $(gdb_proc_service_h) -win32-i386-low.o: win32-i386-low.c $(server_h) $(regdef_h) $(regcache_h) +win32-low.o: win32-low.c $(server_h) $(regdef_h) $(regcache_h) spu-low.o: spu-low.c $(server_h) diff --git a/gdb/gdbserver/configure.srv b/gdb/gdbserver/configure.srv index 2cfb7fcb83e..cc5f86456d0 100644 --- a/gdb/gdbserver/configure.srv +++ b/gdb/gdbserver/configure.srv @@ -44,7 +44,7 @@ case "${target}" in srv_linux_thread_db=yes ;; i[34567]86-*-cygwin*) srv_regobj=reg-i386.o - srv_tgtobj="win32-i386-low.o" + srv_tgtobj="win32-low.o" ;; i[34567]86-*-linux*) srv_regobj=reg-i386-linux.o srv_tgtobj="linux-low.o linux-i386-low.o i387-fp.o" @@ -53,7 +53,7 @@ case "${target}" in srv_linux_thread_db=yes ;; i[34567]86-*-mingw*) srv_regobj=reg-i386.o - srv_tgtobj="win32-i386-low.o" + srv_tgtobj="win32-low.o" srv_mingw=yes ;; ia64-*-linux*) srv_regobj=reg-ia64.o diff --git a/gdb/gdbserver/win32-i386-low.c b/gdb/gdbserver/win32-i386-low.c deleted file mode 100644 index 655da86d7a8..00000000000 --- a/gdb/gdbserver/win32-i386-low.c +++ /dev/null @@ -1,1136 +0,0 @@ -/* Low level interface to Windows debugging, for gdbserver. - Copyright (C) 2006, 2007 Free Software Foundation, Inc. - - Contributed by Leo Zayas. Based on "win32-nat.c" from GDB. - - This file is part of GDB. - - 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 - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - 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. */ - -#include "server.h" -#include "regcache.h" -#include "gdb/signals.h" - -#include -#include -#include -#include -#include -#include - -#ifndef USE_WIN32API -#include -#endif - -#define LOG 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) -#endif - -int using_threads = 1; - -/* Globals. */ -static HANDLE current_process_handle = NULL; -static DWORD current_process_id = 0; -static enum target_signal last_sig = TARGET_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]; - -typedef BOOL winapi_DebugActiveProcessStop (DWORD dwProcessId); -typedef BOOL winapi_DebugSetProcessKillOnExit (BOOL KillOnExit); - -#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 - -/* Thread information structure used to track extra information about - each thread. */ -typedef struct thread_info_struct -{ - DWORD tid; - HANDLE h; - int suspend_count; - CONTEXT context; -} thread_info; -static DWORD main_thread_id = 0; - -/* Get the thread ID from the current selected inferior (the current - thread). */ -static DWORD -current_inferior_tid (void) -{ - thread_info *th = inferior_target_data (current_inferior); - return th->tid; -} - -/* Find a thread record given a thread id. If GET_CONTEXT is set then - also retrieve the context for this thread. */ -static thread_info * -thread_rec (DWORD id, int get_context) -{ - struct thread_info *thread; - thread_info *th; - - thread = (struct thread_info *) find_inferior_id (&all_threads, id); - if (thread == NULL) - return NULL; - - 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; - - th->context.ContextFlags = CONTEXT_DEBUGGER_DR; - - GetThreadContext (th->h, &th->context); - - if (id == current_event.dwThreadId) - { - /* 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; - } - } - - return th; -} - -/* Add a thread to the thread list. */ -static thread_info * -child_add_thread (DWORD tid, HANDLE h) -{ - thread_info *th; - - if ((th = thread_rec (tid, FALSE))) - return th; - - th = (thread_info *) malloc (sizeof (*th)); - memset (th, 0, sizeof (*th)); - th->tid = tid; - th->h = h; - - add_thread (tid, th, (unsigned int) tid); - set_inferior_regcache_data ((struct thread_info *) - find_inferior_id (&all_threads, tid), - new_register_cache ()); - - /* 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; - } - - return th; -} - -/* Delete a thread from the list of threads. */ -static void -delete_thread_info (struct inferior_list_entry *thread) -{ - thread_info *th = inferior_target_data ((struct thread_info *) thread); - - remove_thread ((struct thread_info *) thread); - CloseHandle (th->h); - free (th); -} - -/* Delete a thread from the list of threads. */ -static void -child_delete_thread (DWORD id) -{ - struct inferior_list_entry *thread; - - /* If the last thread is exiting, just return. */ - if (all_threads.head == all_threads.tail) - return; - - thread = find_inferior_id (&all_threads, id); - if (thread == NULL) - return; - - delete_thread_info (thread); -} - -/* 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; - - if (write) - { - WriteProcessMemory (current_process_handle, (LPVOID) addr, - (LPCVOID) our, len, &done); - FlushInstructionCache (current_process_handle, (LPCVOID) addr, len); - } - else - { - ReadProcessMemory (current_process_handle, (LPCVOID) addr, (LPVOID) our, - len, &done); - } - 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 something - (e.g. it called load(2) on AIX). */ - 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 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, -}; - -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; -}; - -#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]) -}; - -#undef context_offset - -/* 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); -} - -static void -do_initial_child_stuff (DWORD pid) -{ - int i; - - last_sig = TARGET_SIGNAL_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)); - - child_init_thread_list (); -} - -/* Resume all artificially suspended threads if we are continuing - execution. */ -static int -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; - thread_info *th = inferior_target_data (thread); - int i; - - if ((thread_id == -1 || thread_id == th->tid) - && th->suspend_count) - { - for (i = 0; i < th->suspend_count; i++) - (void) ResumeThread (th->h); - th->suspend_count = 0; - if (debug_registers_changed) - { - /* 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; - } - } - - return 0; -} - -static BOOL -child_continue (DWORD continue_status, int thread_id) -{ - BOOL res; - - res = ContinueDebugEvent (current_event.dwProcessId, - current_event.dwThreadId, continue_status); - continue_status = 0; - if (res) - find_inferior (&all_threads, continue_one_thread, &thread_id); - - debug_registers_changed = 0; - return res; -} - -/* Fetch register(s) from gdbserver regcache data. */ -static void -do_child_fetch_inferior_registers (thread_info *th, 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); - } - else - supply_register (r, context_offset); -} - -/* Fetch register(s) from the current thread context. */ -static void -child_fetch_inferior_registers (int r) -{ - int regno; - thread_info *th = thread_rec (current_inferior_tid (), TRUE); - if (r == -1 || r == 0 || r > NUM_REGS) - child_fetch_inferior_registers (NUM_REGS); - else - for (regno = 0; regno < r; regno++) - do_child_fetch_inferior_registers (th, regno); -} - -/* Get register from gdbserver regcache data. */ -static void -do_child_store_inferior_registers (thread_info *th, int r) -{ - collect_register (r, ((char *) &th->context) + mappings[r]); -} - -/* 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) -{ - int regno; - 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); -} - -/* Start a new process. - PROGRAM is a path to the program to execute. - ARGS is a standard NULL-terminated array of arguments, - to be passed to the inferior as ``argv''. - Returns the new PID on success, -1 on failure. Registers the new - process with the process list. */ -static int -win32_create_inferior (char *program, char **program_args) -{ -#ifndef USE_WIN32API - char real_path[MAXPATHLEN]; - 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; - - 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 - orig_path = NULL; - path_ptr = getenv ("PATH"); - if (path_ptr) - { - orig_path = alloca (strlen (path_ptr) + 1); - new_path = alloca (cygwin_posix_to_win32_path_list_buf_size (path_ptr)); - strcpy (orig_path, path_ptr); - cygwin_posix_to_win32_path_list (path_ptr, new_path); - setenv ("PATH", new_path, 1); - } - cygwin_conv_to_win32_path (program, real_path); - program = real_path; -#endif - - argslen = strlen (program) + 1; - for (argc = 1; program_args[argc]; argc++) - argslen += strlen (program_args[argc]) + 1; - args = alloca (argslen); - strcpy (args, program); - for (argc = 1; program_args[argc]; argc++) - { - /* FIXME: Can we do better about quoting? How does Cygwin - handle this? */ - strcat (args, " "); - strcat (args, program_args[argc]); - } - OUTMSG2 (("Command line is %s\n", args)); - - flags |= CREATE_NEW_PROCESS_GROUP; - - ret = CreateProcess (0, args, /* command line */ - NULL, /* Security */ - NULL, /* thread */ - TRUE, /* inherit handles */ - flags, /* start flags */ - winenv, NULL, /* current directory */ - &si, &pi); - -#ifndef USE_WIN32API - if (orig_path) - setenv ("PATH", orig_path, 1); -#endif - - if (!ret) - { - error ("Error creating process %s, (error %d): %s\n", args, - (int) GetLastError (), strerror (GetLastError ())); - } - else - { - OUTMSG2 (("Process created: %s\n", (char *) args)); - } - - CloseHandle (pi.hThread); - - current_process_handle = pi.hProcess; - current_process_id = pi.dwProcessId; - - do_initial_child_stuff (current_process_id); - - return current_process_id; -} - -/* Attach to a running process. - PID is the process ID to attach to, specified by the user - or a higher layer. */ -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); - - if (current_process_handle == NULL) - { - res = 0; - if (DebugActiveProcessStop != NULL) - DebugActiveProcessStop (current_process_id); - } - - if (res) - do_initial_child_stuff (pid); - - FreeLibrary (kernel32); - - return res; -} - -/* Handle OUTPUT_DEBUG_STRING_EVENT from child process. */ -static void -handle_output_debug_string (struct target_waitstatus *ourstatus) -{ -#define READ_BUFFER_LEN 1024 - CORE_ADDR addr; - char s[READ_BUFFER_LEN + 1] = { 0 }; - DWORD nbytes = current_event.u.DebugString.nDebugStringLength; - - if (nbytes == 0) - return; - - if (nbytes > READ_BUFFER_LEN) - nbytes = READ_BUFFER_LEN; - - addr = (CORE_ADDR) (size_t) current_event.u.DebugString.lpDebugStringData; - - if (current_event.u.DebugString.fUnicode) - { - /* The event tells us how many bytes, not chars, even - in Unicode. */ - WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 }; - if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0) - return; - wcstombs (s, buffer, (nbytes + 1) / sizeof (WCHAR)); - } - else - { - if (read_inferior_memory (addr, (unsigned char *) s, nbytes) != 0) - return; - } - - if (strncmp (s, "cYg", 3) != 0) - monitor_output (s); -#undef READ_BUFFER_LEN -} - -/* Kill all inferiors. */ -static void -win32_kill (void) -{ - if (current_process_handle == NULL) - return; - - TerminateProcess (current_process_handle, 0); - for (;;) - { - if (!child_continue (DBG_CONTINUE, -1)) - break; - if (!WaitForDebugEvent (¤t_event, INFINITE)) - break; - 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); - } - } -} - -/* Detach from all inferiors. */ -static void -win32_detach (void) -{ - 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 (); - - FreeLibrary (kernel32); -} - -/* Return 1 iff the thread with thread ID TID is alive. */ -static int -win32_thread_alive (unsigned long tid) -{ - int res; - - /* Our thread list is reliable; don't bother to poll target - threads. */ - if (find_inferior_id (&all_threads, tid) != NULL) - res = 1; - else - res = 0; - return res; -} - -/* Resume the inferior process. RESUME_INFO describes how we want - to resume. */ -static void -win32_resume (struct thread_resume *resume_info) -{ - DWORD tid; - enum target_signal sig; - int step; - thread_info *th; - DWORD continue_status = DBG_CONTINUE; - - /* This handles the very limited set of resume packets that GDB can - currently produce. */ - - if (resume_info[0].thread == -1) - tid = -1; - else if (resume_info[1].thread == -1 && !resume_info[1].leave_stopped) - 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) - { - sig = resume_info[0].sig; - step = resume_info[0].step; - } - else - { - sig = 0; - step = 0; - } - - if (sig != TARGET_SIGNAL_0) - { - if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) - { - OUTMSG (("Cannot continue with signal %d here.\n", sig)); - } - else if (sig == last_sig) - continue_status = DBG_EXCEPTION_NOT_HANDLED; - else - OUTMSG (("Can only continue with recieved signal %d.\n", last_sig)); - } - - last_sig = TARGET_SIGNAL_0; - - /* Get context for the currently selected thread. */ - th = thread_rec (current_event.dwThreadId, FALSE); - if (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; - - SetThreadContext (th->h, &th->context); - th->context.ContextFlags = 0; - } - } - - /* Allow continuing with the same signal that interrupted us. - Otherwise complain. */ - - child_continue (continue_status, tid); -} - -static int -handle_exception (struct target_waitstatus *ourstatus) -{ - 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; - break; - case STATUS_STACK_OVERFLOW: - OUTMSG2 (("STATUS_STACK_OVERFLOW")); - ourstatus->value.sig = TARGET_SIGNAL_SEGV; - break; - case STATUS_FLOAT_DENORMAL_OPERAND: - OUTMSG2 (("STATUS_FLOAT_DENORMAL_OPERAND")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; - break; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: - OUTMSG2 (("EXCEPTION_ARRAY_BOUNDS_EXCEEDED")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; - break; - case STATUS_FLOAT_INEXACT_RESULT: - OUTMSG2 (("STATUS_FLOAT_INEXACT_RESULT")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; - break; - case STATUS_FLOAT_INVALID_OPERATION: - OUTMSG2 (("STATUS_FLOAT_INVALID_OPERATION")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; - break; - case STATUS_FLOAT_OVERFLOW: - OUTMSG2 (("STATUS_FLOAT_OVERFLOW")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; - break; - case STATUS_FLOAT_STACK_CHECK: - OUTMSG2 (("STATUS_FLOAT_STACK_CHECK")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; - break; - case STATUS_FLOAT_UNDERFLOW: - OUTMSG2 (("STATUS_FLOAT_UNDERFLOW")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; - break; - case STATUS_FLOAT_DIVIDE_BY_ZERO: - OUTMSG2 (("STATUS_FLOAT_DIVIDE_BY_ZERO")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; - break; - case STATUS_INTEGER_DIVIDE_BY_ZERO: - OUTMSG2 (("STATUS_INTEGER_DIVIDE_BY_ZERO")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; - break; - case STATUS_INTEGER_OVERFLOW: - OUTMSG2 (("STATUS_INTEGER_OVERFLOW")); - ourstatus->value.sig = TARGET_SIGNAL_FPE; - break; - case EXCEPTION_BREAKPOINT: - OUTMSG2 (("EXCEPTION_BREAKPOINT")); - ourstatus->value.sig = TARGET_SIGNAL_TRAP; - break; - case DBG_CONTROL_C: - OUTMSG2 (("DBG_CONTROL_C")); - ourstatus->value.sig = TARGET_SIGNAL_INT; - break; - case DBG_CONTROL_BREAK: - OUTMSG2 (("DBG_CONTROL_BREAK")); - ourstatus->value.sig = TARGET_SIGNAL_INT; - break; - case EXCEPTION_SINGLE_STEP: - OUTMSG2 (("EXCEPTION_SINGLE_STEP")); - ourstatus->value.sig = TARGET_SIGNAL_TRAP; - break; - case EXCEPTION_ILLEGAL_INSTRUCTION: - OUTMSG2 (("EXCEPTION_ILLEGAL_INSTRUCTION")); - ourstatus->value.sig = TARGET_SIGNAL_ILL; - break; - case EXCEPTION_PRIV_INSTRUCTION: - OUTMSG2 (("EXCEPTION_PRIV_INSTRUCTION")); - ourstatus->value.sig = TARGET_SIGNAL_ILL; - break; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: - OUTMSG2 (("EXCEPTION_NONCONTINUABLE_EXCEPTION")); - ourstatus->value.sig = TARGET_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; - break; - } - OUTMSG2 (("\n")); - last_sig = ourstatus->value.sig; - return 1; -} - -/* 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; - thread_info *th = NULL; - static thread_info dummy_thread_info; - int retval = 0; - -in: - - last_sig = TARGET_SIGNAL_0; - ourstatus->kind = TARGET_WAITKIND_SPURIOUS; - - if (!(debug_event = WaitForDebugEvent (¤t_event, 1000))) - goto out; - - current_inferior = - (struct thread_info *) find_inferior_id (&all_threads, - current_event.dwThreadId); - - continue_status = DBG_CONTINUE; - event_code = current_event.dwDebugEventCode; - - switch (event_code) - { - case CREATE_THREAD_DEBUG_EVENT: - OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT " - "for pid=%d 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; - break; - - case EXIT_THREAD_DEBUG_EVENT: - OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT " - "for pid=%d tid=%x\n", - (unsigned) current_event.dwProcessId, - (unsigned) current_event.dwThreadId)); - child_delete_thread (current_event.dwThreadId); - th = &dummy_thread_info; - break; - - case CREATE_PROCESS_DEBUG_EVENT: - OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT " - "for pid=%d tid=%x\n", - (unsigned) current_event.dwProcessId, - (unsigned) current_event.dwThreadId)); - CloseHandle (current_event.u.CreateProcessInfo.hFile); - - current_process_handle = current_event.u.CreateProcessInfo.hProcess; - main_thread_id = current_event.dwThreadId; - - ourstatus->kind = TARGET_WAITKIND_EXECD; - 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; - break; - - case EXIT_PROCESS_DEBUG_EVENT: - OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT " - "for pid=%d tid=%x\n", - (unsigned) current_event.dwProcessId, - (unsigned) current_event.dwThreadId)); - ourstatus->kind = TARGET_WAITKIND_EXITED; - ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode; - 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", - (unsigned) current_event.dwProcessId, - (unsigned) current_event.dwThreadId)); - CloseHandle (current_event.u.LoadDll.hFile); - - ourstatus->kind = TARGET_WAITKIND_LOADED; - ourstatus->value.integer = 0; - retval = main_thread_id; - break; - - case UNLOAD_DLL_DEBUG_EVENT: - OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT " - "for pid=%d tid=%x\n", - (unsigned) current_event.dwProcessId, - (unsigned) current_event.dwThreadId)); - break; - - case EXCEPTION_DEBUG_EVENT: - OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT " - "for pid=%d tid=%x\n", - (unsigned) current_event.dwProcessId, - (unsigned) current_event.dwThreadId)); - retval = 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", - (unsigned) current_event.dwProcessId, - (unsigned) current_event.dwThreadId)); - handle_output_debug_string (ourstatus); - break; - - default: - OUTMSG2 (("gdbserver: kernel event unknown " - "for pid=%d tid=%x code=%ld\n", - (unsigned) current_event.dwProcessId, - (unsigned) current_event.dwThreadId, - 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; -} - -/* 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) -{ - struct target_waitstatus our_status; - - *status = 'T'; - - while (1) - { - get_child_debug_event (&our_status); - - if (our_status.kind == 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; - } - 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) -{ - child_fetch_inferior_registers (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) -{ - child_store_inferior_registers (regno); -} - -/* Read memory from the inferior process. This should generally be - called through read_inferior_memory, which handles breakpoint shadowing. - Read LEN bytes at MEMADDR into a buffer at MYADDR. */ -static int -win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) -{ - return child_xfer_memory (memaddr, myaddr, len, 0, 0) != len; -} - -/* Write memory to the inferior process. This should generally be - called through write_inferior_memory, which handles breakpoint shadowing. - Write LEN bytes from the buffer at MYADDR to MEMADDR. - Returns 0 on success and errno on failure. */ -static int -win32_write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, - int len) -{ - return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len; -} - -static const char * -win32_arch_string (void) -{ - return "i386"; -} - -static struct target_ops win32_target_ops = { - win32_create_inferior, - win32_attach, - win32_kill, - win32_detach, - win32_thread_alive, - win32_resume, - win32_wait, - win32_fetch_inferior_registers, - win32_store_inferior_registers, - win32_read_inferior_memory, - win32_write_inferior_memory, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - win32_arch_string -}; - -/* Initialize the Win32 backend. */ -void -initialize_low (void) -{ - set_target_ops (&win32_target_ops); - - init_registers (); -} diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c new file mode 100644 index 00000000000..655da86d7a8 --- /dev/null +++ b/gdb/gdbserver/win32-low.c @@ -0,0 +1,1136 @@ +/* Low level interface to Windows debugging, for gdbserver. + Copyright (C) 2006, 2007 Free Software Foundation, Inc. + + Contributed by Leo Zayas. Based on "win32-nat.c" from GDB. + + This file is part of GDB. + + 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 + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + 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. */ + +#include "server.h" +#include "regcache.h" +#include "gdb/signals.h" + +#include +#include +#include +#include +#include +#include + +#ifndef USE_WIN32API +#include +#endif + +#define LOG 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) +#endif + +int using_threads = 1; + +/* Globals. */ +static HANDLE current_process_handle = NULL; +static DWORD current_process_id = 0; +static enum target_signal last_sig = TARGET_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]; + +typedef BOOL winapi_DebugActiveProcessStop (DWORD dwProcessId); +typedef BOOL winapi_DebugSetProcessKillOnExit (BOOL KillOnExit); + +#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 + +/* Thread information structure used to track extra information about + each thread. */ +typedef struct thread_info_struct +{ + DWORD tid; + HANDLE h; + int suspend_count; + CONTEXT context; +} thread_info; +static DWORD main_thread_id = 0; + +/* Get the thread ID from the current selected inferior (the current + thread). */ +static DWORD +current_inferior_tid (void) +{ + thread_info *th = inferior_target_data (current_inferior); + return th->tid; +} + +/* Find a thread record given a thread id. If GET_CONTEXT is set then + also retrieve the context for this thread. */ +static thread_info * +thread_rec (DWORD id, int get_context) +{ + struct thread_info *thread; + thread_info *th; + + thread = (struct thread_info *) find_inferior_id (&all_threads, id); + if (thread == NULL) + return NULL; + + 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; + + th->context.ContextFlags = CONTEXT_DEBUGGER_DR; + + GetThreadContext (th->h, &th->context); + + if (id == current_event.dwThreadId) + { + /* 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; + } + } + + return th; +} + +/* Add a thread to the thread list. */ +static thread_info * +child_add_thread (DWORD tid, HANDLE h) +{ + thread_info *th; + + if ((th = thread_rec (tid, FALSE))) + return th; + + th = (thread_info *) malloc (sizeof (*th)); + memset (th, 0, sizeof (*th)); + th->tid = tid; + th->h = h; + + add_thread (tid, th, (unsigned int) tid); + set_inferior_regcache_data ((struct thread_info *) + find_inferior_id (&all_threads, tid), + new_register_cache ()); + + /* 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; + } + + return th; +} + +/* Delete a thread from the list of threads. */ +static void +delete_thread_info (struct inferior_list_entry *thread) +{ + thread_info *th = inferior_target_data ((struct thread_info *) thread); + + remove_thread ((struct thread_info *) thread); + CloseHandle (th->h); + free (th); +} + +/* Delete a thread from the list of threads. */ +static void +child_delete_thread (DWORD id) +{ + struct inferior_list_entry *thread; + + /* If the last thread is exiting, just return. */ + if (all_threads.head == all_threads.tail) + return; + + thread = find_inferior_id (&all_threads, id); + if (thread == NULL) + return; + + delete_thread_info (thread); +} + +/* 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; + + if (write) + { + WriteProcessMemory (current_process_handle, (LPVOID) addr, + (LPCVOID) our, len, &done); + FlushInstructionCache (current_process_handle, (LPCVOID) addr, len); + } + else + { + ReadProcessMemory (current_process_handle, (LPCVOID) addr, (LPVOID) our, + len, &done); + } + 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 something + (e.g. it called load(2) on AIX). */ + 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 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, +}; + +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; +}; + +#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]) +}; + +#undef context_offset + +/* 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); +} + +static void +do_initial_child_stuff (DWORD pid) +{ + int i; + + last_sig = TARGET_SIGNAL_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)); + + child_init_thread_list (); +} + +/* Resume all artificially suspended threads if we are continuing + execution. */ +static int +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; + thread_info *th = inferior_target_data (thread); + int i; + + if ((thread_id == -1 || thread_id == th->tid) + && th->suspend_count) + { + for (i = 0; i < th->suspend_count; i++) + (void) ResumeThread (th->h); + th->suspend_count = 0; + if (debug_registers_changed) + { + /* 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; + } + } + + return 0; +} + +static BOOL +child_continue (DWORD continue_status, int thread_id) +{ + BOOL res; + + res = ContinueDebugEvent (current_event.dwProcessId, + current_event.dwThreadId, continue_status); + continue_status = 0; + if (res) + find_inferior (&all_threads, continue_one_thread, &thread_id); + + debug_registers_changed = 0; + return res; +} + +/* Fetch register(s) from gdbserver regcache data. */ +static void +do_child_fetch_inferior_registers (thread_info *th, 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); + } + else + supply_register (r, context_offset); +} + +/* Fetch register(s) from the current thread context. */ +static void +child_fetch_inferior_registers (int r) +{ + int regno; + thread_info *th = thread_rec (current_inferior_tid (), TRUE); + if (r == -1 || r == 0 || r > NUM_REGS) + child_fetch_inferior_registers (NUM_REGS); + else + for (regno = 0; regno < r; regno++) + do_child_fetch_inferior_registers (th, regno); +} + +/* Get register from gdbserver regcache data. */ +static void +do_child_store_inferior_registers (thread_info *th, int r) +{ + collect_register (r, ((char *) &th->context) + mappings[r]); +} + +/* 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) +{ + int regno; + 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); +} + +/* Start a new process. + PROGRAM is a path to the program to execute. + ARGS is a standard NULL-terminated array of arguments, + to be passed to the inferior as ``argv''. + Returns the new PID on success, -1 on failure. Registers the new + process with the process list. */ +static int +win32_create_inferior (char *program, char **program_args) +{ +#ifndef USE_WIN32API + char real_path[MAXPATHLEN]; + 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; + + 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 + orig_path = NULL; + path_ptr = getenv ("PATH"); + if (path_ptr) + { + orig_path = alloca (strlen (path_ptr) + 1); + new_path = alloca (cygwin_posix_to_win32_path_list_buf_size (path_ptr)); + strcpy (orig_path, path_ptr); + cygwin_posix_to_win32_path_list (path_ptr, new_path); + setenv ("PATH", new_path, 1); + } + cygwin_conv_to_win32_path (program, real_path); + program = real_path; +#endif + + argslen = strlen (program) + 1; + for (argc = 1; program_args[argc]; argc++) + argslen += strlen (program_args[argc]) + 1; + args = alloca (argslen); + strcpy (args, program); + for (argc = 1; program_args[argc]; argc++) + { + /* FIXME: Can we do better about quoting? How does Cygwin + handle this? */ + strcat (args, " "); + strcat (args, program_args[argc]); + } + OUTMSG2 (("Command line is %s\n", args)); + + flags |= CREATE_NEW_PROCESS_GROUP; + + ret = CreateProcess (0, args, /* command line */ + NULL, /* Security */ + NULL, /* thread */ + TRUE, /* inherit handles */ + flags, /* start flags */ + winenv, NULL, /* current directory */ + &si, &pi); + +#ifndef USE_WIN32API + if (orig_path) + setenv ("PATH", orig_path, 1); +#endif + + if (!ret) + { + error ("Error creating process %s, (error %d): %s\n", args, + (int) GetLastError (), strerror (GetLastError ())); + } + else + { + OUTMSG2 (("Process created: %s\n", (char *) args)); + } + + CloseHandle (pi.hThread); + + current_process_handle = pi.hProcess; + current_process_id = pi.dwProcessId; + + do_initial_child_stuff (current_process_id); + + return current_process_id; +} + +/* Attach to a running process. + PID is the process ID to attach to, specified by the user + or a higher layer. */ +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); + + if (current_process_handle == NULL) + { + res = 0; + if (DebugActiveProcessStop != NULL) + DebugActiveProcessStop (current_process_id); + } + + if (res) + do_initial_child_stuff (pid); + + FreeLibrary (kernel32); + + return res; +} + +/* Handle OUTPUT_DEBUG_STRING_EVENT from child process. */ +static void +handle_output_debug_string (struct target_waitstatus *ourstatus) +{ +#define READ_BUFFER_LEN 1024 + CORE_ADDR addr; + char s[READ_BUFFER_LEN + 1] = { 0 }; + DWORD nbytes = current_event.u.DebugString.nDebugStringLength; + + if (nbytes == 0) + return; + + if (nbytes > READ_BUFFER_LEN) + nbytes = READ_BUFFER_LEN; + + addr = (CORE_ADDR) (size_t) current_event.u.DebugString.lpDebugStringData; + + if (current_event.u.DebugString.fUnicode) + { + /* The event tells us how many bytes, not chars, even + in Unicode. */ + WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 }; + if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0) + return; + wcstombs (s, buffer, (nbytes + 1) / sizeof (WCHAR)); + } + else + { + if (read_inferior_memory (addr, (unsigned char *) s, nbytes) != 0) + return; + } + + if (strncmp (s, "cYg", 3) != 0) + monitor_output (s); +#undef READ_BUFFER_LEN +} + +/* Kill all inferiors. */ +static void +win32_kill (void) +{ + if (current_process_handle == NULL) + return; + + TerminateProcess (current_process_handle, 0); + for (;;) + { + if (!child_continue (DBG_CONTINUE, -1)) + break; + if (!WaitForDebugEvent (¤t_event, INFINITE)) + break; + 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); + } + } +} + +/* Detach from all inferiors. */ +static void +win32_detach (void) +{ + 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 (); + + FreeLibrary (kernel32); +} + +/* Return 1 iff the thread with thread ID TID is alive. */ +static int +win32_thread_alive (unsigned long tid) +{ + int res; + + /* Our thread list is reliable; don't bother to poll target + threads. */ + if (find_inferior_id (&all_threads, tid) != NULL) + res = 1; + else + res = 0; + return res; +} + +/* Resume the inferior process. RESUME_INFO describes how we want + to resume. */ +static void +win32_resume (struct thread_resume *resume_info) +{ + DWORD tid; + enum target_signal sig; + int step; + thread_info *th; + DWORD continue_status = DBG_CONTINUE; + + /* This handles the very limited set of resume packets that GDB can + currently produce. */ + + if (resume_info[0].thread == -1) + tid = -1; + else if (resume_info[1].thread == -1 && !resume_info[1].leave_stopped) + 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) + { + sig = resume_info[0].sig; + step = resume_info[0].step; + } + else + { + sig = 0; + step = 0; + } + + if (sig != TARGET_SIGNAL_0) + { + if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) + { + OUTMSG (("Cannot continue with signal %d here.\n", sig)); + } + else if (sig == last_sig) + continue_status = DBG_EXCEPTION_NOT_HANDLED; + else + OUTMSG (("Can only continue with recieved signal %d.\n", last_sig)); + } + + last_sig = TARGET_SIGNAL_0; + + /* Get context for the currently selected thread. */ + th = thread_rec (current_event.dwThreadId, FALSE); + if (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; + + SetThreadContext (th->h, &th->context); + th->context.ContextFlags = 0; + } + } + + /* Allow continuing with the same signal that interrupted us. + Otherwise complain. */ + + child_continue (continue_status, tid); +} + +static int +handle_exception (struct target_waitstatus *ourstatus) +{ + 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; + break; + case STATUS_STACK_OVERFLOW: + OUTMSG2 (("STATUS_STACK_OVERFLOW")); + ourstatus->value.sig = TARGET_SIGNAL_SEGV; + break; + case STATUS_FLOAT_DENORMAL_OPERAND: + OUTMSG2 (("STATUS_FLOAT_DENORMAL_OPERAND")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: + OUTMSG2 (("EXCEPTION_ARRAY_BOUNDS_EXCEEDED")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_INEXACT_RESULT: + OUTMSG2 (("STATUS_FLOAT_INEXACT_RESULT")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_INVALID_OPERATION: + OUTMSG2 (("STATUS_FLOAT_INVALID_OPERATION")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_OVERFLOW: + OUTMSG2 (("STATUS_FLOAT_OVERFLOW")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_STACK_CHECK: + OUTMSG2 (("STATUS_FLOAT_STACK_CHECK")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_UNDERFLOW: + OUTMSG2 (("STATUS_FLOAT_UNDERFLOW")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_FLOAT_DIVIDE_BY_ZERO: + OUTMSG2 (("STATUS_FLOAT_DIVIDE_BY_ZERO")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_INTEGER_DIVIDE_BY_ZERO: + OUTMSG2 (("STATUS_INTEGER_DIVIDE_BY_ZERO")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case STATUS_INTEGER_OVERFLOW: + OUTMSG2 (("STATUS_INTEGER_OVERFLOW")); + ourstatus->value.sig = TARGET_SIGNAL_FPE; + break; + case EXCEPTION_BREAKPOINT: + OUTMSG2 (("EXCEPTION_BREAKPOINT")); + ourstatus->value.sig = TARGET_SIGNAL_TRAP; + break; + case DBG_CONTROL_C: + OUTMSG2 (("DBG_CONTROL_C")); + ourstatus->value.sig = TARGET_SIGNAL_INT; + break; + case DBG_CONTROL_BREAK: + OUTMSG2 (("DBG_CONTROL_BREAK")); + ourstatus->value.sig = TARGET_SIGNAL_INT; + break; + case EXCEPTION_SINGLE_STEP: + OUTMSG2 (("EXCEPTION_SINGLE_STEP")); + ourstatus->value.sig = TARGET_SIGNAL_TRAP; + break; + case EXCEPTION_ILLEGAL_INSTRUCTION: + OUTMSG2 (("EXCEPTION_ILLEGAL_INSTRUCTION")); + ourstatus->value.sig = TARGET_SIGNAL_ILL; + break; + case EXCEPTION_PRIV_INSTRUCTION: + OUTMSG2 (("EXCEPTION_PRIV_INSTRUCTION")); + ourstatus->value.sig = TARGET_SIGNAL_ILL; + break; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: + OUTMSG2 (("EXCEPTION_NONCONTINUABLE_EXCEPTION")); + ourstatus->value.sig = TARGET_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; + break; + } + OUTMSG2 (("\n")); + last_sig = ourstatus->value.sig; + return 1; +} + +/* 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; + thread_info *th = NULL; + static thread_info dummy_thread_info; + int retval = 0; + +in: + + last_sig = TARGET_SIGNAL_0; + ourstatus->kind = TARGET_WAITKIND_SPURIOUS; + + if (!(debug_event = WaitForDebugEvent (¤t_event, 1000))) + goto out; + + current_inferior = + (struct thread_info *) find_inferior_id (&all_threads, + current_event.dwThreadId); + + continue_status = DBG_CONTINUE; + event_code = current_event.dwDebugEventCode; + + switch (event_code) + { + case CREATE_THREAD_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT " + "for pid=%d 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; + break; + + case EXIT_THREAD_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + child_delete_thread (current_event.dwThreadId); + th = &dummy_thread_info; + break; + + case CREATE_PROCESS_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + CloseHandle (current_event.u.CreateProcessInfo.hFile); + + current_process_handle = current_event.u.CreateProcessInfo.hProcess; + main_thread_id = current_event.dwThreadId; + + ourstatus->kind = TARGET_WAITKIND_EXECD; + 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; + break; + + case EXIT_PROCESS_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + ourstatus->kind = TARGET_WAITKIND_EXITED; + ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode; + 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", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + CloseHandle (current_event.u.LoadDll.hFile); + + ourstatus->kind = TARGET_WAITKIND_LOADED; + ourstatus->value.integer = 0; + retval = main_thread_id; + break; + + case UNLOAD_DLL_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + break; + + case EXCEPTION_DEBUG_EVENT: + OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT " + "for pid=%d tid=%x\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + retval = 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", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId)); + handle_output_debug_string (ourstatus); + break; + + default: + OUTMSG2 (("gdbserver: kernel event unknown " + "for pid=%d tid=%x code=%ld\n", + (unsigned) current_event.dwProcessId, + (unsigned) current_event.dwThreadId, + 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; +} + +/* 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) +{ + struct target_waitstatus our_status; + + *status = 'T'; + + while (1) + { + get_child_debug_event (&our_status); + + if (our_status.kind == 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; + } + 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) +{ + child_fetch_inferior_registers (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) +{ + child_store_inferior_registers (regno); +} + +/* Read memory from the inferior process. This should generally be + called through read_inferior_memory, which handles breakpoint shadowing. + Read LEN bytes at MEMADDR into a buffer at MYADDR. */ +static int +win32_read_inferior_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len) +{ + return child_xfer_memory (memaddr, myaddr, len, 0, 0) != len; +} + +/* Write memory to the inferior process. This should generally be + called through write_inferior_memory, which handles breakpoint shadowing. + Write LEN bytes from the buffer at MYADDR to MEMADDR. + Returns 0 on success and errno on failure. */ +static int +win32_write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr, + int len) +{ + return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len; +} + +static const char * +win32_arch_string (void) +{ + return "i386"; +} + +static struct target_ops win32_target_ops = { + win32_create_inferior, + win32_attach, + win32_kill, + win32_detach, + win32_thread_alive, + win32_resume, + win32_wait, + win32_fetch_inferior_registers, + win32_store_inferior_registers, + win32_read_inferior_memory, + win32_write_inferior_memory, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + win32_arch_string +}; + +/* Initialize the Win32 backend. */ +void +initialize_low (void) +{ + set_target_ops (&win32_target_ops); + + init_registers (); +}