-/* 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.
+/* Copyright (C) 2007-2015 Free Software Foundation, Inc.
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
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#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>
+#include "win32-low.h"
+#include "x86-low.h"
+
+#ifndef CONTEXT_EXTENDED_REGISTERS
+#define CONTEXT_EXTENDED_REGISTERS 0
#endif
-#define LOG 0
+#define FCS_REGNUM 27
+#define FOP_REGNUM 31
+
+#define FLAG_TRACE_BIT 0x100
-#define OUTMSG(X) do { printf X; fflush (stdout); } while (0)
-#if LOG
-#define OUTMSG2(X) do { printf X; fflush (stdout); } while (0)
+#ifdef __x86_64__
+/* Defined in auto-generated file reg-amd64.c. */
+void init_registers_amd64 (void);
+extern const struct target_desc *tdesc_amd64;
#else
-#define OUTMSG2(X)
+/* Defined in auto-generated file reg-i386.c. */
+void init_registers_i386 (void);
+extern const struct target_desc *tdesc_i386;
#endif
-int debug_threads;
-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;
+static struct x86_debug_reg_state debug_reg_state;
-/* The current debug event from WaitForDebugEvent. */
-static DEBUG_EVENT current_event;
+static int
+update_debug_registers_callback (struct inferior_list_entry *entry,
+ void *pid_p)
+{
+ struct thread_info *thr = (struct thread_info *) entry;
+ win32_thread_info *th = (win32_thread_info *) inferior_target_data (thr);
+ int pid = *(int *) pid_p;
-static int debug_registers_changed = 0;
-static int debug_registers_used = 0;
-static unsigned dr[8];
+ /* Only update the threads of this process. */
+ if (pid_of (thr) == pid)
+ {
+ /* The actual update is done later just before resuming the lwp,
+ we just mark that the registers need updating. */
+ th->debug_registers_changed = 1;
+ }
-typedef BOOL winapi_DebugActiveProcessStop (DWORD dwProcessId);
-typedef BOOL winapi_DebugSetProcessKillOnExit (BOOL KillOnExit);
+ return 0;
+}
-#define FLAG_TRACE_BIT 0x100
-#define CONTEXT_DEBUGGER (CONTEXT_FULL | CONTEXT_FLOATING_POINT)
-#define CONTEXT_DEBUGGER_DR CONTEXT_DEBUGGER | CONTEXT_DEBUG_REGISTERS \
- | CONTEXT_EXTENDED_REGISTERS
+/* Update the inferior's debug register REGNUM from STATE. */
-/* Thread information structure used to track extra information about
- each thread. */
-typedef struct thread_info_struct
+static void
+x86_dr_low_set_addr (int regnum, CORE_ADDR addr)
{
- 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)
+ /* Only update the threads of this process. */
+ int pid = pid_of (current_thread);
+
+ gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
+
+ find_inferior (&all_threads, update_debug_registers_callback, &pid);
+}
+
+/* Update the inferior's DR7 debug control register from STATE. */
+
+static void
+x86_dr_low_set_control (unsigned long control)
{
- thread_info *th = inferior_target_data (current_inferior);
- return th->tid;
+ /* Only update the threads of this process. */
+ int pid = pid_of (current_thread);
+
+ find_inferior (&all_threads, update_debug_registers_callback, &pid);
}
-/* 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)
+/* Return the current value of a DR register of the current thread's
+ context. */
+
+static DWORD64
+win32_get_current_dr (int dr)
{
- struct thread_info *thread;
- thread_info *th;
+ win32_thread_info *th
+ = (win32_thread_info *) inferior_target_data (current_thread);
+
+ win32_require_context (th);
- thread = (struct thread_info *) find_inferior_id (&all_threads, id);
- if (thread == NULL)
- return NULL;
+#define RET_DR(DR) \
+ case DR: \
+ return th->context.Dr ## DR
- th = inferior_target_data (thread);
- if (!th->suspend_count && get_context)
+ switch (dr)
{
- if (get_context > 0 && id != current_event.dwThreadId)
- th->suspend_count = SuspendThread (th->h) + 1;
- else if (get_context < 0)
- th->suspend_count = -1;
+ RET_DR (0);
+ RET_DR (1);
+ RET_DR (2);
+ RET_DR (3);
+ RET_DR (6);
+ RET_DR (7);
+ }
- th->context.ContextFlags = CONTEXT_DEBUGGER_DR;
+#undef RET_DR
- GetThreadContext (th->h, &th->context);
+ gdb_assert_not_reached ("unhandled dr");
+}
- 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;
- }
- }
+static CORE_ADDR
+x86_dr_low_get_addr (int regnum)
+{
+ gdb_assert (DR_FIRSTADDR <= regnum && regnum <= DR_LASTADDR);
- return th;
+ return win32_get_current_dr (regnum - DR_FIRSTADDR);
}
-/* Add a thread to the thread list. */
-static thread_info *
-child_add_thread (DWORD tid, HANDLE h)
+static unsigned long
+x86_dr_low_get_control (void)
{
- thread_info *th;
+ return win32_get_current_dr (7);
+}
- if ((th = thread_rec (tid, FALSE)))
- return th;
+/* Get the value of the DR6 debug status register from the inferior
+ and record it in STATE. */
- th = (thread_info *) malloc (sizeof (*th));
- memset (th, 0, sizeof (*th));
- th->tid = tid;
- th->h = h;
+static unsigned long
+x86_dr_low_get_status (void)
+{
+ return win32_get_current_dr (6);
+}
- add_thread (tid, th, (unsigned int) tid);
- set_inferior_regcache_data ((struct thread_info *)
- find_inferior_id (&all_threads, tid),
- new_register_cache ());
+/* Low-level function vector. */
+struct x86_dr_low_type x86_dr_low =
+ {
+ x86_dr_low_set_control,
+ x86_dr_low_set_addr,
+ x86_dr_low_get_addr,
+ x86_dr_low_get_status,
+ x86_dr_low_get_control,
+ sizeof (void *),
+ };
+
+/* Breakpoint/watchpoint support. */
- /* Set the debug registers for the new thread if they are used. */
- if (debug_registers_used)
+static int
+i386_supports_z_point_type (char z_type)
+{
+ switch (z_type)
{
- /* 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;
+ case Z_PACKET_WRITE_WP:
+ case Z_PACKET_ACCESS_WP:
+ return 1;
+ default:
+ return 0;
}
-
- return th;
}
-/* Delete a thread from the list of threads. */
-static void
-delete_thread_info (struct inferior_list_entry *thread)
+static int
+i386_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
{
- thread_info *th = inferior_target_data ((struct thread_info *) thread);
+ switch (type)
+ {
+ case raw_bkpt_type_write_wp:
+ case raw_bkpt_type_access_wp:
+ {
+ enum target_hw_bp_type hw_type
+ = raw_bkpt_type_to_target_hw_bp_type (type);
- remove_thread ((struct thread_info *) thread);
- CloseHandle (th->h);
- free (th);
+ return x86_dr_insert_watchpoint (&debug_reg_state,
+ hw_type, addr, size);
+ }
+ default:
+ /* Unsupported. */
+ return 1;
+ }
}
-/* Delete a thread from the list of threads. */
-static void
-child_delete_thread (DWORD id)
+static int
+i386_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
{
- struct inferior_list_entry *thread;
+ switch (type)
+ {
+ case raw_bkpt_type_write_wp:
+ case raw_bkpt_type_access_wp:
+ {
+ enum target_hw_bp_type hw_type
+ = raw_bkpt_type_to_target_hw_bp_type (type);
- /* If the last thread is exiting, just return. */
- if (all_threads.head == all_threads.tail)
- return;
+ return x86_dr_remove_watchpoint (&debug_reg_state,
+ hw_type, addr, size);
+ }
+ default:
+ /* Unsupported. */
+ return 1;
+ }
+}
- thread = find_inferior_id (&all_threads, id);
- if (thread == NULL)
- return;
+static int
+x86_stopped_by_watchpoint (void)
+{
+ return x86_dr_stopped_by_watchpoint (&debug_reg_state);
+}
- delete_thread_info (thread);
+static CORE_ADDR
+x86_stopped_data_address (void)
+{
+ CORE_ADDR addr;
+ if (x86_dr_stopped_data_address (&debug_reg_state, &addr))
+ return addr;
+ return 0;
}
-/* Transfer memory from/to the debugged process. */
-static int
-child_xfer_memory (CORE_ADDR memaddr, char *our, int len,
- int write, struct target_ops *target)
+static void
+i386_initial_stuff (void)
{
- SIZE_T done;
- long addr = (long) memaddr;
+ x86_low_init_dregs (&debug_reg_state);
+}
- if (write)
- {
- WriteProcessMemory (current_process_handle, (LPVOID) addr,
- (LPCVOID) our, len, &done);
- FlushInstructionCache (current_process_handle, (LPCVOID) addr, len);
- }
- else
+static void
+i386_get_thread_context (win32_thread_info *th)
+{
+ /* Requesting the CONTEXT_EXTENDED_REGISTERS register set fails if
+ the system doesn't support extended registers. */
+ static DWORD extended_registers = CONTEXT_EXTENDED_REGISTERS;
+
+ again:
+ th->context.ContextFlags = (CONTEXT_FULL
+ | CONTEXT_FLOATING_POINT
+ | CONTEXT_DEBUG_REGISTERS
+ | extended_registers);
+
+ if (!GetThreadContext (th->h, &th->context))
{
- ReadProcessMemory (current_process_handle, (LPCVOID) addr, (LPVOID) our,
- len, &done);
+ DWORD e = GetLastError ();
+
+ if (extended_registers && e == ERROR_INVALID_PARAMETER)
+ {
+ extended_registers = 0;
+ goto again;
+ }
+
+ error ("GetThreadContext failure %ld\n", (long) e);
}
- return done;
}
-/* Generally, what has the program done? */
-enum target_waitkind
+static void
+i386_prepare_to_resume (win32_thread_info *th)
{
- /* 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,
+ if (th->debug_registers_changed)
+ {
+ struct x86_debug_reg_state *dr = &debug_reg_state;
- /* The program has exec'ed a new executable file. The new file's
- pathname is pointed to by value.execd_pathname. */
+ win32_require_context (th);
- TARGET_WAITKIND_EXECD,
+ th->context.Dr0 = dr->dr_mirror[0];
+ th->context.Dr1 = dr->dr_mirror[1];
+ th->context.Dr2 = dr->dr_mirror[2];
+ th->context.Dr3 = dr->dr_mirror[3];
+ /* th->context.Dr6 = dr->dr_status_mirror;
+ FIXME: should we set dr6 also ?? */
+ th->context.Dr7 = dr->dr_control_mirror;
- /* 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,
-};
+ th->debug_registers_changed = 0;
+ }
+}
-struct target_waitstatus
+static void
+i386_thread_added (win32_thread_info *th)
{
- enum target_waitkind kind;
+ th->debug_registers_changed = 1;
+}
- /* 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;
-};
+static void
+i386_single_step (win32_thread_info *th)
+{
+ th->context.EFlags |= FLAG_TRACE_BIT;
+}
-#define NUM_REGS 41
-#define FCS_REGNUM 27
-#define FOP_REGNUM 31
+#ifndef __x86_64__
+/* An array of offset mappings into a Win32 Context structure.
+ This is a one-to-one mapping which is indexed by gdb's register
+ numbers. It retrieves an offset into the context structure where
+ the 4 byte register is located.
+ An offset value of -1 indicates that Win32 does not provide this
+ register in it's CONTEXT structure. In this case regptr will return
+ a pointer into a dummy register. */
#define context_offset(x) ((int)&(((CONTEXT *)NULL)->x))
static const int mappings[] = {
context_offset (Eax),
/* MXCSR */
context_offset (ExtendedRegisters[24])
};
+#undef context_offset
+#else /* __x86_64__ */
+
+#define context_offset(x) (offsetof (CONTEXT, x))
+static const int mappings[] =
+{
+ context_offset (Rax),
+ context_offset (Rbx),
+ context_offset (Rcx),
+ context_offset (Rdx),
+ context_offset (Rsi),
+ context_offset (Rdi),
+ context_offset (Rbp),
+ context_offset (Rsp),
+ context_offset (R8),
+ context_offset (R9),
+ context_offset (R10),
+ context_offset (R11),
+ context_offset (R12),
+ context_offset (R13),
+ context_offset (R14),
+ context_offset (R15),
+ context_offset (Rip),
+ context_offset (EFlags),
+ context_offset (SegCs),
+ context_offset (SegSs),
+ context_offset (SegDs),
+ context_offset (SegEs),
+ context_offset (SegFs),
+ context_offset (SegGs),
+ context_offset (FloatSave.FloatRegisters[0]),
+ context_offset (FloatSave.FloatRegisters[1]),
+ context_offset (FloatSave.FloatRegisters[2]),
+ context_offset (FloatSave.FloatRegisters[3]),
+ context_offset (FloatSave.FloatRegisters[4]),
+ context_offset (FloatSave.FloatRegisters[5]),
+ context_offset (FloatSave.FloatRegisters[6]),
+ context_offset (FloatSave.FloatRegisters[7]),
+ 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 (Xmm0),
+ context_offset (Xmm1),
+ context_offset (Xmm2),
+ context_offset (Xmm3),
+ context_offset (Xmm4),
+ context_offset (Xmm5),
+ context_offset (Xmm6),
+ context_offset (Xmm7),
+ context_offset (Xmm8),
+ context_offset (Xmm9),
+ context_offset (Xmm10),
+ context_offset (Xmm11),
+ context_offset (Xmm12),
+ context_offset (Xmm13),
+ context_offset (Xmm14),
+ context_offset (Xmm15),
+ /* MXCSR */
+ context_offset (FloatSave.MxCsr)
+};
#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);
-}
+#endif /* __x86_64__ */
+/* Fetch register from gdbserver regcache data. */
static void
-do_initial_child_stuff (DWORD pid)
+i386_fetch_inferior_register (struct regcache *regcache,
+ win32_thread_info *th, int r)
{
- 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));
+ char *context_offset = (char *) &th->context + mappings[r];
- 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);
+ supply_register (regcache, r, (char *) &l);
}
else if (r == FOP_REGNUM)
{
l = (*((long *) context_offset) >> 16) & ((1 << 11) - 1);
- supply_register (r, (char *) &l);
+ supply_register (regcache, r, (char *) &l);
}
else
- supply_register (r, context_offset);
+ supply_register (regcache, r, context_offset);
}
-/* Fetch register(s) from the current thread context. */
+/* Store a new register value into the thread context of TH. */
static void
-child_fetch_inferior_registers (int r)
+i386_store_inferior_register (struct regcache *regcache,
+ win32_thread_info *th, 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);
+ char *context_offset = (char *) &th->context + mappings[r];
+ collect_register (regcache, r, context_offset);
}
-/* 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]);
-}
+static const unsigned char i386_win32_breakpoint = 0xcc;
+#define i386_win32_breakpoint_len 1
-/* 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)
+i386_arch_setup (void)
{
-#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);
+#ifdef __x86_64__
+ init_registers_amd64 ();
+ win32_tdesc = tdesc_amd64;
+#else
+ init_registers_i386 ();
+ win32_tdesc = tdesc_i386;
#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;
-}
-
-/* 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;
- }
-}
-
-/* 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));
- 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
+struct win32_target_ops the_low_target = {
+ i386_arch_setup,
+ sizeof (mappings) / sizeof (mappings[0]),
+ i386_initial_stuff,
+ i386_get_thread_context,
+ i386_prepare_to_resume,
+ i386_thread_added,
+ i386_fetch_inferior_register,
+ i386_store_inferior_register,
+ i386_single_step,
+ &i386_win32_breakpoint,
+ i386_win32_breakpoint_len,
+ i386_supports_z_point_type,
+ i386_insert_point,
+ i386_remove_point,
+ x86_stopped_by_watchpoint,
+ x86_stopped_data_address
};
-
-/* Initialize the Win32 backend. */
-void
-initialize_low (void)
-{
- set_target_ops (&win32_target_ops);
-
- init_registers ();
-}