diff options
author | Simon Marchi <simon.marchi@efficios.com> | 2020-02-13 16:27:51 -0500 |
---|---|---|
committer | Simon Marchi <simon.marchi@efficios.com> | 2020-02-13 16:27:51 -0500 |
commit | feacfcacaac9f7e62f467a33c4ae54c56501ed18 (patch) | |
tree | 0e083366350e9fd279bec8c7b12413c90b9f255d /gdbserver/win32-low.cc | |
parent | 06b3c5bdb018262d7c09cda9d4637015c7ebe779 (diff) | |
download | gdb-feacfcacaac9f7e62f467a33c4ae54c56501ed18.zip gdb-feacfcacaac9f7e62f467a33c4ae54c56501ed18.tar.gz gdb-feacfcacaac9f7e62f467a33c4ae54c56501ed18.tar.bz2 |
gdbserver: rename source files to .cc
For the same reasons outlined in the previous patch, this patch renames
gdbserver source files to .cc.
I have moved the "-x c++" switch to only those rules that require it.
gdbserver/ChangeLog:
* Makefile.in: Rename source files from .c to .cc.
* %.c: Rename to %.cc.
* configure.ac: Rename server.c to server.cc.
* configure: Re-generate.
Diffstat (limited to 'gdbserver/win32-low.cc')
-rw-r--r-- | gdbserver/win32-low.cc | 1913 |
1 files changed, 1913 insertions, 0 deletions
diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc new file mode 100644 index 0000000..557c90d --- /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 (¤t_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 (¤t_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 (¤t_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 = ¤t_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, ¤t_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 (¤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_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 (¤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 comfortable 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) + { + 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 (¤t_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 (¤t_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 (¤t_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 (); +} |