X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Fwin32-low.c;h=bcf0e4d5f74c3abc529fec409fbfc8e59e6211a5;hb=723b724b171ec32ce7005c43fc7da4f5206f7190;hp=51b1915ee8447d53da7352db61d3ff704abd3552;hpb=34b34921087ce04e7a45cbb38a3b8af17b0c630a;p=deliverable%2Fbinutils-gdb.git
diff --git a/gdb/gdbserver/win32-low.c b/gdb/gdbserver/win32-low.c
index 51b1915ee8..bcf0e4d5f7 100644
--- a/gdb/gdbserver/win32-low.c
+++ b/gdb/gdbserver/win32-low.c
@@ -1,5 +1,6 @@
/* Low level interface to Windows debugging, for gdbserver.
- Copyright (C) 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011
+ Free Software Foundation, Inc.
Contributed by Leo Zayas. Based on "win32-nat.c" from GDB.
@@ -7,7 +8,7 @@
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
@@ -16,36 +17,39 @@
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor,
- Boston, MA 02110-1301, USA. */
+ along with this program. If not, see . */
#include "server.h"
#include "regcache.h"
#include "gdb/signals.h"
+#include "gdb/fileio.h"
#include "mem-break.h"
#include "win32-low.h"
+#include
#include
#include
#include
+#include
#include
#include
-#include
#include
#ifndef USE_WIN32API
#include
#endif
-#define LOG 0
+#define OUTMSG(X) do { printf X; fflush (stderr); } while (0)
-#define OUTMSG(X) do { printf X; fflush (stdout); } while (0)
-#if LOG
-#define OUTMSG2(X) do { printf X; fflush (stdout); } while (0)
-#else
-#define OUTMSG2(X) do ; while (0)
-#endif
+#define OUTMSG2(X) \
+ do \
+ { \
+ if (debug_threads) \
+ { \
+ printf X; \
+ fflush (stderr); \
+ } \
+ } while (0)
#ifndef _T
#define _T(x) TEXT (x)
@@ -66,50 +70,108 @@
int using_threads = 1;
/* Globals. */
+static int attaching = 0;
static HANDLE current_process_handle = NULL;
static DWORD current_process_id = 0;
+static DWORD main_thread_id = 0;
static enum target_signal last_sig = TARGET_SIGNAL_0;
/* The current debug event from WaitForDebugEvent. */
static DEBUG_EVENT current_event;
+/* Non zero if an interrupt request is to be satisfied by suspending
+ all threads. */
+static int soft_interrupt_requested = 0;
+
+/* Non zero if the inferior is stopped in a simulated breakpoint done
+ by suspending all the threads. */
+static int faked_breakpoint = 0;
+
#define NUM_REGS (the_low_target.num_regs)
typedef BOOL WINAPI (*winapi_DebugActiveProcessStop) (DWORD dwProcessId);
typedef BOOL WINAPI (*winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit);
+typedef BOOL WINAPI (*winapi_DebugBreakProcess) (HANDLE);
+typedef BOOL WINAPI (*winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD);
-static DWORD main_thread_id = 0;
-
-static void win32_resume (struct thread_resume *resume_info);
+static void win32_resume (struct thread_resume *resume_info, size_t n);
/* Get the thread ID from the current selected inferior (the current
thread). */
-static DWORD
-current_inferior_tid (void)
+static ptid_t
+current_inferior_ptid (void)
+{
+ return ((struct inferior_list_entry*) current_inferior)->id;
+}
+
+/* The current debug event from WaitForDebugEvent. */
+static ptid_t
+debug_event_ptid (DEBUG_EVENT *event)
+{
+ return ptid_build (event->dwProcessId, event->dwThreadId, 0);
+}
+
+/* Get the thread context of the thread associated with TH. */
+
+static void
+win32_get_thread_context (win32_thread_info *th)
+{
+ memset (&th->context, 0, sizeof (CONTEXT));
+ (*the_low_target.get_thread_context) (th, ¤t_event);
+#ifdef _WIN32_WCE
+ memcpy (&th->base_context, &th->context, sizeof (CONTEXT));
+#endif
+}
+
+/* Set the thread context of the thread associated with TH. */
+
+static void
+win32_set_thread_context (win32_thread_info *th)
{
- win32_thread_info *th = inferior_target_data (current_inferior);
- return th->tid;
+#ifdef _WIN32_WCE
+ /* Calling SuspendThread on a thread that is running kernel code
+ will report that the suspending was successful, but in fact, that
+ will often not be true. In those cases, the context returned by
+ GetThreadContext will not be correct by the time the thread
+ stops, hence we can't set that context back into the thread when
+ resuming - it will most likelly crash the inferior.
+ Unfortunately, there is no way to know when the thread will
+ really stop. To work around it, we'll only write the context
+ back to the thread when either the user or GDB explicitly change
+ it between stopping and resuming. */
+ if (memcmp (&th->context, &th->base_context, sizeof (CONTEXT)) != 0)
+#endif
+ (*the_low_target.set_thread_context) (th, ¤t_event);
}
/* Find a thread record given a thread id. If GET_CONTEXT is set then
also retrieve the context for this thread. */
static win32_thread_info *
-thread_rec (DWORD id, int get_context)
+thread_rec (ptid_t ptid, int get_context)
{
struct thread_info *thread;
win32_thread_info *th;
- thread = (struct thread_info *) find_inferior_id (&all_threads, id);
+ thread = (struct thread_info *) find_inferior_id (&all_threads, ptid);
if (thread == NULL)
return NULL;
th = inferior_target_data (thread);
- if (!th->suspend_count && get_context)
+ if (get_context && th->context.ContextFlags == 0)
{
- if (id != current_event.dwThreadId)
- th->suspend_count = SuspendThread (th->h) + 1;
+ if (!th->suspended)
+ {
+ if (SuspendThread (th->h) == (DWORD) -1)
+ {
+ DWORD err = GetLastError ();
+ OUTMSG (("warning: SuspendThread failed in thread_rec, "
+ "(error %d): %s\n", (int) err, strwinerror (err)));
+ }
+ else
+ th->suspended = 1;
+ }
- (*the_low_target.get_thread_context) (th, ¤t_event);
+ win32_get_thread_context (th);
}
return th;
@@ -117,21 +179,22 @@ thread_rec (DWORD id, int get_context)
/* Add a thread to the thread list. */
static win32_thread_info *
-child_add_thread (DWORD tid, HANDLE h)
+child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
{
win32_thread_info *th;
+ ptid_t ptid = ptid_build (pid, tid, 0);
- if ((th = thread_rec (tid, FALSE)))
+ if ((th = thread_rec (ptid, FALSE)))
return th;
- th = (win32_thread_info *) malloc (sizeof (*th));
- memset (th, 0, sizeof (*th));
+ th = xcalloc (1, sizeof (*th));
th->tid = tid;
th->h = h;
+ th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb;
- add_thread (tid, th, (unsigned int) tid);
+ add_thread (ptid, th);
set_inferior_regcache_data ((struct thread_info *)
- find_inferior_id (&all_threads, tid),
+ find_inferior_id (&all_threads, ptid),
new_register_cache ());
if (the_low_target.thread_added != NULL)
@@ -153,28 +216,72 @@ delete_thread_info (struct inferior_list_entry *thread)
/* Delete a thread from the list of threads. */
static void
-child_delete_thread (DWORD id)
+child_delete_thread (DWORD pid, DWORD tid)
{
struct inferior_list_entry *thread;
+ ptid_t ptid;
/* If the last thread is exiting, just return. */
if (all_threads.head == all_threads.tail)
return;
- thread = find_inferior_id (&all_threads, id);
+ ptid = ptid_build (pid, tid, 0);
+ thread = find_inferior_id (&all_threads, ptid);
if (thread == NULL)
return;
delete_thread_info (thread);
}
+/* These watchpoint related wrapper functions simply pass on the function call
+ if the low target has registered a corresponding function. */
+
+static int
+win32_insert_point (char type, CORE_ADDR addr, int len)
+{
+ if (the_low_target.insert_point != NULL)
+ return the_low_target.insert_point (type, addr, len);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+win32_remove_point (char type, CORE_ADDR addr, int len)
+{
+ if (the_low_target.remove_point != NULL)
+ return the_low_target.remove_point (type, addr, len);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+win32_stopped_by_watchpoint (void)
+{
+ if (the_low_target.stopped_by_watchpoint != NULL)
+ return the_low_target.stopped_by_watchpoint ();
+ else
+ return 0;
+}
+
+static CORE_ADDR
+win32_stopped_data_address (void)
+{
+ if (the_low_target.stopped_data_address != NULL)
+ return the_low_target.stopped_data_address ();
+ else
+ return 0;
+}
+
+
/* Transfer memory from/to the debugged process. */
static int
child_xfer_memory (CORE_ADDR memaddr, char *our, int len,
int write, struct target_ops *target)
{
SIZE_T done;
- long addr = (long) memaddr;
+ uintptr_t addr = (uintptr_t) memaddr;
if (write)
{
@@ -190,46 +297,6 @@ child_xfer_memory (CORE_ADDR memaddr, char *our, int len,
return done;
}
-/* Generally, what has the program done? */
-enum target_waitkind
-{
- /* The program has exited. The exit status is in value.integer. */
- TARGET_WAITKIND_EXITED,
-
- /* The program has stopped with a signal. Which signal is in
- value.sig. */
- TARGET_WAITKIND_STOPPED,
-
- /* The program is letting us know that it dynamically loaded 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;
-};
-
/* Clear out any old thread list and reinitialize it to a pristine
state. */
static void
@@ -239,12 +306,20 @@ child_init_thread_list (void)
}
static void
-do_initial_child_stuff (DWORD pid)
+do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
{
last_sig = TARGET_SIGNAL_0;
+ current_process_handle = proch;
+ current_process_id = pid;
+ main_thread_id = 0;
+
+ soft_interrupt_requested = 0;
+ faked_breakpoint = 0;
+
memset (¤t_event, 0, sizeof (current_event));
+ add_process (pid, attached);
child_init_thread_list ();
if (the_low_target.initial_stuff != NULL)
@@ -259,20 +334,23 @@ continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr)
struct thread_info *thread = (struct thread_info *) this_thread;
int thread_id = * (int *) id_ptr;
win32_thread_info *th = inferior_target_data (thread);
- int i;
if ((thread_id == -1 || thread_id == th->tid)
- && th->suspend_count)
+ && th->suspended)
{
if (th->context.ContextFlags)
{
- (*the_low_target.set_thread_context) (th, ¤t_event);
+ win32_set_thread_context (th);
th->context.ContextFlags = 0;
}
- for (i = 0; i < th->suspend_count; i++)
- (void) ResumeThread (th->h);
- th->suspend_count = 0;
+ if (ResumeThread (th->h) == (DWORD) -1)
+ {
+ DWORD err = GetLastError ();
+ OUTMSG (("warning: ResumeThread failed in continue_one_thread, "
+ "(error %d): %s\n", (int) err, strwinerror (err)));
+ }
+ th->suspended = 0;
}
return 0;
@@ -281,41 +359,44 @@ continue_one_thread (struct inferior_list_entry *this_thread, void *id_ptr)
static BOOL
child_continue (DWORD continue_status, int thread_id)
{
- BOOL res;
+ /* The inferior will only continue after the ContinueDebugEvent
+ call. */
+ find_inferior (&all_threads, continue_one_thread, &thread_id);
+ faked_breakpoint = 0;
- res = ContinueDebugEvent (current_event.dwProcessId,
- current_event.dwThreadId, continue_status);
- if (res)
- find_inferior (&all_threads, continue_one_thread, &thread_id);
+ if (!ContinueDebugEvent (current_event.dwProcessId,
+ current_event.dwThreadId,
+ continue_status))
+ return FALSE;
- return res;
+ return TRUE;
}
/* Fetch register(s) from the current thread context. */
static void
-child_fetch_inferior_registers (int r)
+child_fetch_inferior_registers (struct regcache *regcache, int r)
{
int regno;
- win32_thread_info *th = thread_rec (current_inferior_tid (), TRUE);
- if (r == -1 || r == 0 || r > NUM_REGS)
- child_fetch_inferior_registers (NUM_REGS);
+ win32_thread_info *th = thread_rec (current_inferior_ptid (), TRUE);
+ if (r == -1 || r > NUM_REGS)
+ child_fetch_inferior_registers (regcache, NUM_REGS);
else
for (regno = 0; regno < r; regno++)
- (*the_low_target.fetch_inferior_register) (th, regno);
+ (*the_low_target.fetch_inferior_register) (regcache, th, regno);
}
/* Store a new register value into the current thread context. We don't
change the program's context until later, when we resume it. */
static void
-child_store_inferior_registers (int r)
+child_store_inferior_registers (struct regcache *regcache, int r)
{
int regno;
- win32_thread_info *th = thread_rec (current_inferior_tid (), TRUE);
+ win32_thread_info *th = thread_rec (current_inferior_ptid (), TRUE);
if (r == -1 || r == 0 || r > NUM_REGS)
- child_store_inferior_registers (NUM_REGS);
+ child_store_inferior_registers (regcache, NUM_REGS);
else
for (regno = 0; regno < r; regno++)
- (*the_low_target.store_inferior_register) (th, regno);
+ (*the_low_target.store_inferior_register) (regcache, th, regno);
}
/* Map the Windows error number in ERROR to a locale-dependent error
@@ -373,6 +454,55 @@ strwinerror (DWORD error)
return buf;
}
+static BOOL
+create_process (const char *program, char *args,
+ DWORD flags, PROCESS_INFORMATION *pi)
+{
+ BOOL ret;
+
+#ifdef _WIN32_WCE
+ wchar_t *p, *wprogram, *wargs;
+ size_t argslen;
+
+ wprogram = alloca ((strlen (program) + 1) * sizeof (wchar_t));
+ mbstowcs (wprogram, program, strlen (program) + 1);
+
+ for (p = wprogram; *p; ++p)
+ if (L'/' == *p)
+ *p = L'\\';
+
+ argslen = strlen (args);
+ wargs = alloca ((argslen + 1) * sizeof (wchar_t));
+ mbstowcs (wargs, args, argslen + 1);
+
+ ret = CreateProcessW (wprogram, /* image name */
+ wargs, /* command line */
+ NULL, /* security, not supported */
+ NULL, /* thread, not supported */
+ FALSE, /* inherit handles, not supported */
+ flags, /* start flags */
+ NULL, /* environment, not supported */
+ NULL, /* current directory, not supported */
+ NULL, /* start info, not supported */
+ pi); /* proc info */
+#else
+ STARTUPINFOA si = { sizeof (STARTUPINFOA) };
+
+ ret = CreateProcessA (program, /* image name */
+ args, /* command line */
+ NULL, /* security */
+ NULL, /* thread */
+ TRUE, /* inherit handles */
+ flags, /* start flags */
+ NULL, /* environment */
+ NULL, /* current directory */
+ &si, /* start info */
+ pi); /* proc info */
+#endif
+
+ return ret;
+}
+
/* Start a new process.
PROGRAM is a path to the program to execute.
ARGS is a standard NULL-terminated array of arguments,
@@ -392,12 +522,10 @@ win32_create_inferior (char *program, char **program_args)
int argslen;
int argc;
PROCESS_INFORMATION pi;
-#ifndef __MINGW32CE__
- STARTUPINFOA si = { sizeof (STARTUPINFOA) };
- char *winenv = NULL;
-#else
- wchar_t *wargs, *wprogram;
-#endif
+ DWORD err;
+
+ /* win32_wait needs to know we're not attaching. */
+ attaching = 0;
if (!program)
error ("No executable specified, specify executable to debug.\n");
@@ -409,13 +537,15 @@ win32_create_inferior (char *program, char **program_args)
path_ptr = getenv ("PATH");
if (path_ptr)
{
+ int size = cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, NULL, 0);
orig_path = alloca (strlen (path_ptr) + 1);
- new_path = alloca (cygwin_posix_to_win32_path_list_buf_size (path_ptr));
+ new_path = alloca (size);
strcpy (orig_path, path_ptr);
- cygwin_posix_to_win32_path_list (path_ptr, new_path);
+ cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, new_path, size);
setenv ("PATH", new_path, 1);
- }
- cygwin_conv_to_win32_path (program, real_path);
+ }
+ cygwin_conv_path (CCP_POSIX_TO_WIN_A, program, real_path,
+ MAXPATHLEN);
program = real_path;
#endif
@@ -427,7 +557,7 @@ win32_create_inferior (char *program, char **program_args)
for (argc = 1; program_args[argc]; argc++)
{
/* FIXME: Can we do better about quoting? How does Cygwin
- handle this? */
+ handle this? */
strcat (args, " ");
strcat (args, program_args[argc]);
}
@@ -437,34 +567,15 @@ win32_create_inferior (char *program, char **program_args)
flags |= CREATE_NEW_PROCESS_GROUP;
#endif
-#ifdef __MINGW32CE__
- to_back_slashes (program);
- wargs = alloca (argslen * sizeof (wchar_t));
- mbstowcs (wargs, args, argslen);
- wprogram = alloca ((strlen (program) + 1) * sizeof (wchar_t));
- mbstowcs (wprogram, program, strlen (program) + 1);
- ret = CreateProcessW (wprogram, /* image name */
- wargs, /* command line */
- NULL, /* security, not supported */
- NULL, /* thread, not supported */
- FALSE, /* inherit handles, not supported */
- flags, /* start flags */
- NULL, /* environment, not supported */
- NULL, /* current directory, not supported */
- NULL, /* start info, not supported */
- &pi); /* proc info */
-#else
- ret = CreateProcessA (program, /* image name */
- args, /* command line */
- NULL, /* security */
- NULL, /* thread */
- TRUE, /* inherit handles */
- flags, /* start flags */
- winenv, /* environment */
- NULL, /* current directory */
- &si, /* start info */
- &pi); /* proc info */
-#endif
+ ret = create_process (program, args, flags, &pi);
+ err = GetLastError ();
+ if (!ret && err == ERROR_FILE_NOT_FOUND)
+ {
+ char *exename = alloca (strlen (program) + 5);
+ strcat (strcpy (exename, program), ".exe");
+ ret = create_process (exename, args, flags, &pi);
+ err = GetLastError ();
+ }
#ifndef USE_WIN32API
if (orig_path)
@@ -473,7 +584,6 @@ win32_create_inferior (char *program, char **program_args)
if (!ret)
{
- DWORD err = GetLastError ();
error ("Error creating process \"%s%s\", (error %d): %s\n",
program, args, (int) err, strwinerror (err));
}
@@ -489,10 +599,7 @@ win32_create_inferior (char *program, char **program_args)
CloseHandle (pi.hThread);
#endif
- current_process_handle = pi.hProcess;
- current_process_id = pi.dwProcessId;
-
- do_initial_child_stuff (current_process_id);
+ do_initial_child_stuff (pi.hProcess, pi.dwProcessId, 0);
return current_process_id;
}
@@ -503,34 +610,36 @@ win32_create_inferior (char *program, char **program_args)
static int
win32_attach (unsigned long pid)
{
- winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL;
+ HANDLE h;
winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
+ DWORD err;
#ifdef _WIN32_WCE
HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
#else
HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
#endif
- DebugActiveProcessStop = GETPROCADDRESS (dll, DebugActiveProcessStop);
DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit);
- if (DebugActiveProcess (pid))
+ h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
+ if (h != NULL)
{
- if (DebugSetProcessKillOnExit != NULL)
- DebugSetProcessKillOnExit (FALSE);
-
- current_process_handle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
-
- if (current_process_handle != NULL)
- {
- current_process_id = pid;
- do_initial_child_stuff (pid);
- return 0;
- }
- if (DebugActiveProcessStop != NULL)
- DebugActiveProcessStop (current_process_id);
+ if (DebugActiveProcess (pid))
+ {
+ if (DebugSetProcessKillOnExit != NULL)
+ DebugSetProcessKillOnExit (FALSE);
+
+ /* win32_wait needs to know we're attaching. */
+ attaching = 1;
+ do_initial_child_stuff (h, pid, 1);
+ return 0;
+ }
+
+ CloseHandle (h);
}
- error ("Attach to process failed.");
+ err = GetLastError ();
+ error ("Attach to process failed (error %d): %s\n",
+ (int) err, strwinerror (err));
}
/* Handle OUTPUT_DEBUG_STRING_EVENT from child process. */
@@ -553,7 +662,7 @@ handle_output_debug_string (struct target_waitstatus *ourstatus)
if (current_event.u.DebugString.fUnicode)
{
/* The event tells us how many bytes, not chars, even
- in Unicode. */
+ in Unicode. */
WCHAR buffer[(READ_BUFFER_LEN + 1) / sizeof (WCHAR)] = { 0 };
if (read_inferior_memory (addr, (unsigned char *) buffer, nbytes) != 0)
return;
@@ -566,18 +675,36 @@ handle_output_debug_string (struct target_waitstatus *ourstatus)
}
if (strncmp (s, "cYg", 3) != 0)
- monitor_output (s);
+ {
+ if (!server_waiting)
+ {
+ OUTMSG2(("%s", s));
+ return;
+ }
+
+ monitor_output (s);
+ }
#undef READ_BUFFER_LEN
}
-/* Kill all inferiors. */
static void
-win32_kill (void)
+win32_clear_inferiors (void)
+{
+ if (current_process_handle != NULL)
+ CloseHandle (current_process_handle);
+
+ for_each_inferior (&all_threads, delete_thread_info);
+ clear_inferiors ();
+}
+
+/* Kill all inferiors. */
+static int
+win32_kill (int pid)
{
- win32_thread_info *current_thread;
+ struct process_info *process;
if (current_process_handle == NULL)
- return;
+ return -1;
TerminateProcess (current_process_handle, 0);
for (;;)
@@ -590,27 +717,23 @@ win32_kill (void)
break;
else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
{
- struct target_waitstatus our_status = { 0 };
+ struct target_waitstatus our_status = { 0 };
handle_output_debug_string (&our_status);
- }
+ }
}
- CloseHandle (current_process_handle);
+ win32_clear_inferiors ();
- current_thread = inferior_target_data (current_inferior);
- if (current_thread && current_thread->h)
- {
- /* This may fail in an attached process, so don't check. */
- (void) CloseHandle (current_thread->h);
- }
+ process = find_process_pid (pid);
+ remove_process (process);
+ return 0;
}
-/* Detach from all inferiors. */
+/* Detach from inferior PID. */
static int
-win32_detach (void)
+win32_detach (int pid)
{
- HANDLE h;
-
+ struct process_info *process;
winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL;
winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
#ifdef _WIN32_WCE
@@ -625,60 +748,52 @@ win32_detach (void)
|| DebugActiveProcessStop == NULL)
return -1;
- /* We need a new handle, since DebugActiveProcessStop
- closes all the ones that came through the events. */
- if ((h = OpenProcess (PROCESS_ALL_ACCESS,
- FALSE,
- current_process_id)) == NULL)
- {
- /* The process died. */
- return -1;
- }
-
{
struct thread_resume resume;
- resume.thread = -1;
- resume.step = 0;
+ resume.thread = minus_one_ptid;
+ resume.kind = resume_continue;
resume.sig = 0;
- resume.leave_stopped = 0;
- win32_resume (&resume);
+ win32_resume (&resume, 1);
}
if (!DebugActiveProcessStop (current_process_id))
- {
- CloseHandle (h);
- return -1;
- }
+ return -1;
+
DebugSetProcessKillOnExit (FALSE);
+ process = find_process_pid (pid);
+ remove_process (process);
- current_process_handle = h;
+ win32_clear_inferiors ();
return 0;
}
-/* Wait for inferiors to end. */
static void
-win32_join (void)
+win32_mourn (struct process_info *process)
{
- if (current_process_id == 0
- || current_process_handle == NULL)
- return;
-
- WaitForSingleObject (current_process_handle, INFINITE);
- CloseHandle (current_process_handle);
+ remove_process (process);
+}
- current_process_handle = NULL;
- current_process_id = 0;
+/* Wait for inferiors to end. */
+static void
+win32_join (int pid)
+{
+ HANDLE h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
+ if (h != NULL)
+ {
+ WaitForSingleObject (h, INFINITE);
+ CloseHandle (h);
+ }
}
/* Return 1 iff the thread with thread ID TID is alive. */
static int
-win32_thread_alive (unsigned long tid)
+win32_thread_alive (ptid_t ptid)
{
int res;
/* Our thread list is reliable; don't bother to poll target
threads. */
- if (find_inferior_id (&all_threads, tid) != NULL)
+ if (find_inferior_id (&all_threads, ptid) != NULL)
res = 1;
else
res = 0;
@@ -688,30 +803,31 @@ win32_thread_alive (unsigned long tid)
/* Resume the inferior process. RESUME_INFO describes how we want
to resume. */
static void
-win32_resume (struct thread_resume *resume_info)
+win32_resume (struct thread_resume *resume_info, size_t n)
{
DWORD tid;
enum target_signal sig;
int step;
win32_thread_info *th;
DWORD continue_status = DBG_CONTINUE;
+ ptid_t ptid;
/* This handles the very limited set of resume packets that GDB can
currently produce. */
- if (resume_info[0].thread == -1)
+ if (n == 1 && ptid_equal (resume_info[0].thread, minus_one_ptid))
tid = -1;
- else if (resume_info[1].thread == -1 && !resume_info[1].leave_stopped)
+ else if (n > 1)
tid = -1;
else
/* Yes, we're ignoring resume_info[0].thread. It'd be tricky to make
the Windows resume code do the right thing for thread switching. */
tid = current_event.dwThreadId;
- if (resume_info[0].thread != -1)
+ if (!ptid_equal (resume_info[0].thread, minus_one_ptid))
{
sig = resume_info[0].sig;
- step = resume_info[0].step;
+ step = resume_info[0].kind == resume_step;
}
else
{
@@ -734,7 +850,8 @@ win32_resume (struct thread_resume *resume_info)
last_sig = TARGET_SIGNAL_0;
/* Get context for the currently selected thread. */
- th = thread_rec (current_event.dwThreadId, FALSE);
+ ptid = debug_event_ptid (¤t_event);
+ th = thread_rec (ptid, FALSE);
if (th)
{
if (th->context.ContextFlags)
@@ -752,7 +869,7 @@ win32_resume (struct thread_resume *resume_info)
"in this configuration.\n");
}
- (*the_low_target.set_thread_context) (th, ¤t_event);
+ win32_set_thread_context (th);
th->context.ContextFlags = 0;
}
}
@@ -763,6 +880,347 @@ win32_resume (struct thread_resume *resume_info)
child_continue (continue_status, tid);
}
+static void
+win32_add_one_solib (const char *name, CORE_ADDR load_addr)
+{
+ char buf[MAX_PATH + 1];
+ char buf2[MAX_PATH + 1];
+
+#ifdef _WIN32_WCE
+ WIN32_FIND_DATA w32_fd;
+ WCHAR wname[MAX_PATH + 1];
+ mbstowcs (wname, name, MAX_PATH);
+ HANDLE h = FindFirstFile (wname, &w32_fd);
+#else
+ WIN32_FIND_DATAA w32_fd;
+ HANDLE h = FindFirstFileA (name, &w32_fd);
+#endif
+
+ if (h == INVALID_HANDLE_VALUE)
+ strcpy (buf, name);
+ else
+ {
+ FindClose (h);
+ strcpy (buf, name);
+#ifndef _WIN32_WCE
+ {
+ char cwd[MAX_PATH + 1];
+ char *p;
+ if (GetCurrentDirectoryA (MAX_PATH + 1, cwd))
+ {
+ p = strrchr (buf, '\\');
+ if (p)
+ p[1] = '\0';
+ SetCurrentDirectoryA (buf);
+ GetFullPathNameA (w32_fd.cFileName, MAX_PATH, buf, &p);
+ SetCurrentDirectoryA (cwd);
+ }
+ }
+#endif
+ }
+
+#ifndef _WIN32_WCE
+ if (strcasecmp (buf, "ntdll.dll") == 0)
+ {
+ GetSystemDirectoryA (buf, sizeof (buf));
+ strcat (buf, "\\ntdll.dll");
+ }
+#endif
+
+#ifdef __CYGWIN__
+ cygwin_conv_path (CCP_WIN_A_TO_POSIX, buf, buf2, sizeof (buf2));
+#else
+ strcpy (buf2, buf);
+#endif
+
+ loaded_dll (buf2, load_addr);
+}
+
+static char *
+get_image_name (HANDLE h, void *address, int unicode)
+{
+ static char buf[(2 * MAX_PATH) + 1];
+ DWORD size = unicode ? sizeof (WCHAR) : sizeof (char);
+ char *address_ptr;
+ int len = 0;
+ char b[2];
+ SIZE_T done;
+
+ /* Attempt to read the name of the dll that was detected.
+ This is documented to work only when actively debugging
+ a program. It will not work for attached processes. */
+ if (address == NULL)
+ return NULL;
+
+#ifdef _WIN32_WCE
+ /* Windows CE reports the address of the image name,
+ instead of an address of a pointer into the image name. */
+ address_ptr = address;
+#else
+ /* See if we could read the address of a string, and that the
+ address isn't null. */
+ if (!ReadProcessMemory (h, address, &address_ptr,
+ sizeof (address_ptr), &done)
+ || done != sizeof (address_ptr)
+ || !address_ptr)
+ return NULL;
+#endif
+
+ /* Find the length of the string */
+ while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done)
+ && (b[0] != 0 || b[size - 1] != 0) && done == size)
+ continue;
+
+ if (!unicode)
+ ReadProcessMemory (h, address_ptr, buf, len, &done);
+ else
+ {
+ WCHAR *unicode_address = (WCHAR *) alloca (len * sizeof (WCHAR));
+ ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR),
+ &done);
+
+ WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0);
+ }
+
+ return buf;
+}
+
+typedef BOOL (WINAPI *winapi_EnumProcessModules) (HANDLE, HMODULE *,
+ DWORD, LPDWORD);
+typedef BOOL (WINAPI *winapi_GetModuleInformation) (HANDLE, HMODULE,
+ LPMODULEINFO, DWORD);
+typedef DWORD (WINAPI *winapi_GetModuleFileNameExA) (HANDLE, HMODULE,
+ LPSTR, DWORD);
+
+static winapi_EnumProcessModules win32_EnumProcessModules;
+static winapi_GetModuleInformation win32_GetModuleInformation;
+static winapi_GetModuleFileNameExA win32_GetModuleFileNameExA;
+
+static BOOL
+load_psapi (void)
+{
+ static int psapi_loaded = 0;
+ static HMODULE dll = NULL;
+
+ if (!psapi_loaded)
+ {
+ psapi_loaded = 1;
+ dll = LoadLibrary (TEXT("psapi.dll"));
+ if (!dll)
+ return FALSE;
+ win32_EnumProcessModules =
+ GETPROCADDRESS (dll, EnumProcessModules);
+ win32_GetModuleInformation =
+ GETPROCADDRESS (dll, GetModuleInformation);
+ win32_GetModuleFileNameExA =
+ GETPROCADDRESS (dll, GetModuleFileNameExA);
+ }
+
+ return (win32_EnumProcessModules != NULL
+ && win32_GetModuleInformation != NULL
+ && win32_GetModuleFileNameExA != NULL);
+}
+
+static int
+psapi_get_dll_name (LPVOID BaseAddress, char *dll_name_ret)
+{
+ DWORD len;
+ MODULEINFO mi;
+ size_t i;
+ HMODULE dh_buf[1];
+ HMODULE *DllHandle = dh_buf;
+ DWORD cbNeeded;
+ BOOL ok;
+
+ if (!load_psapi ())
+ goto failed;
+
+ cbNeeded = 0;
+ ok = (*win32_EnumProcessModules) (current_process_handle,
+ DllHandle,
+ sizeof (HMODULE),
+ &cbNeeded);
+
+ if (!ok || !cbNeeded)
+ goto failed;
+
+ DllHandle = (HMODULE *) alloca (cbNeeded);
+ if (!DllHandle)
+ goto failed;
+
+ ok = (*win32_EnumProcessModules) (current_process_handle,
+ DllHandle,
+ cbNeeded,
+ &cbNeeded);
+ if (!ok)
+ goto failed;
+
+ for (i = 0; i < ((size_t) cbNeeded / sizeof (HMODULE)); i++)
+ {
+ if (!(*win32_GetModuleInformation) (current_process_handle,
+ DllHandle[i],
+ &mi,
+ sizeof (mi)))
+ {
+ DWORD err = GetLastError ();
+ error ("Can't get module info: (error %d): %s\n",
+ (int) err, strwinerror (err));
+ }
+
+ if (mi.lpBaseOfDll == BaseAddress)
+ {
+ len = (*win32_GetModuleFileNameExA) (current_process_handle,
+ DllHandle[i],
+ dll_name_ret,
+ MAX_PATH);
+ if (len == 0)
+ {
+ DWORD err = GetLastError ();
+ error ("Error getting dll name: (error %d): %s\n",
+ (int) err, strwinerror (err));
+ }
+ return 1;
+ }
+ }
+
+failed:
+ dll_name_ret[0] = '\0';
+ return 0;
+}
+
+typedef HANDLE (WINAPI *winapi_CreateToolhelp32Snapshot) (DWORD, DWORD);
+typedef BOOL (WINAPI *winapi_Module32First) (HANDLE, LPMODULEENTRY32);
+typedef BOOL (WINAPI *winapi_Module32Next) (HANDLE, LPMODULEENTRY32);
+
+static winapi_CreateToolhelp32Snapshot win32_CreateToolhelp32Snapshot;
+static winapi_Module32First win32_Module32First;
+static winapi_Module32Next win32_Module32Next;
+#ifdef _WIN32_WCE
+typedef BOOL (WINAPI *winapi_CloseToolhelp32Snapshot) (HANDLE);
+static winapi_CloseToolhelp32Snapshot win32_CloseToolhelp32Snapshot;
+#endif
+
+static BOOL
+load_toolhelp (void)
+{
+ static int toolhelp_loaded = 0;
+ static HMODULE dll = NULL;
+
+ if (!toolhelp_loaded)
+ {
+ toolhelp_loaded = 1;
+#ifndef _WIN32_WCE
+ dll = GetModuleHandle (_T("KERNEL32.DLL"));
+#else
+ dll = LoadLibrary (L"TOOLHELP.DLL");
+#endif
+ if (!dll)
+ return FALSE;
+
+ win32_CreateToolhelp32Snapshot =
+ GETPROCADDRESS (dll, CreateToolhelp32Snapshot);
+ win32_Module32First = GETPROCADDRESS (dll, Module32First);
+ win32_Module32Next = GETPROCADDRESS (dll, Module32Next);
+#ifdef _WIN32_WCE
+ win32_CloseToolhelp32Snapshot =
+ GETPROCADDRESS (dll, CloseToolhelp32Snapshot);
+#endif
+ }
+
+ return (win32_CreateToolhelp32Snapshot != NULL
+ && win32_Module32First != NULL
+ && win32_Module32Next != NULL
+#ifdef _WIN32_WCE
+ && win32_CloseToolhelp32Snapshot != NULL
+#endif
+ );
+}
+
+static int
+toolhelp_get_dll_name (LPVOID BaseAddress, char *dll_name_ret)
+{
+ HANDLE snapshot_module;
+ MODULEENTRY32 modEntry = { sizeof (MODULEENTRY32) };
+ int found = 0;
+
+ if (!load_toolhelp ())
+ return 0;
+
+ snapshot_module = win32_CreateToolhelp32Snapshot (TH32CS_SNAPMODULE,
+ current_event.dwProcessId);
+ if (snapshot_module == INVALID_HANDLE_VALUE)
+ return 0;
+
+ /* Ignore the first module, which is the exe. */
+ if (win32_Module32First (snapshot_module, &modEntry))
+ while (win32_Module32Next (snapshot_module, &modEntry))
+ if (modEntry.modBaseAddr == BaseAddress)
+ {
+#ifdef UNICODE
+ wcstombs (dll_name_ret, modEntry.szExePath, MAX_PATH + 1);
+#else
+ strcpy (dll_name_ret, modEntry.szExePath);
+#endif
+ found = 1;
+ break;
+ }
+
+#ifdef _WIN32_WCE
+ win32_CloseToolhelp32Snapshot (snapshot_module);
+#else
+ CloseHandle (snapshot_module);
+#endif
+ return found;
+}
+
+static void
+handle_load_dll (void)
+{
+ LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll;
+ char dll_buf[MAX_PATH + 1];
+ char *dll_name = NULL;
+ CORE_ADDR load_addr;
+
+ dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0';
+
+ /* Windows does not report the image name of the dlls in the debug
+ event on attaches. We resort to iterating over the list of
+ loaded dlls looking for a match by image base. */
+ if (!psapi_get_dll_name (event->lpBaseOfDll, dll_buf))
+ {
+ if (!server_waiting)
+ /* On some versions of Windows and Windows CE, we can't create
+ toolhelp snapshots while the inferior is stopped in a
+ LOAD_DLL_DEBUG_EVENT due to a dll load, but we can while
+ Windows is reporting the already loaded dlls. */
+ toolhelp_get_dll_name (event->lpBaseOfDll, dll_buf);
+ }
+
+ dll_name = dll_buf;
+
+ if (*dll_name == '\0')
+ dll_name = get_image_name (current_process_handle,
+ event->lpImageName, event->fUnicode);
+ if (!dll_name)
+ return;
+
+ /* The symbols in a dll are offset by 0x1000, which is the
+ offset from 0 of the first byte in an image - because
+ of the file header and the section alignment. */
+
+ load_addr = (CORE_ADDR) (uintptr_t) event->lpBaseOfDll + 0x1000;
+ win32_add_one_solib (dll_name, load_addr);
+}
+
+static void
+handle_unload_dll (void)
+{
+ CORE_ADDR load_addr =
+ (CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll;
+ load_addr += 0x1000;
+ unloaded_dll (NULL, load_addr);
+}
+
static void
handle_exception (struct target_waitstatus *ourstatus)
{
@@ -826,7 +1284,7 @@ handle_exception (struct target_waitstatus *ourstatus)
#ifdef _WIN32_WCE
/* Remove the initial breakpoint. */
check_breakpoints ((CORE_ADDR) (long) current_event
- .u.Exception.ExceptionRecord.ExceptionAddress);
+ .u.Exception.ExceptionRecord.ExceptionAddress);
#endif
break;
case DBG_CONTROL_C:
@@ -859,10 +1317,10 @@ handle_exception (struct target_waitstatus *ourstatus)
ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
return;
}
- OUTMSG2 (("gdbserver: unknown target exception 0x%08lx at 0x%08lx",
+ OUTMSG2 (("gdbserver: unknown target exception 0x%08lx at 0x%s",
current_event.u.Exception.ExceptionRecord.ExceptionCode,
- (DWORD) current_event.u.Exception.ExceptionRecord.
- ExceptionAddress));
+ phex_nz ((uintptr_t) current_event.u.Exception.ExceptionRecord.
+ ExceptionAddress, sizeof (uintptr_t))));
ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN;
break;
}
@@ -870,21 +1328,125 @@ handle_exception (struct target_waitstatus *ourstatus)
last_sig = ourstatus->value.sig;
}
-/* Get the next event from the child. */
+
static void
+suspend_one_thread (struct inferior_list_entry *entry)
+{
+ struct thread_info *thread = (struct thread_info *) entry;
+ win32_thread_info *th = inferior_target_data (thread);
+
+ if (!th->suspended)
+ {
+ if (SuspendThread (th->h) == (DWORD) -1)
+ {
+ DWORD err = GetLastError ();
+ OUTMSG (("warning: SuspendThread failed in suspend_one_thread, "
+ "(error %d): %s\n", (int) err, strwinerror (err)));
+ }
+ else
+ th->suspended = 1;
+ }
+}
+
+static void
+fake_breakpoint_event (void)
+{
+ OUTMSG2(("fake_breakpoint_event\n"));
+
+ faked_breakpoint = 1;
+
+ memset (¤t_event, 0, sizeof (current_event));
+ current_event.dwThreadId = main_thread_id;
+ current_event.dwDebugEventCode = EXCEPTION_DEBUG_EVENT;
+ current_event.u.Exception.ExceptionRecord.ExceptionCode
+ = EXCEPTION_BREAKPOINT;
+
+ for_each_inferior (&all_threads, suspend_one_thread);
+}
+
+#ifdef _WIN32_WCE
+static int
+auto_delete_breakpoint (CORE_ADDR stop_pc)
+{
+ return 1;
+}
+#endif
+
+/* Get the next event from the child. */
+
+static int
get_child_debug_event (struct target_waitstatus *ourstatus)
{
- BOOL debug_event;
+ ptid_t ptid;
last_sig = TARGET_SIGNAL_0;
ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
- if (!(debug_event = WaitForDebugEvent (¤t_event, 1000)))
- return;
+ /* Check if GDB sent us an interrupt request. */
+ check_remote_input_interrupt_request ();
- current_inferior =
- (struct thread_info *) find_inferior_id (&all_threads,
- current_event.dwThreadId);
+ if (soft_interrupt_requested)
+ {
+ soft_interrupt_requested = 0;
+ fake_breakpoint_event ();
+ goto gotevent;
+ }
+
+#ifndef _WIN32_WCE
+ attaching = 0;
+#else
+ if (attaching)
+ {
+ /* WinCE doesn't set an initial breakpoint automatically. To
+ stop the inferior, we flush all currently pending debug
+ events -- the thread list and the dll list are always
+ reported immediatelly without delay, then, we suspend all
+ threads and pretend we saw a trap at the current PC of the
+ main thread.
+
+ Contrary to desktop Windows, Windows CE *does* report the dll
+ names on LOAD_DLL_DEBUG_EVENTs resulting from a
+ DebugActiveProcess call. This limits the way we can detect
+ if all the dlls have already been reported. If we get a real
+ debug event before leaving attaching, the worst that will
+ happen is the user will see a spurious breakpoint. */
+
+ current_event.dwDebugEventCode = 0;
+ if (!WaitForDebugEvent (¤t_event, 0))
+ {
+ OUTMSG2(("no attach events left\n"));
+ fake_breakpoint_event ();
+ attaching = 0;
+ }
+ else
+ OUTMSG2(("got attach event\n"));
+ }
+ else
+#endif
+ {
+ /* Keep the wait time low enough for confortable remote
+ interruption, but high enough so gdbserver doesn't become a
+ bottleneck. */
+ if (!WaitForDebugEvent (¤t_event, 250))
+ {
+ DWORD e = GetLastError();
+
+ if (e == ERROR_PIPE_NOT_CONNECTED)
+ {
+ /* This will happen if the loader fails to succesfully
+ load the application, e.g., if the main executable
+ tries to pull in a non-existing export from a
+ DLL. */
+ ourstatus->kind = TARGET_WAITKIND_EXITED;
+ ourstatus->value.integer = 1;
+ return 1;
+ }
+
+ return 0;
+ }
+ }
+
+ gotevent:
switch (current_event.dwDebugEventCode)
{
@@ -895,8 +1457,10 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
(unsigned) current_event.dwThreadId));
/* Record the existence of this thread. */
- child_add_thread (current_event.dwThreadId,
- current_event.u.CreateThread.hThread);
+ child_add_thread (current_event.dwProcessId,
+ current_event.dwThreadId,
+ current_event.u.CreateThread.hThread,
+ current_event.u.CreateThread.lpThreadLocalBase);
break;
case EXIT_THREAD_DEBUG_EVENT:
@@ -904,8 +1468,11 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
"for pid=%d tid=%x\n",
(unsigned) current_event.dwProcessId,
(unsigned) current_event.dwThreadId));
- child_delete_thread (current_event.dwThreadId);
- break;
+ child_delete_thread (current_event.dwProcessId,
+ current_event.dwThreadId);
+
+ current_inferior = (struct thread_info *) all_threads.head;
+ return 1;
case CREATE_PROCESS_DEBUG_EVENT:
OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT "
@@ -921,17 +1488,23 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
ourstatus->value.execd_pathname = "Main executable";
/* Add the main thread. */
- child_add_thread (main_thread_id,
- current_event.u.CreateProcessInfo.hThread);
+ child_add_thread (current_event.dwProcessId,
+ main_thread_id,
+ current_event.u.CreateProcessInfo.hThread,
+ current_event.u.CreateProcessInfo.lpThreadLocalBase);
- ourstatus->value.related_pid = current_event.dwThreadId;
+ ourstatus->value.related_pid = debug_event_ptid (¤t_event);
#ifdef _WIN32_WCE
- /* Windows CE doesn't set the initial breakpoint automatically
- like the desktop versions of Windows do. We add it explicitly
- here. It will be removed as soon as it is hit. */
- set_breakpoint_at ((CORE_ADDR) (long) current_event.u
- .CreateProcessInfo.lpStartAddress,
- delete_breakpoint_at);
+ if (!attaching)
+ {
+ /* Windows CE doesn't set the initial breakpoint
+ automatically like the desktop versions of Windows do.
+ We add it explicitly here. It will be removed as soon as
+ it is hit. */
+ set_breakpoint_at ((CORE_ADDR) (long) current_event.u
+ .CreateProcessInfo.lpStartAddress,
+ auto_delete_breakpoint);
+ }
#endif
break;
@@ -942,6 +1515,7 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
(unsigned) current_event.dwThreadId));
ourstatus->kind = TARGET_WAITKIND_EXITED;
ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode;
+ child_continue (DBG_CONTINUE, -1);
CloseHandle (current_process_handle);
current_process_handle = NULL;
break;
@@ -952,9 +1526,10 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
(unsigned) current_event.dwProcessId,
(unsigned) current_event.dwThreadId));
CloseHandle (current_event.u.LoadDll.hFile);
+ handle_load_dll ();
ourstatus->kind = TARGET_WAITKIND_LOADED;
- ourstatus->value.integer = 0;
+ ourstatus->value.sig = TARGET_SIGNAL_TRAP;
break;
case UNLOAD_DLL_DEBUG_EVENT:
@@ -962,6 +1537,9 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
"for pid=%d tid=%x\n",
(unsigned) current_event.dwProcessId,
(unsigned) current_event.dwThreadId));
+ handle_unload_dll ();
+ ourstatus->kind = TARGET_WAITKIND_LOADED;
+ ourstatus->value.sig = TARGET_SIGNAL_TRAP;
break;
case EXCEPTION_DEBUG_EVENT:
@@ -990,51 +1568,60 @@ get_child_debug_event (struct target_waitstatus *ourstatus)
break;
}
+ ptid = debug_event_ptid (¤t_event);
current_inferior =
- (struct thread_info *) find_inferior_id (&all_threads,
- current_event.dwThreadId);
+ (struct thread_info *) find_inferior_id (&all_threads, ptid);
+ return 1;
}
/* Wait for the inferior process to change state.
STATUS will be filled in with a response code to send to GDB.
Returns the signal which caused the process to stop. */
-static unsigned char
-win32_wait (char *status)
+static ptid_t
+win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
{
- struct target_waitstatus our_status;
-
- *status = 'T';
+ struct regcache *regcache;
while (1)
{
- get_child_debug_event (&our_status);
+ if (!get_child_debug_event (ourstatus))
+ continue;
- switch (our_status.kind)
+ switch (ourstatus->kind)
{
case TARGET_WAITKIND_EXITED:
OUTMSG2 (("Child exited with retcode = %x\n",
- our_status.value.integer));
-
- *status = 'W';
-
- child_fetch_inferior_registers (-1);
-
- return our_status.value.integer;
+ ourstatus->value.integer));
+ win32_clear_inferiors ();
+ return pid_to_ptid (current_event.dwProcessId);
case TARGET_WAITKIND_STOPPED:
+ case TARGET_WAITKIND_LOADED:
OUTMSG2 (("Child Stopped with signal = %d \n",
- our_status.value.sig));
+ ourstatus->value.sig));
- *status = 'T';
+ regcache = get_thread_regcache (current_inferior, 1);
+ child_fetch_inferior_registers (regcache, -1);
- child_fetch_inferior_registers (-1);
+ if (ourstatus->kind == TARGET_WAITKIND_LOADED
+ && !server_waiting)
+ {
+ /* When gdb connects, we want to be stopped at the
+ initial breakpoint, not in some dll load event. */
+ child_continue (DBG_CONTINUE, -1);
+ break;
+ }
- return our_status.value.sig;
- default:
- OUTMSG (("Ignoring unknown internal event, %d\n", our_status.kind));
- /* fall-through */
- case TARGET_WAITKIND_SPURIOUS:
- case TARGET_WAITKIND_LOADED:
- case TARGET_WAITKIND_EXECD:
+ /* We don't expose _LOADED events to gdbserver core. See
+ the `dlls_changed' global. */
+ if (ourstatus->kind == TARGET_WAITKIND_LOADED)
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+
+ return debug_event_ptid (¤t_event);
+ default:
+ OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind));
+ /* fall-through */
+ case TARGET_WAITKIND_SPURIOUS:
+ case TARGET_WAITKIND_EXECD:
/* do nothing, just continue */
child_continue (DBG_CONTINUE, -1);
break;
@@ -1045,17 +1632,17 @@ win32_wait (char *status)
/* Fetch registers from the inferior process.
If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */
static void
-win32_fetch_inferior_registers (int regno)
+win32_fetch_inferior_registers (struct regcache *regcache, int regno)
{
- child_fetch_inferior_registers (regno);
+ child_fetch_inferior_registers (regcache, regno);
}
/* Store registers to the inferior process.
If REGNO is -1, store all registers; otherwise, store at least REGNO. */
static void
-win32_store_inferior_registers (int regno)
+win32_store_inferior_registers (struct regcache *regcache, int regno)
{
- child_store_inferior_registers (regno);
+ child_store_inferior_registers (regcache, regno);
}
/* Read memory from the inferior process. This should generally be
@@ -1078,10 +1665,111 @@ win32_write_inferior_memory (CORE_ADDR memaddr, const unsigned char *myaddr,
return child_xfer_memory (memaddr, (char *) myaddr, len, 1, 0) != len;
}
-static const char *
-win32_arch_string (void)
+/* Send an interrupt request to the inferior process. */
+static void
+win32_request_interrupt (void)
{
- return the_low_target.arch_string;
+ winapi_DebugBreakProcess DebugBreakProcess;
+ winapi_GenerateConsoleCtrlEvent GenerateConsoleCtrlEvent;
+
+#ifdef _WIN32_WCE
+ HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
+#else
+ HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
+#endif
+
+ GenerateConsoleCtrlEvent = GETPROCADDRESS (dll, GenerateConsoleCtrlEvent);
+
+ if (GenerateConsoleCtrlEvent != NULL
+ && GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, current_process_id))
+ return;
+
+ /* GenerateConsoleCtrlEvent can fail if process id being debugged is
+ not a process group id.
+ Fallback to XP/Vista 'DebugBreakProcess', which generates a
+ breakpoint exception in the interior process. */
+
+ DebugBreakProcess = GETPROCADDRESS (dll, DebugBreakProcess);
+
+ if (DebugBreakProcess != NULL
+ && DebugBreakProcess (current_process_handle))
+ return;
+
+ /* Last resort, suspend all threads manually. */
+ soft_interrupt_requested = 1;
+}
+
+#ifdef _WIN32_WCE
+int
+win32_error_to_fileio_error (DWORD err)
+{
+ switch (err)
+ {
+ case ERROR_BAD_PATHNAME:
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_INVALID_NAME:
+ case ERROR_PATH_NOT_FOUND:
+ return FILEIO_ENOENT;
+ case ERROR_CRC:
+ case ERROR_IO_DEVICE:
+ case ERROR_OPEN_FAILED:
+ return FILEIO_EIO;
+ case ERROR_INVALID_HANDLE:
+ return FILEIO_EBADF;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_SHARING_VIOLATION:
+ return FILEIO_EACCES;
+ case ERROR_NOACCESS:
+ return FILEIO_EFAULT;
+ case ERROR_BUSY:
+ return FILEIO_EBUSY;
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ return FILEIO_EEXIST;
+ case ERROR_BAD_DEVICE:
+ return FILEIO_ENODEV;
+ case ERROR_DIRECTORY:
+ return FILEIO_ENOTDIR;
+ case ERROR_FILENAME_EXCED_RANGE:
+ case ERROR_INVALID_DATA:
+ case ERROR_INVALID_PARAMETER:
+ case ERROR_NEGATIVE_SEEK:
+ return FILEIO_EINVAL;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ return FILEIO_EMFILE;
+ case ERROR_HANDLE_DISK_FULL:
+ case ERROR_DISK_FULL:
+ return FILEIO_ENOSPC;
+ case ERROR_WRITE_PROTECT:
+ return FILEIO_EROFS;
+ case ERROR_NOT_SUPPORTED:
+ return FILEIO_ENOSYS;
+ }
+
+ return FILEIO_EUNKNOWN;
+}
+
+static void
+wince_hostio_last_error (char *buf)
+{
+ DWORD winerr = GetLastError ();
+ int fileio_err = win32_error_to_fileio_error (winerr);
+ sprintf (buf, "F-1,%x", fileio_err);
+}
+#endif
+
+/* Write Windows OS Thread Information Block address. */
+
+static int
+win32_get_tib_address (ptid_t ptid, CORE_ADDR *addr)
+{
+ win32_thread_info *th;
+ th = thread_rec (ptid, 0);
+ if (th == NULL)
+ return 0;
+ if (addr != NULL)
+ *addr = th->thread_local_base;
+ return 1;
}
static struct target_ops win32_target_ops = {
@@ -1089,24 +1777,47 @@ static struct target_ops win32_target_ops = {
win32_attach,
win32_kill,
win32_detach,
+ win32_mourn,
win32_join,
win32_thread_alive,
win32_resume,
win32_wait,
win32_fetch_inferior_registers,
win32_store_inferior_registers,
+ NULL, /* prepare_to_access_memory */
+ NULL, /* done_accessing_memory */
win32_read_inferior_memory,
win32_write_inferior_memory,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- win32_arch_string
+ NULL, /* lookup_symbols */
+ win32_request_interrupt,
+ NULL, /* read_auxv */
+ win32_insert_point,
+ win32_remove_point,
+ win32_stopped_by_watchpoint,
+ win32_stopped_data_address,
+ NULL, /* read_offsets */
+ NULL, /* get_tls_address */
+ NULL, /* qxfer_spu */
+#ifdef _WIN32_WCE
+ wince_hostio_last_error,
+#else
+ hostio_last_error_from_errno,
+#endif
+ NULL, /* qxfer_osdata */
+ NULL, /* qxfer_siginfo */
+ NULL, /* supports_non_stop */
+ NULL, /* async */
+ NULL, /* start_non_stop */
+ NULL, /* supports_multi_process */
+ NULL, /* handle_monitor_command */
+ NULL, /* core_of_thread */
+ NULL, /* read_loadmap */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* read_pc */
+ NULL, /* write_pc */
+ NULL, /* thread_stopped */
+ win32_get_tib_address
};
/* Initialize the Win32 backend. */
@@ -1117,5 +1828,5 @@ initialize_low (void)
if (the_low_target.breakpoint != NULL)
set_breakpoint_data (the_low_target.breakpoint,
the_low_target.breakpoint_len);
- init_registers ();
+ the_low_target.arch_setup ();
}