summaryrefslogtreecommitdiff
path: root/gdbserver/win32-low.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gdbserver/win32-low.cc')
-rw-r--r--gdbserver/win32-low.cc1913
1 files changed, 1913 insertions, 0 deletions
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc
new file mode 100644
index 00000000000..557c90d97c6
--- /dev/null
+++ b/gdbserver/win32-low.cc
@@ -0,0 +1,1913 @@
+/* Low level interface to Windows debugging, for gdbserver.
+ Copyright (C) 2006-2020 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 3 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, see <http://www.gnu.org/licenses/>. */
+
+#include "server.h"
+#include "regcache.h"
+#include "gdb/fileio.h"
+#include "mem-break.h"
+#include "win32-low.h"
+#include "gdbthread.h"
+#include "dll.h"
+#include "hostio.h"
+#include <windows.h>
+#include <winnt.h>
+#include <imagehlp.h>
+#include <tlhelp32.h>
+#include <psapi.h>
+#include <process.h>
+#include "gdbsupport/gdb_tilde_expand.h"
+#include "gdbsupport/common-inferior.h"
+#include "gdbsupport/gdb_wait.h"
+
+#ifndef USE_WIN32API
+#include <sys/cygwin.h>
+#endif
+
+#define OUTMSG(X) do { printf X; fflush (stderr); } while (0)
+
+#define OUTMSG2(X) \
+ do \
+ { \
+ if (debug_threads) \
+ { \
+ printf X; \
+ fflush (stderr); \
+ } \
+ } while (0)
+
+#ifndef _T
+#define _T(x) TEXT (x)
+#endif
+
+#ifndef COUNTOF
+#define COUNTOF(STR) (sizeof (STR) / sizeof ((STR)[0]))
+#endif
+
+#ifdef _WIN32_WCE
+# define GETPROCADDRESS(DLL, PROC) \
+ ((winapi_ ## PROC) GetProcAddress (DLL, TEXT (#PROC)))
+#else
+# define GETPROCADDRESS(DLL, PROC) \
+ ((winapi_ ## PROC) GetProcAddress (DLL, #PROC))
+#endif
+
+int using_threads = 1;
+
+/* Globals. */
+static int attaching = 0;
+static HANDLE current_process_handle = NULL;
+static DWORD current_process_id = 0;
+static DWORD main_thread_id = 0;
+static EXCEPTION_RECORD siginfo_er; /* Contents of $_siginfo */
+static enum gdb_signal last_sig = GDB_SIGNAL_0;
+
+/* The current debug event from WaitForDebugEvent. */
+static DEBUG_EVENT current_event;
+
+/* A status that hasn't been reported to the core yet, and so
+ win32_wait should return it next, instead of fetching the next
+ debug event off the win32 API. */
+static struct target_waitstatus cached_status;
+
+/* 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;
+
+const struct target_desc *win32_tdesc;
+
+#define NUM_REGS (the_low_target.num_regs)
+
+typedef BOOL (WINAPI *winapi_DebugActiveProcessStop) (DWORD dwProcessId);
+typedef BOOL (WINAPI *winapi_DebugSetProcessKillOnExit) (BOOL KillOnExit);
+typedef BOOL (WINAPI *winapi_DebugBreakProcess) (HANDLE);
+typedef BOOL (WINAPI *winapi_GenerateConsoleCtrlEvent) (DWORD, DWORD);
+
+static ptid_t win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus,
+ int options);
+static void win32_resume (struct thread_resume *resume_info, size_t n);
+#ifndef _WIN32_WCE
+static void win32_add_all_dlls (void);
+#endif
+
+/* Get the thread ID from the current selected inferior (the current
+ thread). */
+static ptid_t
+current_thread_ptid (void)
+{
+ return current_ptid;
+}
+
+/* The current debug event from WaitForDebugEvent. */
+static ptid_t
+debug_event_ptid (DEBUG_EVENT *event)
+{
+ return ptid_t (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);
+#ifdef _WIN32_WCE
+ memcpy (&th->base_context, &th->context, sizeof (CONTEXT));
+#endif
+}
+
+/* Set the thread context of the thread associated with TH. */
+
+static void
+win32_set_thread_context (win32_thread_info *th)
+{
+#ifdef _WIN32_WCE
+ /* Calling SuspendThread on a thread that is running kernel code
+ will report that the suspending was successful, but in fact, that
+ will often not be true. In those cases, the context returned by
+ GetThreadContext will not be correct by the time the thread
+ stops, hence we can't set that context back into the thread when
+ resuming - it will most likely crash the inferior.
+ Unfortunately, there is no way to know when the thread will
+ really stop. To work around it, we'll only write the context
+ back to the thread when either the user or GDB explicitly change
+ it between stopping and resuming. */
+ if (memcmp (&th->context, &th->base_context, sizeof (CONTEXT)) != 0)
+#endif
+ SetThreadContext (th->h, &th->context);
+}
+
+/* Set the thread context of the thread associated with TH. */
+
+static void
+win32_prepare_to_resume (win32_thread_info *th)
+{
+ if (the_low_target.prepare_to_resume != NULL)
+ (*the_low_target.prepare_to_resume) (th);
+}
+
+/* See win32-low.h. */
+
+void
+win32_require_context (win32_thread_info *th)
+{
+ if (th->context.ContextFlags == 0)
+ {
+ 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;
+ }
+
+ win32_get_thread_context (th);
+ }
+}
+
+/* Find a thread record given a thread id. If GET_CONTEXT is set then
+ also retrieve the context for this thread. */
+static win32_thread_info *
+thread_rec (ptid_t ptid, int get_context)
+{
+ thread_info *thread = find_thread_ptid (ptid);
+ if (thread == NULL)
+ return NULL;
+
+ win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+ if (get_context)
+ win32_require_context (th);
+ return th;
+}
+
+/* Add a thread to the thread list. */
+static win32_thread_info *
+child_add_thread (DWORD pid, DWORD tid, HANDLE h, void *tlb)
+{
+ win32_thread_info *th;
+ ptid_t ptid = ptid_t (pid, tid, 0);
+
+ if ((th = thread_rec (ptid, FALSE)))
+ return th;
+
+ th = XCNEW (win32_thread_info);
+ th->tid = tid;
+ th->h = h;
+ th->thread_local_base = (CORE_ADDR) (uintptr_t) tlb;
+
+ add_thread (ptid, th);
+
+ if (the_low_target.thread_added != NULL)
+ (*the_low_target.thread_added) (th);
+
+ return th;
+}
+
+/* Delete a thread from the list of threads. */
+static void
+delete_thread_info (thread_info *thread)
+{
+ win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+
+ remove_thread (thread);
+ CloseHandle (th->h);
+ free (th);
+}
+
+/* Delete a thread from the list of threads. */
+static void
+child_delete_thread (DWORD pid, DWORD tid)
+{
+ /* If the last thread is exiting, just return. */
+ if (all_threads.size () == 1)
+ return;
+
+ thread_info *thread = find_thread_ptid (ptid_t (pid, tid));
+ if (thread == NULL)
+ return;
+
+ delete_thread_info (thread);
+}
+
+/* These watchpoint related wrapper functions simply pass on the function call
+ if the low target has registered a corresponding function. */
+
+static int
+win32_supports_z_point_type (char z_type)
+{
+ return (the_low_target.supports_z_point_type != NULL
+ && the_low_target.supports_z_point_type (z_type));
+}
+
+static int
+win32_insert_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ if (the_low_target.insert_point != NULL)
+ return the_low_target.insert_point (type, addr, size, bp);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+win32_remove_point (enum raw_bkpt_type type, CORE_ADDR addr,
+ int size, struct raw_breakpoint *bp)
+{
+ if (the_low_target.remove_point != NULL)
+ return the_low_target.remove_point (type, addr, size, bp);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+win32_stopped_by_watchpoint (void)
+{
+ if (the_low_target.stopped_by_watchpoint != NULL)
+ return the_low_target.stopped_by_watchpoint ();
+ else
+ return 0;
+}
+
+static CORE_ADDR
+win32_stopped_data_address (void)
+{
+ if (the_low_target.stopped_data_address != NULL)
+ return the_low_target.stopped_data_address ();
+ else
+ return 0;
+}
+
+
+/* Transfer memory from/to the debugged process. */
+static int
+child_xfer_memory (CORE_ADDR memaddr, char *our, int len,
+ int write, process_stratum_target *target)
+{
+ BOOL success;
+ SIZE_T done = 0;
+ DWORD lasterror = 0;
+ uintptr_t addr = (uintptr_t) memaddr;
+
+ if (write)
+ {
+ success = WriteProcessMemory (current_process_handle, (LPVOID) addr,
+ (LPCVOID) our, len, &done);
+ if (!success)
+ lasterror = GetLastError ();
+ FlushInstructionCache (current_process_handle, (LPCVOID) addr, len);
+ }
+ else
+ {
+ success = ReadProcessMemory (current_process_handle, (LPCVOID) addr,
+ (LPVOID) our, len, &done);
+ if (!success)
+ lasterror = GetLastError ();
+ }
+ if (!success && lasterror == ERROR_PARTIAL_COPY && done > 0)
+ return done;
+ else
+ return success ? done : -1;
+}
+
+/* Clear out any old thread list and reinitialize it to a pristine
+ state. */
+static void
+child_init_thread_list (void)
+{
+ for_each_thread (delete_thread_info);
+}
+
+/* Zero during the child initialization phase, and nonzero otherwise. */
+
+static int child_initialization_done = 0;
+
+static void
+do_initial_child_stuff (HANDLE proch, DWORD pid, int attached)
+{
+ struct process_info *proc;
+
+ last_sig = GDB_SIGNAL_0;
+
+ current_process_handle = proch;
+ current_process_id = pid;
+ main_thread_id = 0;
+
+ soft_interrupt_requested = 0;
+ faked_breakpoint = 0;
+
+ memset (&current_event, 0, sizeof (current_event));
+
+ proc = add_process (pid, attached);
+ proc->tdesc = win32_tdesc;
+ child_init_thread_list ();
+ child_initialization_done = 0;
+
+ if (the_low_target.initial_stuff != NULL)
+ (*the_low_target.initial_stuff) ();
+
+ cached_status.kind = TARGET_WAITKIND_IGNORE;
+
+ /* Flush all currently pending debug events (thread and dll list) up
+ to the initial breakpoint. */
+ while (1)
+ {
+ struct target_waitstatus status;
+
+ win32_wait (minus_one_ptid, &status, 0);
+
+ /* Note win32_wait doesn't return thread events. */
+ if (status.kind != TARGET_WAITKIND_LOADED)
+ {
+ cached_status = status;
+ break;
+ }
+
+ {
+ struct thread_resume resume;
+
+ resume.thread = minus_one_ptid;
+ resume.kind = resume_continue;
+ resume.sig = 0;
+
+ win32_resume (&resume, 1);
+ }
+ }
+
+#ifndef _WIN32_WCE
+ /* Now that the inferior has been started and all DLLs have been mapped,
+ we can iterate over all DLLs and load them in.
+
+ We avoid doing it any earlier because, on certain versions of Windows,
+ LOAD_DLL_DEBUG_EVENTs are sometimes not complete. In particular,
+ we have seen on Windows 8.1 that the ntdll.dll load event does not
+ include the DLL name, preventing us from creating an associated SO.
+ A possible explanation is that ntdll.dll might be mapped before
+ the SO info gets created by the Windows system -- ntdll.dll is
+ the first DLL to be reported via LOAD_DLL_DEBUG_EVENT and other DLLs
+ do not seem to suffer from that problem.
+
+ Rather than try to work around this sort of issue, it is much
+ simpler to just ignore DLL load/unload events during the startup
+ phase, and then process them all in one batch now. */
+ win32_add_all_dlls ();
+#endif
+
+ child_initialization_done = 1;
+}
+
+/* Resume all artificially suspended threads if we are continuing
+ execution. */
+static void
+continue_one_thread (thread_info *thread, int thread_id)
+{
+ win32_thread_info *th = (win32_thread_info *) thread_target_data (thread);
+
+ if (thread_id == -1 || thread_id == th->tid)
+ {
+ win32_prepare_to_resume (th);
+
+ if (th->suspended)
+ {
+ if (th->context.ContextFlags)
+ {
+ win32_set_thread_context (th);
+ th->context.ContextFlags = 0;
+ }
+
+ if (ResumeThread (th->h) == (DWORD) -1)
+ {
+ DWORD err = GetLastError ();
+ OUTMSG (("warning: ResumeThread failed in continue_one_thread, "
+ "(error %d): %s\n", (int) err, strwinerror (err)));
+ }
+ th->suspended = 0;
+ }
+ }
+}
+
+static BOOL
+child_continue (DWORD continue_status, int thread_id)
+{
+ /* The inferior will only continue after the ContinueDebugEvent
+ call. */
+ for_each_thread ([&] (thread_info *thread)
+ {
+ continue_one_thread (thread, thread_id);
+ });
+ faked_breakpoint = 0;
+
+ if (!ContinueDebugEvent (current_event.dwProcessId,
+ current_event.dwThreadId,
+ continue_status))
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Fetch register(s) from the current thread context. */
+static void
+child_fetch_inferior_registers (struct regcache *regcache, int r)
+{
+ int regno;
+ win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE);
+ if (r == -1 || r > NUM_REGS)
+ child_fetch_inferior_registers (regcache, NUM_REGS);
+ else
+ for (regno = 0; regno < r; 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 (struct regcache *regcache, int r)
+{
+ int regno;
+ win32_thread_info *th = thread_rec (current_thread_ptid (), TRUE);
+ if (r == -1 || r == 0 || r > NUM_REGS)
+ child_store_inferior_registers (regcache, NUM_REGS);
+ else
+ for (regno = 0; regno < r; regno++)
+ (*the_low_target.store_inferior_register) (regcache, th, regno);
+}
+
+/* Map the Windows error number in ERROR to a locale-dependent error
+ message string and return a pointer to it. Typically, the values
+ for ERROR come from GetLastError.
+
+ The string pointed to shall not be modified by the application,
+ but may be overwritten by a subsequent call to strwinerror
+
+ The strwinerror function does not change the current setting
+ of GetLastError. */
+
+char *
+strwinerror (DWORD error)
+{
+ static char buf[1024];
+ TCHAR *msgbuf;
+ DWORD lasterr = GetLastError ();
+ DWORD chars = FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
+ | FORMAT_MESSAGE_ALLOCATE_BUFFER,
+ NULL,
+ error,
+ 0, /* Default language */
+ (LPTSTR) &msgbuf,
+ 0,
+ NULL);
+ if (chars != 0)
+ {
+ /* If there is an \r\n appended, zap it. */
+ if (chars >= 2
+ && msgbuf[chars - 2] == '\r'
+ && msgbuf[chars - 1] == '\n')
+ {
+ chars -= 2;
+ msgbuf[chars] = 0;
+ }
+
+ if (chars > ((COUNTOF (buf)) - 1))
+ {
+ chars = COUNTOF (buf) - 1;
+ msgbuf [chars] = 0;
+ }
+
+#ifdef UNICODE
+ wcstombs (buf, msgbuf, chars + 1);
+#else
+ strncpy (buf, msgbuf, chars + 1);
+#endif
+ LocalFree (msgbuf);
+ }
+ else
+ sprintf (buf, "unknown win32 error (%u)", (unsigned) error);
+
+ SetLastError (lasterr);
+ return buf;
+}
+
+static BOOL
+create_process (const char *program, char *args,
+ DWORD flags, PROCESS_INFORMATION *pi)
+{
+ const char *inferior_cwd = get_inferior_cwd ();
+ BOOL ret;
+
+#ifdef _WIN32_WCE
+ wchar_t *p, *wprogram, *wargs, *wcwd = NULL;
+ 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);
+
+ if (inferior_cwd != NULL)
+ {
+ std::string expanded_infcwd = gdb_tilde_expand (inferior_cwd);
+ std::replace (expanded_infcwd.begin (), expanded_infcwd.end (),
+ '/', '\\');
+ wcwd = alloca ((expanded_infcwd.size () + 1) * sizeof (wchar_t));
+ if (mbstowcs (wcwd, expanded_infcwd.c_str (),
+ expanded_infcwd.size () + 1) == NULL)
+ {
+ error (_("\
+Could not convert the expanded inferior cwd to wide-char."));
+ }
+ }
+
+ 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 */
+ wcwd, /* current directory */
+ 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 */
+ /* current directory */
+ (inferior_cwd == NULL
+ ? NULL
+ : gdb_tilde_expand (inferior_cwd).c_str()),
+ &si, /* start info */
+ pi); /* proc info */
+#endif
+
+ return ret;
+}
+
+/* Start a new process.
+ PROGRAM is the program name.
+ PROGRAM_ARGS is the vector containing the inferior's args.
+ Returns the new PID on success, -1 on failure. Registers the new
+ process with the process list. */
+static int
+win32_create_inferior (const char *program,
+ const std::vector<char *> &program_args)
+{
+ client_state &cs = get_client_state ();
+#ifndef USE_WIN32API
+ char real_path[PATH_MAX];
+ char *orig_path, *new_path, *path_ptr;
+#endif
+ BOOL ret;
+ DWORD flags;
+ PROCESS_INFORMATION pi;
+ DWORD err;
+ std::string str_program_args = stringify_argv (program_args);
+ char *args = (char *) str_program_args.c_str ();
+
+ /* win32_wait needs to know we're not attaching. */
+ attaching = 0;
+
+ if (!program)
+ error ("No executable specified, specify executable to debug.\n");
+
+ flags = DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS;
+
+#ifndef USE_WIN32API
+ orig_path = NULL;
+ path_ptr = getenv ("PATH");
+ if (path_ptr)
+ {
+ int size = cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, NULL, 0);
+ orig_path = (char *) alloca (strlen (path_ptr) + 1);
+ new_path = (char *) alloca (size);
+ strcpy (orig_path, path_ptr);
+ cygwin_conv_path_list (CCP_POSIX_TO_WIN_A, path_ptr, new_path, size);
+ setenv ("PATH", new_path, 1);
+ }
+ cygwin_conv_path (CCP_POSIX_TO_WIN_A, program, real_path, PATH_MAX);
+ program = real_path;
+#endif
+
+ OUTMSG2 (("Command line is \"%s\"\n", args));
+
+#ifdef CREATE_NEW_PROCESS_GROUP
+ flags |= CREATE_NEW_PROCESS_GROUP;
+#endif
+
+ ret = create_process (program, args, flags, &pi);
+ err = GetLastError ();
+ if (!ret && err == ERROR_FILE_NOT_FOUND)
+ {
+ char *exename = (char *) alloca (strlen (program) + 5);
+ strcat (strcpy (exename, program), ".exe");
+ ret = create_process (exename, args, flags, &pi);
+ err = GetLastError ();
+ }
+
+#ifndef USE_WIN32API
+ if (orig_path)
+ setenv ("PATH", orig_path, 1);
+#endif
+
+ if (!ret)
+ {
+ error ("Error creating process \"%s%s\", (error %d): %s\n",
+ program, args, (int) err, strwinerror (err));
+ }
+ else
+ {
+ OUTMSG2 (("Process created: %s\n", (char *) args));
+ }
+
+#ifndef _WIN32_WCE
+ /* On Windows CE this handle can't be closed. The OS reuses
+ it in the debug events, while the 9x/NT versions of Windows
+ probably use a DuplicateHandle'd one. */
+ CloseHandle (pi.hThread);
+#endif
+
+ do_initial_child_stuff (pi.hProcess, pi.dwProcessId, 0);
+
+ /* Wait till we are at 1st instruction in program, return new pid
+ (assuming success). */
+ cs.last_ptid = win32_wait (ptid_t (current_process_id), &cs.last_status, 0);
+
+ /* Necessary for handle_v_kill. */
+ signal_pid = 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)
+{
+ HANDLE h;
+ winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
+ DWORD err;
+#ifdef _WIN32_WCE
+ HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
+#else
+ HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
+#endif
+ DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit);
+
+ h = OpenProcess (PROCESS_ALL_ACCESS, FALSE, pid);
+ if (h != NULL)
+ {
+ 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);
+ }
+
+ err = GetLastError ();
+ error ("Attach to process failed (error %d): %s\n",
+ (int) err, strwinerror (err));
+}
+
+/* Handle OUTPUT_DEBUG_STRING_EVENT from child process. */
+static void
+handle_output_debug_string (void)
+{
+#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 (!startswith (s, "cYg"))
+ {
+ if (!server_waiting)
+ {
+ OUTMSG2(("%s", s));
+ return;
+ }
+
+ monitor_output (s);
+ }
+#undef READ_BUFFER_LEN
+}
+
+static void
+win32_clear_inferiors (void)
+{
+ if (current_process_handle != NULL)
+ CloseHandle (current_process_handle);
+
+ for_each_thread (delete_thread_info);
+ siginfo_er.ExceptionCode = 0;
+ clear_inferiors ();
+}
+
+/* Implementation of target_ops::kill. */
+
+static int
+win32_kill (process_info *process)
+{
+ TerminateProcess (current_process_handle, 0);
+ for (;;)
+ {
+ if (!child_continue (DBG_CONTINUE, -1))
+ break;
+ if (!WaitForDebugEvent (&current_event, INFINITE))
+ break;
+ if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT)
+ break;
+ else if (current_event.dwDebugEventCode == OUTPUT_DEBUG_STRING_EVENT)
+ handle_output_debug_string ();
+ }
+
+ win32_clear_inferiors ();
+
+ remove_process (process);
+ return 0;
+}
+
+/* Implementation of target_ops::detach. */
+
+static int
+win32_detach (process_info *process)
+{
+ winapi_DebugActiveProcessStop DebugActiveProcessStop = NULL;
+ winapi_DebugSetProcessKillOnExit DebugSetProcessKillOnExit = NULL;
+#ifdef _WIN32_WCE
+ HMODULE dll = GetModuleHandle (_T("COREDLL.DLL"));
+#else
+ HMODULE dll = GetModuleHandle (_T("KERNEL32.DLL"));
+#endif
+ DebugActiveProcessStop = GETPROCADDRESS (dll, DebugActiveProcessStop);
+ DebugSetProcessKillOnExit = GETPROCADDRESS (dll, DebugSetProcessKillOnExit);
+
+ if (DebugSetProcessKillOnExit == NULL
+ || DebugActiveProcessStop == NULL)
+ return -1;
+
+ {
+ struct thread_resume resume;
+ resume.thread = minus_one_ptid;
+ resume.kind = resume_continue;
+ resume.sig = 0;
+ win32_resume (&resume, 1);
+ }
+
+ if (!DebugActiveProcessStop (current_process_id))
+ return -1;
+
+ DebugSetProcessKillOnExit (FALSE);
+ remove_process (process);
+
+ win32_clear_inferiors ();
+ return 0;
+}
+
+static void
+win32_mourn (struct process_info *process)
+{
+ remove_process (process);
+}
+
+/* Implementation of target_ops::join. */
+
+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 (ptid_t ptid)
+{
+ /* Our thread list is reliable; don't bother to poll target
+ threads. */
+ return find_thread_ptid (ptid) != NULL;
+}
+
+/* Resume the inferior process. RESUME_INFO describes how we want
+ to resume. */
+static void
+win32_resume (struct thread_resume *resume_info, size_t n)
+{
+ DWORD tid;
+ enum gdb_signal sig;
+ int step;
+ win32_thread_info *th;
+ DWORD continue_status = DBG_CONTINUE;
+ ptid_t ptid;
+
+ /* This handles the very limited set of resume packets that GDB can
+ currently produce. */
+
+ if (n == 1 && resume_info[0].thread == minus_one_ptid)
+ tid = -1;
+ 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 != minus_one_ptid)
+ {
+ sig = gdb_signal_from_host (resume_info[0].sig);
+ step = resume_info[0].kind == resume_step;
+ }
+ else
+ {
+ sig = GDB_SIGNAL_0;
+ step = 0;
+ }
+
+ if (sig != GDB_SIGNAL_0)
+ {
+ if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT)
+ {
+ OUTMSG (("Cannot continue with signal %s here.\n",
+ gdb_signal_to_string (sig)));
+ }
+ else if (sig == last_sig)
+ continue_status = DBG_EXCEPTION_NOT_HANDLED;
+ else
+ OUTMSG (("Can only continue with received signal %s.\n",
+ gdb_signal_to_string (last_sig)));
+ }
+
+ last_sig = GDB_SIGNAL_0;
+
+ /* Get context for the currently selected thread. */
+ ptid = debug_event_ptid (&current_event);
+ th = thread_rec (ptid, FALSE);
+ if (th)
+ {
+ win32_prepare_to_resume (th);
+
+ if (th->context.ContextFlags)
+ {
+ /* Move register values from the inferior into the thread
+ context structure. */
+ regcache_invalidate ();
+
+ if (step)
+ {
+ if (the_low_target.single_step != NULL)
+ (*the_low_target.single_step) (th);
+ else
+ error ("Single stepping is not supported "
+ "in this configuration.\n");
+ }
+
+ win32_set_thread_context (th);
+ th->context.ContextFlags = 0;
+ }
+ }
+
+ /* Allow continuing with the same signal that interrupted us.
+ Otherwise complain. */
+
+ 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
+
+ /* The symbols in a dll are offset by 0x1000, which is the
+ offset from 0 of the first byte in an image - because
+ of the file header and the section alignment. */
+ load_addr += 0x1000;
+
+ if (h == INVALID_HANDLE_VALUE)
+ strcpy (buf, name);
+ else
+ {
+ FindClose (h);
+ strcpy (buf, name);
+#ifndef _WIN32_WCE
+ {
+ char cwd[MAX_PATH + 1];
+ char *p;
+ if (GetCurrentDirectoryA (MAX_PATH + 1, cwd))
+ {
+ p = strrchr (buf, '\\');
+ if (p)
+ p[1] = '\0';
+ SetCurrentDirectoryA (buf);
+ GetFullPathNameA (w32_fd.cFileName, MAX_PATH, buf, &p);
+ SetCurrentDirectoryA (cwd);
+ }
+ }
+#endif
+ }
+
+#ifndef _WIN32_WCE
+ if (strcasecmp (buf, "ntdll.dll") == 0)
+ {
+ GetSystemDirectoryA (buf, sizeof (buf));
+ strcat (buf, "\\ntdll.dll");
+ }
+#endif
+
+#ifdef __CYGWIN__
+ cygwin_conv_path (CCP_WIN_A_TO_POSIX, buf, buf2, sizeof (buf2));
+#else
+ strcpy (buf2, buf);
+#endif
+
+ loaded_dll (buf2, load_addr);
+}
+
+static char *
+get_image_name (HANDLE h, void *address, int unicode)
+{
+ static char buf[(2 * MAX_PATH) + 1];
+ DWORD size = unicode ? sizeof (WCHAR) : sizeof (char);
+ char *address_ptr;
+ int len = 0;
+ char b[2];
+ SIZE_T done;
+
+ /* Attempt to read the name of the dll that was detected.
+ This is documented to work only when actively debugging
+ a program. It will not work for attached processes. */
+ if (address == NULL)
+ return NULL;
+
+#ifdef _WIN32_WCE
+ /* Windows CE reports the address of the image name,
+ instead of an address of a pointer into the image name. */
+ address_ptr = address;
+#else
+ /* See if we could read the address of a string, and that the
+ address isn't null. */
+ if (!ReadProcessMemory (h, address, &address_ptr,
+ sizeof (address_ptr), &done)
+ || done != sizeof (address_ptr)
+ || !address_ptr)
+ return NULL;
+#endif
+
+ /* Find the length of the string */
+ while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done)
+ && (b[0] != 0 || b[size - 1] != 0) && done == size)
+ continue;
+
+ if (!unicode)
+ ReadProcessMemory (h, address_ptr, buf, len, &done);
+ else
+ {
+ WCHAR *unicode_address = XALLOCAVEC (WCHAR, len);
+ ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR),
+ &done);
+
+ WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0);
+ }
+
+ return buf;
+}
+
+typedef BOOL (WINAPI *winapi_EnumProcessModules) (HANDLE, HMODULE *,
+ DWORD, LPDWORD);
+typedef BOOL (WINAPI *winapi_GetModuleInformation) (HANDLE, HMODULE,
+ LPMODULEINFO, DWORD);
+typedef DWORD (WINAPI *winapi_GetModuleFileNameExA) (HANDLE, HMODULE,
+ LPSTR, DWORD);
+
+static winapi_EnumProcessModules win32_EnumProcessModules;
+static winapi_GetModuleInformation win32_GetModuleInformation;
+static winapi_GetModuleFileNameExA win32_GetModuleFileNameExA;
+
+static BOOL
+load_psapi (void)
+{
+ static int psapi_loaded = 0;
+ static HMODULE dll = NULL;
+
+ if (!psapi_loaded)
+ {
+ psapi_loaded = 1;
+ dll = LoadLibrary (TEXT("psapi.dll"));
+ if (!dll)
+ return FALSE;
+ win32_EnumProcessModules =
+ GETPROCADDRESS (dll, EnumProcessModules);
+ win32_GetModuleInformation =
+ GETPROCADDRESS (dll, GetModuleInformation);
+ win32_GetModuleFileNameExA =
+ GETPROCADDRESS (dll, GetModuleFileNameExA);
+ }
+
+ return (win32_EnumProcessModules != NULL
+ && win32_GetModuleInformation != NULL
+ && win32_GetModuleFileNameExA != NULL);
+}
+
+#ifndef _WIN32_WCE
+
+/* Iterate over all DLLs currently mapped by our inferior, and
+ add them to our list of solibs. */
+
+static void
+win32_add_all_dlls (void)
+{
+ size_t i;
+ HMODULE dh_buf[1];
+ HMODULE *DllHandle = dh_buf;
+ DWORD cbNeeded;
+ BOOL ok;
+
+ if (!load_psapi ())
+ return;
+
+ cbNeeded = 0;
+ ok = (*win32_EnumProcessModules) (current_process_handle,
+ DllHandle,
+ sizeof (HMODULE),
+ &cbNeeded);
+
+ if (!ok || !cbNeeded)
+ return;
+
+ DllHandle = (HMODULE *) alloca (cbNeeded);
+ if (!DllHandle)
+ return;
+
+ ok = (*win32_EnumProcessModules) (current_process_handle,
+ DllHandle,
+ cbNeeded,
+ &cbNeeded);
+ if (!ok)
+ return;
+
+ for (i = 1; i < ((size_t) cbNeeded / sizeof (HMODULE)); i++)
+ {
+ MODULEINFO mi;
+ char dll_name[MAX_PATH];
+
+ if (!(*win32_GetModuleInformation) (current_process_handle,
+ DllHandle[i],
+ &mi,
+ sizeof (mi)))
+ continue;
+ if ((*win32_GetModuleFileNameExA) (current_process_handle,
+ DllHandle[i],
+ dll_name,
+ MAX_PATH) == 0)
+ continue;
+ win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) mi.lpBaseOfDll);
+ }
+}
+#endif
+
+typedef HANDLE (WINAPI *winapi_CreateToolhelp32Snapshot) (DWORD, DWORD);
+typedef BOOL (WINAPI *winapi_Module32First) (HANDLE, LPMODULEENTRY32);
+typedef BOOL (WINAPI *winapi_Module32Next) (HANDLE, LPMODULEENTRY32);
+
+/* Handle a DLL load event.
+
+ This function assumes that this event did not occur during inferior
+ initialization, where their event info may be incomplete (see
+ do_initial_child_stuff and win32_add_all_dlls for more info on
+ how we handle DLL loading during that phase). */
+
+static void
+handle_load_dll (void)
+{
+ LOAD_DLL_DEBUG_INFO *event = &current_event.u.LoadDll;
+ char *dll_name;
+
+ dll_name = get_image_name (current_process_handle,
+ event->lpImageName, event->fUnicode);
+ if (!dll_name)
+ return;
+
+ win32_add_one_solib (dll_name, (CORE_ADDR) (uintptr_t) event->lpBaseOfDll);
+}
+
+/* Handle a DLL unload event.
+
+ This function assumes that this event did not occur during inferior
+ initialization, where their event info may be incomplete (see
+ do_initial_child_stuff and win32_add_one_solib for more info
+ on how we handle DLL loading during that phase). */
+
+static void
+handle_unload_dll (void)
+{
+ CORE_ADDR load_addr =
+ (CORE_ADDR) (uintptr_t) current_event.u.UnloadDll.lpBaseOfDll;
+
+ /* The symbols in a dll are offset by 0x1000, which is the
+ offset from 0 of the first byte in an image - because
+ of the file header and the section alignment. */
+ load_addr += 0x1000;
+ unloaded_dll (NULL, load_addr);
+}
+
+static void
+handle_exception (struct target_waitstatus *ourstatus)
+{
+ DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode;
+
+ memcpy (&siginfo_er, &current_event.u.Exception.ExceptionRecord,
+ sizeof siginfo_er);
+
+ ourstatus->kind = TARGET_WAITKIND_STOPPED;
+
+ switch (code)
+ {
+ case EXCEPTION_ACCESS_VIOLATION:
+ OUTMSG2 (("EXCEPTION_ACCESS_VIOLATION"));
+ ourstatus->value.sig = GDB_SIGNAL_SEGV;
+ break;
+ case STATUS_STACK_OVERFLOW:
+ OUTMSG2 (("STATUS_STACK_OVERFLOW"));
+ ourstatus->value.sig = GDB_SIGNAL_SEGV;
+ break;
+ case STATUS_FLOAT_DENORMAL_OPERAND:
+ OUTMSG2 (("STATUS_FLOAT_DENORMAL_OPERAND"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+ OUTMSG2 (("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_INEXACT_RESULT:
+ OUTMSG2 (("STATUS_FLOAT_INEXACT_RESULT"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_INVALID_OPERATION:
+ OUTMSG2 (("STATUS_FLOAT_INVALID_OPERATION"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_OVERFLOW:
+ OUTMSG2 (("STATUS_FLOAT_OVERFLOW"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_STACK_CHECK:
+ OUTMSG2 (("STATUS_FLOAT_STACK_CHECK"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_UNDERFLOW:
+ OUTMSG2 (("STATUS_FLOAT_UNDERFLOW"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_FLOAT_DIVIDE_BY_ZERO:
+ OUTMSG2 (("STATUS_FLOAT_DIVIDE_BY_ZERO"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_INTEGER_DIVIDE_BY_ZERO:
+ OUTMSG2 (("STATUS_INTEGER_DIVIDE_BY_ZERO"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case STATUS_INTEGER_OVERFLOW:
+ OUTMSG2 (("STATUS_INTEGER_OVERFLOW"));
+ ourstatus->value.sig = GDB_SIGNAL_FPE;
+ break;
+ case EXCEPTION_BREAKPOINT:
+ OUTMSG2 (("EXCEPTION_BREAKPOINT"));
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+#ifdef _WIN32_WCE
+ /* Remove the initial breakpoint. */
+ check_breakpoints ((CORE_ADDR) (long) current_event
+ .u.Exception.ExceptionRecord.ExceptionAddress);
+#endif
+ break;
+ case DBG_CONTROL_C:
+ OUTMSG2 (("DBG_CONTROL_C"));
+ ourstatus->value.sig = GDB_SIGNAL_INT;
+ break;
+ case DBG_CONTROL_BREAK:
+ OUTMSG2 (("DBG_CONTROL_BREAK"));
+ ourstatus->value.sig = GDB_SIGNAL_INT;
+ break;
+ case EXCEPTION_SINGLE_STEP:
+ OUTMSG2 (("EXCEPTION_SINGLE_STEP"));
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+ break;
+ case EXCEPTION_ILLEGAL_INSTRUCTION:
+ OUTMSG2 (("EXCEPTION_ILLEGAL_INSTRUCTION"));
+ ourstatus->value.sig = GDB_SIGNAL_ILL;
+ break;
+ case EXCEPTION_PRIV_INSTRUCTION:
+ OUTMSG2 (("EXCEPTION_PRIV_INSTRUCTION"));
+ ourstatus->value.sig = GDB_SIGNAL_ILL;
+ break;
+ case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+ OUTMSG2 (("EXCEPTION_NONCONTINUABLE_EXCEPTION"));
+ ourstatus->value.sig = GDB_SIGNAL_ILL;
+ break;
+ default:
+ if (current_event.u.Exception.dwFirstChance)
+ {
+ ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+ return;
+ }
+ OUTMSG2 (("gdbserver: unknown target exception 0x%08x at 0x%s",
+ (unsigned) current_event.u.Exception.ExceptionRecord.ExceptionCode,
+ phex_nz ((uintptr_t) current_event.u.Exception.ExceptionRecord.
+ ExceptionAddress, sizeof (uintptr_t))));
+ ourstatus->value.sig = GDB_SIGNAL_UNKNOWN;
+ break;
+ }
+ OUTMSG2 (("\n"));
+ last_sig = ourstatus->value.sig;
+}
+
+
+static void
+suspend_one_thread (thread_info *thread)
+{
+ win32_thread_info *th = (win32_thread_info *) thread_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 (&current_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_thread (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)
+{
+ ptid_t ptid;
+
+ last_sig = GDB_SIGNAL_0;
+ ourstatus->kind = TARGET_WAITKIND_SPURIOUS;
+
+ /* Check if GDB sent us an interrupt request. */
+ check_remote_input_interrupt_request ();
+
+ if (soft_interrupt_requested)
+ {
+ soft_interrupt_requested = 0;
+ fake_breakpoint_event ();
+ goto gotevent;
+ }
+
+#ifndef _WIN32_WCE
+ attaching = 0;
+#else
+ if (attaching)
+ {
+ /* WinCE doesn't set an initial breakpoint automatically. To
+ stop the inferior, we flush all currently pending debug
+ events -- the thread list and the dll list are always
+ reported immediatelly without delay, then, we suspend all
+ threads and pretend we saw a trap at the current PC of the
+ main thread.
+
+ Contrary to desktop Windows, Windows CE *does* report the dll
+ names on LOAD_DLL_DEBUG_EVENTs resulting from a
+ DebugActiveProcess call. This limits the way we can detect
+ if all the dlls have already been reported. If we get a real
+ debug event before leaving attaching, the worst that will
+ happen is the user will see a spurious breakpoint. */
+
+ current_event.dwDebugEventCode = 0;
+ if (!WaitForDebugEvent (&current_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 comfortable remote
+ interruption, but high enough so gdbserver doesn't become a
+ bottleneck. */
+ if (!WaitForDebugEvent (&current_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)
+ {
+ case CREATE_THREAD_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event CREATE_THREAD_DEBUG_EVENT "
+ "for pid=%u tid=%x)\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+
+ /* Record the existence of this thread. */
+ child_add_thread (current_event.dwProcessId,
+ current_event.dwThreadId,
+ current_event.u.CreateThread.hThread,
+ current_event.u.CreateThread.lpThreadLocalBase);
+ break;
+
+ case EXIT_THREAD_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event EXIT_THREAD_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ child_delete_thread (current_event.dwProcessId,
+ current_event.dwThreadId);
+
+ current_thread = get_first_thread ();
+ return 1;
+
+ case CREATE_PROCESS_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event CREATE_PROCESS_DEBUG_EVENT "
+ "for pid=%u 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;
+
+ /* Add the main thread. */
+ child_add_thread (current_event.dwProcessId,
+ main_thread_id,
+ current_event.u.CreateProcessInfo.hThread,
+ current_event.u.CreateProcessInfo.lpThreadLocalBase);
+
+#ifdef _WIN32_WCE
+ if (!attaching)
+ {
+ /* Windows CE doesn't set the initial breakpoint
+ automatically like the desktop versions of Windows do.
+ We add it explicitly here. It will be removed as soon as
+ it is hit. */
+ set_breakpoint_at ((CORE_ADDR) (long) current_event.u
+ .CreateProcessInfo.lpStartAddress,
+ auto_delete_breakpoint);
+ }
+#endif
+ break;
+
+ case EXIT_PROCESS_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event EXIT_PROCESS_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ {
+ DWORD exit_status = current_event.u.ExitProcess.dwExitCode;
+ /* If the exit status looks like a fatal exception, but we
+ don't recognize the exception's code, make the original
+ exit status value available, to avoid losing information. */
+ int exit_signal
+ = WIFSIGNALED (exit_status) ? WTERMSIG (exit_status) : -1;
+ if (exit_signal == -1)
+ {
+ ourstatus->kind = TARGET_WAITKIND_EXITED;
+ ourstatus->value.integer = exit_status;
+ }
+ else
+ {
+ ourstatus->kind = TARGET_WAITKIND_SIGNALLED;
+ ourstatus->value.sig = gdb_signal_from_host (exit_signal);
+ }
+ }
+ child_continue (DBG_CONTINUE, -1);
+ CloseHandle (current_process_handle);
+ current_process_handle = NULL;
+ break;
+
+ case LOAD_DLL_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event LOAD_DLL_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ CloseHandle (current_event.u.LoadDll.hFile);
+ if (! child_initialization_done)
+ break;
+ handle_load_dll ();
+
+ ourstatus->kind = TARGET_WAITKIND_LOADED;
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+ break;
+
+ case UNLOAD_DLL_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event UNLOAD_DLL_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ if (! child_initialization_done)
+ break;
+ handle_unload_dll ();
+ ourstatus->kind = TARGET_WAITKIND_LOADED;
+ ourstatus->value.sig = GDB_SIGNAL_TRAP;
+ break;
+
+ case EXCEPTION_DEBUG_EVENT:
+ OUTMSG2 (("gdbserver: kernel event EXCEPTION_DEBUG_EVENT "
+ "for pid=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ 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=%u tid=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId));
+ handle_output_debug_string ();
+ break;
+
+ default:
+ OUTMSG2 (("gdbserver: kernel event unknown "
+ "for pid=%u tid=%x code=%x\n",
+ (unsigned) current_event.dwProcessId,
+ (unsigned) current_event.dwThreadId,
+ (unsigned) current_event.dwDebugEventCode));
+ break;
+ }
+
+ ptid = debug_event_ptid (&current_event);
+ current_thread = find_thread_ptid (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 ptid_t
+win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus, int options)
+{
+ struct regcache *regcache;
+
+ if (cached_status.kind != TARGET_WAITKIND_IGNORE)
+ {
+ /* The core always does a wait after creating the inferior, and
+ do_initial_child_stuff already ran the inferior to the
+ initial breakpoint (or an exit, if creating the process
+ fails). Report it now. */
+ *ourstatus = cached_status;
+ cached_status.kind = TARGET_WAITKIND_IGNORE;
+ return debug_event_ptid (&current_event);
+ }
+
+ while (1)
+ {
+ if (!get_child_debug_event (ourstatus))
+ continue;
+
+ switch (ourstatus->kind)
+ {
+ case TARGET_WAITKIND_EXITED:
+ OUTMSG2 (("Child exited with retcode = %x\n",
+ ourstatus->value.integer));
+ win32_clear_inferiors ();
+ return ptid_t (current_event.dwProcessId);
+ case TARGET_WAITKIND_STOPPED:
+ case TARGET_WAITKIND_SIGNALLED:
+ case TARGET_WAITKIND_LOADED:
+ OUTMSG2 (("Child Stopped with signal = %d \n",
+ ourstatus->value.sig));
+
+ regcache = get_thread_regcache (current_thread, 1);
+ child_fetch_inferior_registers (regcache, -1);
+ return debug_event_ptid (&current_event);
+ default:
+ OUTMSG (("Ignoring unknown internal event, %d\n", ourstatus->kind));
+ /* fall-through */
+ case TARGET_WAITKIND_SPURIOUS:
+ /* do nothing, just continue */
+ child_continue (DBG_CONTINUE, -1);
+ break;
+ }
+ }
+}
+
+/* Fetch registers from the inferior process.
+ If REGNO is -1, fetch all registers; otherwise, fetch at least REGNO. */
+static void
+win32_fetch_inferior_registers (struct regcache *regcache, int 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 (struct regcache *regcache, int regno)
+{
+ child_store_inferior_registers (regcache, 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, (char *) 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;
+}
+
+/* Send an interrupt request to the inferior process. */
+static void
+win32_request_interrupt (void)
+{
+ 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 signal info. */
+
+static int
+win32_xfer_siginfo (const char *annex, unsigned char *readbuf,
+ unsigned const char *writebuf, CORE_ADDR offset, int len)
+{
+ if (siginfo_er.ExceptionCode == 0)
+ return -1;
+
+ if (readbuf == nullptr)
+ return -1;
+
+ if (offset > sizeof (siginfo_er))
+ return -1;
+
+ if (offset + len > sizeof (siginfo_er))
+ len = sizeof (siginfo_er) - offset;
+
+ memcpy (readbuf, (char *) &siginfo_er + offset, len);
+
+ return len;
+}
+
+/* Write Windows OS Thread Information Block address. */
+
+static int
+win32_get_tib_address (ptid_t ptid, CORE_ADDR *addr)
+{
+ win32_thread_info *th;
+ th = thread_rec (ptid, 0);
+ if (th == NULL)
+ return 0;
+ if (addr != NULL)
+ *addr = th->thread_local_base;
+ return 1;
+}
+
+/* Implementation of the target_ops method "sw_breakpoint_from_kind". */
+
+static const gdb_byte *
+win32_sw_breakpoint_from_kind (int kind, int *size)
+{
+ *size = the_low_target.breakpoint_len;
+ return the_low_target.breakpoint;
+}
+
+static process_stratum_target win32_target_ops = {
+ win32_create_inferior,
+ NULL, /* post_create_inferior */
+ win32_attach,
+ win32_kill,
+ win32_detach,
+ win32_mourn,
+ win32_join,
+ win32_thread_alive,
+ win32_resume,
+ win32_wait,
+ win32_fetch_inferior_registers,
+ win32_store_inferior_registers,
+ NULL, /* prepare_to_access_memory */
+ NULL, /* done_accessing_memory */
+ win32_read_inferior_memory,
+ win32_write_inferior_memory,
+ NULL, /* lookup_symbols */
+ win32_request_interrupt,
+ NULL, /* read_auxv */
+ win32_supports_z_point_type,
+ win32_insert_point,
+ win32_remove_point,
+ NULL, /* stopped_by_sw_breakpoint */
+ NULL, /* supports_stopped_by_sw_breakpoint */
+ NULL, /* stopped_by_hw_breakpoint */
+ NULL, /* supports_stopped_by_hw_breakpoint */
+ target_can_do_hardware_single_step,
+ win32_stopped_by_watchpoint,
+ win32_stopped_data_address,
+ NULL, /* read_offsets */
+ NULL, /* get_tls_address */
+#ifdef _WIN32_WCE
+ wince_hostio_last_error,
+#else
+ hostio_last_error_from_errno,
+#endif
+ NULL, /* qxfer_osdata */
+ win32_xfer_siginfo,
+ NULL, /* supports_non_stop */
+ NULL, /* async */
+ NULL, /* start_non_stop */
+ NULL, /* supports_multi_process */
+ NULL, /* supports_fork_events */
+ NULL, /* supports_vfork_events */
+ NULL, /* supports_exec_events */
+ NULL, /* handle_new_gdb_connection */
+ NULL, /* handle_monitor_command */
+ NULL, /* core_of_thread */
+ NULL, /* read_loadmap */
+ NULL, /* process_qsupported */
+ NULL, /* supports_tracepoints */
+ NULL, /* read_pc */
+ NULL, /* write_pc */
+ NULL, /* thread_stopped */
+ win32_get_tib_address,
+ NULL, /* pause_all */
+ NULL, /* unpause_all */
+ NULL, /* stabilize_threads */
+ NULL, /* install_fast_tracepoint_jump_pad */
+ NULL, /* emit_ops */
+ NULL, /* supports_disable_randomization */
+ NULL, /* get_min_fast_tracepoint_insn_len */
+ NULL, /* qxfer_libraries_svr4 */
+ NULL, /* support_agent */
+ NULL, /* enable_btrace */
+ NULL, /* disable_btrace */
+ NULL, /* read_btrace */
+ NULL, /* read_btrace_conf */
+ NULL, /* supports_range_stepping */
+ NULL, /* pid_to_exec_file */
+ NULL, /* multifs_open */
+ NULL, /* multifs_unlink */
+ NULL, /* multifs_readlink */
+ NULL, /* breakpoint_kind_from_pc */
+ win32_sw_breakpoint_from_kind,
+};
+
+/* Initialize the Win32 backend. */
+void
+initialize_low (void)
+{
+ set_target_ops (&win32_target_ops);
+ the_low_target.arch_setup ();
+}