* win32-low.c: ... this.
* configure.srv: Replace win32-i386-low.o with win32-low.o.
* Makefile.in: Likewise.
+2007-03-28 Pedro Alves <pedro_alves@portugalmail.pt>
+
+ * 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 <pedro_alves@portugalmail.pt>
* remote-utils.c (monitor_output): Constify msg parameter.
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)
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"
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
+++ /dev/null
-/* 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 <windows.h>
-#include <imagehlp.h>
-#include <psapi.h>
-#include <sys/param.h>
-#include <malloc.h>
-#include <process.h>
-
-#ifndef USE_WIN32API
-#include <sys/cygwin.h>
-#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 ();
-}
--- /dev/null
+/* 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 <windows.h>
+#include <imagehlp.h>
+#include <psapi.h>
+#include <sys/param.h>
+#include <malloc.h>
+#include <process.h>
+
+#ifndef USE_WIN32API
+#include <sys/cygwin.h>
+#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 ();
+}