| /* Target-vector operations for controlling win32 child processes, for GDB. |
| |
| Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, |
| 2005, 2006, 2007, 2008 Free Software Foundation, Inc. |
| |
| Contributed by Cygnus Solutions, A Red Hat Company. |
| |
| 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/>. */ |
| |
| /* Originally by Steve Chamberlain, sac@cygnus.com */ |
| |
| #include "defs.h" |
| #include "frame.h" /* required by inferior.h */ |
| #include "inferior.h" |
| #include "target.h" |
| #include "exceptions.h" |
| #include "gdbcore.h" |
| #include "command.h" |
| #include "completer.h" |
| #include "regcache.h" |
| #include "top.h" |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <fcntl.h> |
| #include <stdlib.h> |
| #include <windows.h> |
| #include <imagehlp.h> |
| #ifdef __CYGWIN__ |
| #include <sys/cygwin.h> |
| #endif |
| #include <signal.h> |
| |
| #include "buildsym.h" |
| #include "symfile.h" |
| #include "objfiles.h" |
| #include "gdb_obstack.h" |
| #include "gdb_string.h" |
| #include "gdbthread.h" |
| #include "gdbcmd.h" |
| #include <sys/param.h> |
| #include <unistd.h> |
| #include "exec.h" |
| #include "solist.h" |
| #include "solib.h" |
| #include "xml-support.h" |
| |
| #include "i386-tdep.h" |
| #include "i387-tdep.h" |
| |
| #include "i386-cygwin-tdep.h" |
| |
| static struct target_ops win32_ops; |
| |
| #ifdef __CYGWIN__ |
| /* The starting and ending address of the cygwin1.dll text segment. */ |
| static bfd_vma cygwin_load_start; |
| static bfd_vma cygwin_load_end; |
| #endif |
| |
| static int have_saved_context; /* True if we've saved context from a cygwin signal. */ |
| static CONTEXT saved_context; /* Containes the saved context from a cygwin signal. */ |
| |
| /* If we're not using the old Cygwin header file set, define the |
| following which never should have been in the generic Win32 API |
| headers in the first place since they were our own invention... */ |
| #ifndef _GNU_H_WINDOWS_H |
| enum |
| { |
| FLAG_TRACE_BIT = 0x100, |
| CONTEXT_DEBUGGER = (CONTEXT_FULL | CONTEXT_FLOATING_POINT) |
| }; |
| #endif |
| #include <psapi.h> |
| |
| #define CONTEXT_DEBUGGER_DR CONTEXT_DEBUGGER | CONTEXT_DEBUG_REGISTERS \ |
| | CONTEXT_EXTENDED_REGISTERS |
| |
| static unsigned dr[8]; |
| static int debug_registers_changed; |
| static int debug_registers_used; |
| #define DR6_CLEAR_VALUE 0xffff0ff0 |
| |
| /* The string sent by cygwin when it processes a signal. |
| FIXME: This should be in a cygwin include file. */ |
| #ifndef _CYGWIN_SIGNAL_STRING |
| #define _CYGWIN_SIGNAL_STRING "cYgSiGw00f" |
| #endif |
| |
| #define CHECK(x) check (x, __FILE__,__LINE__) |
| #define DEBUG_EXEC(x) if (debug_exec) printf_unfiltered x |
| #define DEBUG_EVENTS(x) if (debug_events) printf_unfiltered x |
| #define DEBUG_MEM(x) if (debug_memory) printf_unfiltered x |
| #define DEBUG_EXCEPT(x) if (debug_exceptions) printf_unfiltered x |
| |
| static void win32_stop (ptid_t); |
| static int win32_win32_thread_alive (ptid_t); |
| static void win32_kill_inferior (void); |
| |
| static enum target_signal last_sig = TARGET_SIGNAL_0; |
| /* Set if a signal was received from the debugged process */ |
| |
| /* Thread information structure used to track information that is |
| not available in gdb's thread structure. */ |
| typedef struct thread_info_struct |
| { |
| struct thread_info_struct *next; |
| DWORD id; |
| HANDLE h; |
| char *name; |
| int suspended; |
| int reload_context; |
| CONTEXT context; |
| STACKFRAME sf; |
| } |
| thread_info; |
| |
| static thread_info thread_head; |
| |
| /* The process and thread handles for the above context. */ |
| |
| static DEBUG_EVENT current_event; /* The current debug event from |
| WaitForDebugEvent */ |
| static HANDLE current_process_handle; /* Currently executing process */ |
| static thread_info *current_thread; /* Info on currently selected thread */ |
| static DWORD main_thread_id; /* Thread ID of the main thread */ |
| |
| /* Counts of things. */ |
| static int exception_count = 0; |
| static int event_count = 0; |
| static int saw_create; |
| static int open_process_used = 0; |
| |
| /* User options. */ |
| static int new_console = 0; |
| #ifdef __CYGWIN__ |
| static int cygwin_exceptions = 0; |
| #endif |
| static int new_group = 1; |
| static int debug_exec = 0; /* show execution */ |
| static int debug_events = 0; /* show events from kernel */ |
| static int debug_memory = 0; /* show target memory accesses */ |
| static int debug_exceptions = 0; /* show target exceptions */ |
| static int useshell = 0; /* use shell for subprocesses */ |
| |
| /* This vector maps GDB's idea of a register's number into an address |
| in the win32 exception context vector. |
| |
| It also contains the bit mask needed to load the register in question. |
| |
| One day we could read a reg, we could inspect the context we |
| already have loaded, if it doesn't have the bit set that we need, |
| we read that set of registers in using GetThreadContext. If the |
| context already contains what we need, we just unpack it. Then to |
| write a register, first we have to ensure that the context contains |
| the other regs of the group, and then we copy the info in and set |
| out bit. */ |
| |
| #define context_offset(x) ((int)&(((CONTEXT *)NULL)->x)) |
| static const int mappings[] = |
| { |
| context_offset (Eax), |
| context_offset (Ecx), |
| context_offset (Edx), |
| context_offset (Ebx), |
| context_offset (Esp), |
| context_offset (Ebp), |
| context_offset (Esi), |
| context_offset (Edi), |
| context_offset (Eip), |
| context_offset (EFlags), |
| context_offset (SegCs), |
| context_offset (SegSs), |
| context_offset (SegDs), |
| context_offset (SegEs), |
| context_offset (SegFs), |
| context_offset (SegGs), |
| context_offset (FloatSave.RegisterArea[0 * 10]), |
| context_offset (FloatSave.RegisterArea[1 * 10]), |
| context_offset (FloatSave.RegisterArea[2 * 10]), |
| context_offset (FloatSave.RegisterArea[3 * 10]), |
| context_offset (FloatSave.RegisterArea[4 * 10]), |
| context_offset (FloatSave.RegisterArea[5 * 10]), |
| context_offset (FloatSave.RegisterArea[6 * 10]), |
| context_offset (FloatSave.RegisterArea[7 * 10]), |
| context_offset (FloatSave.ControlWord), |
| context_offset (FloatSave.StatusWord), |
| context_offset (FloatSave.TagWord), |
| context_offset (FloatSave.ErrorSelector), |
| context_offset (FloatSave.ErrorOffset), |
| context_offset (FloatSave.DataSelector), |
| context_offset (FloatSave.DataOffset), |
| context_offset (FloatSave.ErrorSelector) |
| /* XMM0-7 */ , |
| context_offset (ExtendedRegisters[10*16]), |
| context_offset (ExtendedRegisters[11*16]), |
| context_offset (ExtendedRegisters[12*16]), |
| context_offset (ExtendedRegisters[13*16]), |
| context_offset (ExtendedRegisters[14*16]), |
| context_offset (ExtendedRegisters[15*16]), |
| context_offset (ExtendedRegisters[16*16]), |
| context_offset (ExtendedRegisters[17*16]), |
| /* MXCSR */ |
| context_offset (ExtendedRegisters[24]) |
| }; |
| |
| #undef context_offset |
| |
| /* This vector maps the target's idea of an exception (extracted |
| from the DEBUG_EVENT structure) to GDB's idea. */ |
| |
| struct xlate_exception |
| { |
| int them; |
| enum target_signal us; |
| }; |
| |
| static const struct xlate_exception |
| xlate[] = |
| { |
| {EXCEPTION_ACCESS_VIOLATION, TARGET_SIGNAL_SEGV}, |
| {STATUS_STACK_OVERFLOW, TARGET_SIGNAL_SEGV}, |
| {EXCEPTION_BREAKPOINT, TARGET_SIGNAL_TRAP}, |
| {DBG_CONTROL_C, TARGET_SIGNAL_INT}, |
| {EXCEPTION_SINGLE_STEP, TARGET_SIGNAL_TRAP}, |
| {STATUS_FLOAT_DIVIDE_BY_ZERO, TARGET_SIGNAL_FPE}, |
| {-1, -1}}; |
| |
| static void |
| check (BOOL ok, const char *file, int line) |
| { |
| if (!ok) |
| printf_filtered ("error return %s:%d was %lu\n", file, line, |
| GetLastError ()); |
| } |
| |
| /* Find a thread record given a thread id. If GET_CONTEXT is not 0, |
| then also retrieve the context for this thread. If GET_CONTEXT is |
| negative, then don't suspend the thread. */ |
| static thread_info * |
| thread_rec (DWORD id, int get_context) |
| { |
| thread_info *th; |
| |
| for (th = &thread_head; (th = th->next) != NULL;) |
| if (th->id == id) |
| { |
| if (!th->suspended && get_context) |
| { |
| if (get_context > 0 && id != current_event.dwThreadId) |
| { |
| if (SuspendThread (th->h) == (DWORD) -1) |
| { |
| DWORD err = GetLastError (); |
| warning (_("SuspendThread failed. (winerr %d)"), |
| (int) err); |
| return NULL; |
| } |
| th->suspended = 1; |
| } |
| else if (get_context < 0) |
| th->suspended = -1; |
| th->reload_context = 1; |
| } |
| return th; |
| } |
| |
| return NULL; |
| } |
| |
| /* Add a thread to the thread list. */ |
| static thread_info * |
| win32_add_thread (ptid_t ptid, HANDLE h) |
| { |
| thread_info *th; |
| DWORD id; |
| |
| gdb_assert (ptid_get_tid (ptid) != 0); |
| |
| id = ptid_get_tid (ptid); |
| |
| if ((th = thread_rec (id, FALSE))) |
| return th; |
| |
| th = XZALLOC (thread_info); |
| th->id = id; |
| th->h = h; |
| th->next = thread_head.next; |
| thread_head.next = th; |
| add_thread (ptid); |
| /* Set the debug registers for the new thread if they are used. */ |
| if (debug_registers_used) |
| { |
| /* Only change the value of the debug registers. */ |
| th->context.ContextFlags = CONTEXT_DEBUG_REGISTERS; |
| CHECK (GetThreadContext (th->h, &th->context)); |
| th->context.Dr0 = dr[0]; |
| th->context.Dr1 = dr[1]; |
| th->context.Dr2 = dr[2]; |
| th->context.Dr3 = dr[3]; |
| th->context.Dr6 = DR6_CLEAR_VALUE; |
| th->context.Dr7 = dr[7]; |
| CHECK (SetThreadContext (th->h, &th->context)); |
| th->context.ContextFlags = 0; |
| } |
| return th; |
| } |
| |
| /* Clear out any old thread list and reintialize it to a |
| pristine state. */ |
| static void |
| win32_init_thread_list (void) |
| { |
| thread_info *th = &thread_head; |
| |
| DEBUG_EVENTS (("gdb: win32_init_thread_list\n")); |
| init_thread_list (); |
| while (th->next != NULL) |
| { |
| thread_info *here = th->next; |
| th->next = here->next; |
| xfree (here); |
| } |
| thread_head.next = NULL; |
| } |
| |
| /* Delete a thread from the list of threads */ |
| static void |
| win32_delete_thread (ptid_t ptid) |
| { |
| thread_info *th; |
| DWORD id; |
| |
| gdb_assert (ptid_get_tid (ptid) != 0); |
| |
| id = ptid_get_tid (ptid); |
| |
| if (info_verbose) |
| printf_unfiltered ("[Deleting %s]\n", target_pid_to_str (ptid)); |
| delete_thread (ptid); |
| |
| for (th = &thread_head; |
| th->next != NULL && th->next->id != id; |
| th = th->next) |
| continue; |
| |
| if (th->next != NULL) |
| { |
| thread_info *here = th->next; |
| th->next = here->next; |
| xfree (here); |
| } |
| } |
| |
| static void |
| do_win32_fetch_inferior_registers (struct regcache *regcache, int r) |
| { |
| char *context_offset = ((char *) ¤t_thread->context) + mappings[r]; |
| struct gdbarch *gdbarch = get_regcache_arch (regcache); |
| struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); |
| long l; |
| |
| if (!current_thread) |
| return; /* Windows sometimes uses a non-existent thread id in its |
| events */ |
| |
| if (current_thread->reload_context) |
| { |
| #ifdef __COPY_CONTEXT_SIZE |
| if (have_saved_context) |
| { |
| /* Lie about where the program actually is stopped since cygwin has informed us that |
| we should consider the signal to have occurred at another location which is stored |
| in "saved_context. */ |
| memcpy (¤t_thread->context, &saved_context, __COPY_CONTEXT_SIZE); |
| have_saved_context = 0; |
| } |
| else |
| #endif |
| { |
| thread_info *th = current_thread; |
| th->context.ContextFlags = CONTEXT_DEBUGGER_DR; |
| GetThreadContext (th->h, &th->context); |
| /* Copy dr values from that thread. |
| But only if there were not modified since last stop. PR gdb/2388 */ |
| if (!debug_registers_changed) |
| { |
| dr[0] = th->context.Dr0; |
| dr[1] = th->context.Dr1; |
| dr[2] = th->context.Dr2; |
| dr[3] = th->context.Dr3; |
| dr[6] = th->context.Dr6; |
| dr[7] = th->context.Dr7; |
| } |
| } |
| current_thread->reload_context = 0; |
| } |
| |
| if (r == I387_FISEG_REGNUM (tdep)) |
| { |
| l = *((long *) context_offset) & 0xffff; |
| regcache_raw_supply (regcache, r, (char *) &l); |
| } |
| else if (r == I387_FOP_REGNUM (tdep)) |
| { |
| l = (*((long *) context_offset) >> 16) & ((1 << 11) - 1); |
| regcache_raw_supply (regcache, r, (char *) &l); |
| } |
| else if (r >= 0) |
| regcache_raw_supply (regcache, r, context_offset); |
| else |
| { |
| for (r = 0; r < gdbarch_num_regs (gdbarch); r++) |
| do_win32_fetch_inferior_registers (regcache, r); |
| } |
| } |
| |
| static void |
| win32_fetch_inferior_registers (struct regcache *regcache, int r) |
| { |
| current_thread = thread_rec (ptid_get_tid (inferior_ptid), TRUE); |
| /* Check if current_thread exists. Windows sometimes uses a non-existent |
| thread id in its events */ |
| if (current_thread) |
| do_win32_fetch_inferior_registers (regcache, r); |
| } |
| |
| static void |
| do_win32_store_inferior_registers (const struct regcache *regcache, int r) |
| { |
| if (!current_thread) |
| /* Windows sometimes uses a non-existent thread id in its events */; |
| else if (r >= 0) |
| regcache_raw_collect (regcache, r, |
| ((char *) ¤t_thread->context) + mappings[r]); |
| else |
| { |
| for (r = 0; r < gdbarch_num_regs (get_regcache_arch (regcache)); r++) |
| do_win32_store_inferior_registers (regcache, r); |
| } |
| } |
| |
| /* Store a new register value into the current thread context */ |
| static void |
| win32_store_inferior_registers (struct regcache *regcache, int r) |
| { |
| current_thread = thread_rec (ptid_get_tid (inferior_ptid), TRUE); |
| /* Check if current_thread exists. Windows sometimes uses a non-existent |
| thread id in its events */ |
| if (current_thread) |
| do_win32_store_inferior_registers (regcache, r); |
| } |
| |
| static int psapi_loaded = 0; |
| static BOOL WINAPI (*psapi_EnumProcessModules) (HANDLE, HMODULE *, DWORD, |
| LPDWORD); |
| static BOOL WINAPI (*psapi_GetModuleInformation) (HANDLE, HMODULE, LPMODULEINFO, |
| DWORD); |
| static DWORD WINAPI (*psapi_GetModuleFileNameExA) (HANDLE, HMODULE, LPSTR, |
| DWORD); |
| |
| /* Get the name of a given module at at given base address. If base_address |
| is zero return the first loaded module (which is always the name of the |
| executable). */ |
| static int |
| get_module_name (DWORD base_address, char *dll_name_ret) |
| { |
| DWORD len; |
| MODULEINFO mi; |
| int i; |
| HMODULE dh_buf[1]; |
| HMODULE *DllHandle = dh_buf; /* Set to temporary storage for initial query */ |
| DWORD cbNeeded; |
| #ifdef __CYGWIN__ |
| char pathbuf[PATH_MAX + 1]; /* Temporary storage prior to converting to |
| posix form */ |
| #else |
| char *pathbuf = dll_name_ret; /* Just copy directly to passed-in arg */ |
| #endif |
| |
| /* If psapi_loaded < 0 either psapi.dll is not available or it does not contain |
| the needed functions. */ |
| if (psapi_loaded <= 0) |
| goto failed; |
| |
| cbNeeded = 0; |
| /* Find size of buffer needed to handle list of modules loaded in inferior */ |
| if (!psapi_EnumProcessModules (current_process_handle, DllHandle, |
| sizeof (HMODULE), &cbNeeded) || !cbNeeded) |
| goto failed; |
| |
| /* Allocate correct amount of space for module list */ |
| DllHandle = (HMODULE *) alloca (cbNeeded); |
| if (!DllHandle) |
| goto failed; |
| |
| /* Get the list of modules */ |
| if (!psapi_EnumProcessModules (current_process_handle, DllHandle, cbNeeded, |
| &cbNeeded)) |
| goto failed; |
| |
| for (i = 0; i < (int) (cbNeeded / sizeof (HMODULE)); i++) |
| { |
| /* Get information on this module */ |
| if (!psapi_GetModuleInformation (current_process_handle, DllHandle[i], |
| &mi, sizeof (mi))) |
| error (_("Can't get module info")); |
| |
| if (!base_address || (DWORD) (mi.lpBaseOfDll) == base_address) |
| { |
| /* Try to find the name of the given module */ |
| len = psapi_GetModuleFileNameExA (current_process_handle, |
| DllHandle[i], pathbuf, MAX_PATH); |
| if (len == 0) |
| error (_("Error getting dll name: %u."), (unsigned) GetLastError ()); |
| #ifdef __CYGWIN__ |
| /* Cygwin prefers that the path be in /x/y/z format */ |
| cygwin_conv_to_full_posix_path (pathbuf, dll_name_ret); |
| #endif |
| return 1; /* success */ |
| } |
| } |
| |
| failed: |
| dll_name_ret[0] = '\0'; |
| return 0; /* failure */ |
| } |
| |
| /* Encapsulate the information required in a call to |
| symbol_file_add_args */ |
| struct safe_symbol_file_add_args |
| { |
| char *name; |
| int from_tty; |
| struct section_addr_info *addrs; |
| int mainline; |
| int flags; |
| struct ui_file *err, *out; |
| struct objfile *ret; |
| }; |
| |
| /* Maintain a linked list of "so" information. */ |
| struct lm_info |
| { |
| DWORD load_addr; |
| }; |
| |
| static struct so_list solib_start, *solib_end; |
| |
| /* Call symbol_file_add with stderr redirected. We don't care if there |
| are errors. */ |
| static int |
| safe_symbol_file_add_stub (void *argv) |
| { |
| #define p ((struct safe_symbol_file_add_args *) argv) |
| p->ret = symbol_file_add (p->name, p->from_tty, p->addrs, p->mainline, p->flags); |
| return !!p->ret; |
| #undef p |
| } |
| |
| /* Restore gdb's stderr after calling symbol_file_add */ |
| static void |
| safe_symbol_file_add_cleanup (void *p) |
| { |
| #define sp ((struct safe_symbol_file_add_args *)p) |
| gdb_flush (gdb_stderr); |
| gdb_flush (gdb_stdout); |
| ui_file_delete (gdb_stderr); |
| ui_file_delete (gdb_stdout); |
| gdb_stderr = sp->err; |
| gdb_stdout = sp->out; |
| #undef sp |
| } |
| |
| /* symbol_file_add wrapper that prevents errors from being displayed. */ |
| static struct objfile * |
| safe_symbol_file_add (char *name, int from_tty, |
| struct section_addr_info *addrs, |
| int mainline, int flags) |
| { |
| struct safe_symbol_file_add_args p; |
| struct cleanup *cleanup; |
| |
| cleanup = make_cleanup (safe_symbol_file_add_cleanup, &p); |
| |
| p.err = gdb_stderr; |
| p.out = gdb_stdout; |
| gdb_flush (gdb_stderr); |
| gdb_flush (gdb_stdout); |
| gdb_stderr = ui_file_new (); |
| gdb_stdout = ui_file_new (); |
| p.name = name; |
| p.from_tty = from_tty; |
| p.addrs = addrs; |
| p.mainline = mainline; |
| p.flags = flags; |
| catch_errors (safe_symbol_file_add_stub, &p, "", RETURN_MASK_ERROR); |
| |
| do_cleanups (cleanup); |
| return p.ret; |
| } |
| |
| static struct so_list * |
| win32_make_so (const char *name, DWORD load_addr) |
| { |
| struct so_list *so; |
| char buf[MAX_PATH + 1]; |
| char cwd[MAX_PATH + 1]; |
| char *p; |
| WIN32_FIND_DATA w32_fd; |
| HANDLE h = FindFirstFile(name, &w32_fd); |
| MEMORY_BASIC_INFORMATION m; |
| |
| if (h == INVALID_HANDLE_VALUE) |
| strcpy (buf, name); |
| else |
| { |
| FindClose (h); |
| strcpy (buf, name); |
| if (GetCurrentDirectory (MAX_PATH + 1, cwd)) |
| { |
| p = strrchr (buf, '\\'); |
| if (p) |
| p[1] = '\0'; |
| SetCurrentDirectory (buf); |
| GetFullPathName (w32_fd.cFileName, MAX_PATH, buf, &p); |
| SetCurrentDirectory (cwd); |
| } |
| } |
| |
| if (strcasecmp (buf, "ntdll.dll") == 0) |
| { |
| GetSystemDirectory (buf, sizeof (buf)); |
| strcat (buf, "\\ntdll.dll"); |
| } |
| so = XZALLOC (struct so_list); |
| so->lm_info = (struct lm_info *) xmalloc (sizeof (struct lm_info)); |
| so->lm_info->load_addr = load_addr; |
| strcpy (so->so_original_name, name); |
| #ifndef __CYGWIN__ |
| strcpy (so->so_name, buf); |
| #else |
| cygwin_conv_to_posix_path (buf, so->so_name); |
| /* Record cygwin1.dll .text start/end. */ |
| p = strchr (so->so_name, '\0') - (sizeof ("/cygwin1.dll") - 1); |
| if (p >= so->so_name && strcasecmp (p, "/cygwin1.dll") == 0) |
| { |
| bfd *abfd; |
| asection *text = NULL; |
| CORE_ADDR text_vma; |
| |
| abfd = bfd_openr (so->so_name, "pei-i386"); |
| |
| if (!abfd) |
| return so; |
| |
| if (bfd_check_format (abfd, bfd_object)) |
| text = bfd_get_section_by_name (abfd, ".text"); |
| |
| if (!text) |
| { |
| bfd_close (abfd); |
| return so; |
| } |
| |
| /* The symbols in a dll are offset by 0x1000, which is the the |
| offset from 0 of the first byte in an image - because of the |
| file header and the section alignment. */ |
| cygwin_load_start = load_addr + 0x1000; |
| cygwin_load_end = cygwin_load_start + bfd_section_size (abfd, text); |
| |
| bfd_close (abfd); |
| } |
| #endif |
| |
| return so; |
| } |
| |
| 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]; |
| DWORD 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; |
| |
| /* 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; |
| |
| /* Find the length of the string */ |
| while (ReadProcessMemory (h, address_ptr + len++ * size, &b, size, &done) |
| && (b[0] != 0 || b[size - 1] != 0) && done == size) |
| continue; |
| |
| if (!unicode) |
| ReadProcessMemory (h, address_ptr, buf, len, &done); |
| else |
| { |
| WCHAR *unicode_address = (WCHAR *) alloca (len * sizeof (WCHAR)); |
| ReadProcessMemory (h, address_ptr, unicode_address, len * sizeof (WCHAR), |
| &done); |
| |
| WideCharToMultiByte (CP_ACP, 0, unicode_address, len, buf, len, 0, 0); |
| } |
| |
| return buf; |
| } |
| |
| /* Wait for child to do something. Return pid of child, or -1 in case |
| of error; store status through argument pointer OURSTATUS. */ |
| static int |
| handle_load_dll (void *dummy) |
| { |
| LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll; |
| char dll_buf[MAX_PATH + 1]; |
| char *dll_name = NULL; |
| |
| dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0'; |
| |
| if (!get_module_name ((DWORD) event->lpBaseOfDll, dll_buf)) |
| dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0'; |
| |
| dll_name = dll_buf; |
| |
| if (*dll_name == '\0') |
| dll_name = get_image_name (current_process_handle, |
| event->lpImageName, event->fUnicode); |
| if (!dll_name) |
| return 1; |
| |
| solib_end->next = win32_make_so (dll_name, (DWORD) event->lpBaseOfDll); |
| solib_end = solib_end->next; |
| |
| DEBUG_EVENTS (("gdb: Loading dll \"%s\" at 0x%lx.\n", solib_end->so_name, |
| (DWORD) solib_end->lm_info->load_addr)); |
| |
| return 1; |
| } |
| |
| static void |
| win32_free_so (struct so_list *so) |
| { |
| if (so->lm_info) |
| xfree (so->lm_info); |
| xfree (so); |
| } |
| |
| static int |
| handle_unload_dll (void *dummy) |
| { |
| DWORD lpBaseOfDll = (DWORD) current_event.u.UnloadDll.lpBaseOfDll; |
| struct so_list *so; |
| |
| for (so = &solib_start; so->next != NULL; so = so->next) |
| if (so->next->lm_info->load_addr == lpBaseOfDll) |
| { |
| struct so_list *sodel = so->next; |
| so->next = sodel->next; |
| if (!so->next) |
| solib_end = so; |
| DEBUG_EVENTS (("gdb: Unloading dll \"%s\".\n", sodel->so_name)); |
| |
| win32_free_so (sodel); |
| solib_add (NULL, 0, NULL, auto_solib_add); |
| return 1; |
| } |
| |
| error (_("Error: dll starting at 0x%lx not found."), (DWORD) lpBaseOfDll); |
| |
| return 0; |
| } |
| |
| /* Clear list of loaded DLLs. */ |
| static void |
| win32_clear_solib (void) |
| { |
| solib_start.next = NULL; |
| solib_end = &solib_start; |
| } |
| |
| /* Load DLL symbol info. */ |
| void |
| dll_symbol_command (char *args, int from_tty) |
| { |
| int n; |
| dont_repeat (); |
| |
| if (args == NULL) |
| error (_("dll-symbols requires a file name")); |
| |
| n = strlen (args); |
| if (n > 4 && strcasecmp (args + n - 4, ".dll") != 0) |
| { |
| char *newargs = (char *) alloca (n + 4 + 1); |
| strcpy (newargs, args); |
| strcat (newargs, ".dll"); |
| args = newargs; |
| } |
| |
| safe_symbol_file_add (args, from_tty, NULL, 0, OBJF_SHARED | OBJF_USERLOADED); |
| } |
| |
| /* Handle DEBUG_STRING output from child process. |
| Cygwin prepends its messages with a "cygwin:". Interpret this as |
| a Cygwin signal. Otherwise just print the string as a warning. */ |
| static int |
| handle_output_debug_string (struct target_waitstatus *ourstatus) |
| { |
| char *s = NULL; |
| int retval = 0; |
| |
| if (!target_read_string |
| ((CORE_ADDR) (uintptr_t) current_event.u.DebugString.lpDebugStringData, |
| &s, 1024, 0) |
| || !s || !*s) |
| /* nothing to do */; |
| else if (strncmp (s, _CYGWIN_SIGNAL_STRING, sizeof (_CYGWIN_SIGNAL_STRING) - 1) != 0) |
| { |
| #ifdef __CYGWIN__ |
| if (strncmp (s, "cYg", 3) != 0) |
| #endif |
| warning (("%s"), s); |
| } |
| #ifdef __COPY_CONTEXT_SIZE |
| else |
| { |
| /* Got a cygwin signal marker. A cygwin signal is followed by the signal number |
| itself and then optionally followed by the thread id and address to saved context |
| within the DLL. If these are supplied, then the given thread is assumed to have |
| issued the signal and the context from the thread is assumed to be stored at the |
| given address in the inferior. Tell gdb to treat this like a real signal. */ |
| char *p; |
| int sig = strtol (s + sizeof (_CYGWIN_SIGNAL_STRING) - 1, &p, 0); |
| int gotasig = target_signal_from_host (sig); |
| ourstatus->value.sig = gotasig; |
| if (gotasig) |
| { |
| LPCVOID x; |
| DWORD n; |
| ourstatus->kind = TARGET_WAITKIND_STOPPED; |
| retval = strtoul (p, &p, 0); |
| if (!retval) |
| retval = main_thread_id; |
| else if ((x = (LPCVOID) strtoul (p, &p, 0)) |
| && ReadProcessMemory (current_process_handle, x, |
| &saved_context, __COPY_CONTEXT_SIZE, &n) |
| && n == __COPY_CONTEXT_SIZE) |
| have_saved_context = 1; |
| current_event.dwThreadId = retval; |
| } |
| } |
| #endif |
| |
| if (s) |
| xfree (s); |
| return retval; |
| } |
| |
| static int |
| display_selector (HANDLE thread, DWORD sel) |
| { |
| LDT_ENTRY info; |
| if (GetThreadSelectorEntry (thread, sel, &info)) |
| { |
| int base, limit; |
| printf_filtered ("0x%03lx: ", sel); |
| if (!info.HighWord.Bits.Pres) |
| { |
| puts_filtered ("Segment not present\n"); |
| return 0; |
| } |
| base = (info.HighWord.Bits.BaseHi << 24) + |
| (info.HighWord.Bits.BaseMid << 16) |
| + info.BaseLow; |
| limit = (info.HighWord.Bits.LimitHi << 16) + info.LimitLow; |
| if (info.HighWord.Bits.Granularity) |
| limit = (limit << 12) | 0xfff; |
| printf_filtered ("base=0x%08x limit=0x%08x", base, limit); |
| if (info.HighWord.Bits.Default_Big) |
| puts_filtered(" 32-bit "); |
| else |
| puts_filtered(" 16-bit "); |
| switch ((info.HighWord.Bits.Type & 0xf) >> 1) |
| { |
| case 0: |
| puts_filtered ("Data (Read-Only, Exp-up"); |
| break; |
| case 1: |
| puts_filtered ("Data (Read/Write, Exp-up"); |
| break; |
| case 2: |
| puts_filtered ("Unused segment ("); |
| break; |
| case 3: |
| puts_filtered ("Data (Read/Write, Exp-down"); |
| break; |
| case 4: |
| puts_filtered ("Code (Exec-Only, N.Conf"); |
| break; |
| case 5: |
| puts_filtered ("Code (Exec/Read, N.Conf"); |
| break; |
| case 6: |
| puts_filtered ("Code (Exec-Only, Conf"); |
| break; |
| case 7: |
| puts_filtered ("Code (Exec/Read, Conf"); |
| break; |
| default: |
| printf_filtered ("Unknown type 0x%x",info.HighWord.Bits.Type); |
| } |
| if ((info.HighWord.Bits.Type & 0x1) == 0) |
| puts_filtered(", N.Acc"); |
| puts_filtered (")\n"); |
| if ((info.HighWord.Bits.Type & 0x10) == 0) |
| puts_filtered("System selector "); |
| printf_filtered ("Priviledge level = %d. ", info.HighWord.Bits.Dpl); |
| if (info.HighWord.Bits.Granularity) |
| puts_filtered ("Page granular.\n"); |
| else |
| puts_filtered ("Byte granular.\n"); |
| return 1; |
| } |
| else |
| { |
| printf_filtered ("Invalid selector 0x%lx.\n",sel); |
| return 0; |
| } |
| } |
| |
| static void |
| display_selectors (char * args, int from_tty) |
| { |
| if (!current_thread) |
| { |
| puts_filtered ("Impossible to display selectors now.\n"); |
| return; |
| } |
| if (!args) |
| { |
| |
| puts_filtered ("Selector $cs\n"); |
| display_selector (current_thread->h, |
| current_thread->context.SegCs); |
| puts_filtered ("Selector $ds\n"); |
| display_selector (current_thread->h, |
| current_thread->context.SegDs); |
| puts_filtered ("Selector $es\n"); |
| display_selector (current_thread->h, |
| current_thread->context.SegEs); |
| puts_filtered ("Selector $ss\n"); |
| display_selector (current_thread->h, |
| current_thread->context.SegSs); |
| puts_filtered ("Selector $fs\n"); |
| display_selector (current_thread->h, |
| current_thread->context.SegFs); |
| puts_filtered ("Selector $gs\n"); |
| display_selector (current_thread->h, |
| current_thread->context.SegGs); |
| } |
| else |
| { |
| int sel; |
| sel = parse_and_eval_long (args); |
| printf_filtered ("Selector \"%s\"\n",args); |
| display_selector (current_thread->h, sel); |
| } |
| } |
| |
| static struct cmd_list_element *info_w32_cmdlist = NULL; |
| |
| static void |
| info_w32_command (char *args, int from_tty) |
| { |
| help_list (info_w32_cmdlist, "info w32 ", class_info, gdb_stdout); |
| } |
| |
| |
| #define DEBUG_EXCEPTION_SIMPLE(x) if (debug_exceptions) \ |
| printf_unfiltered ("gdb: Target exception %s at 0x%08lx\n", x, \ |
| (DWORD) current_event.u.Exception.ExceptionRecord.ExceptionAddress) |
| |
| static int |
| handle_exception (struct target_waitstatus *ourstatus) |
| { |
| thread_info *th; |
| DWORD code = current_event.u.Exception.ExceptionRecord.ExceptionCode; |
| |
| ourstatus->kind = TARGET_WAITKIND_STOPPED; |
| |
| /* Record the context of the current thread */ |
| th = thread_rec (current_event.dwThreadId, -1); |
| |
| switch (code) |
| { |
| case EXCEPTION_ACCESS_VIOLATION: |
| DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ACCESS_VIOLATION"); |
| ourstatus->value.sig = TARGET_SIGNAL_SEGV; |
| #ifdef __CYGWIN__ |
| { |
| /* See if the access violation happened within the cygwin DLL itself. Cygwin uses |
| a kind of exception handling to deal with passed-in invalid addresses. gdb |
| should not treat these as real SEGVs since they will be silently handled by |
| cygwin. A real SEGV will (theoretically) be caught by cygwin later in the process |
| and will be sent as a cygwin-specific-signal. So, ignore SEGVs if they show up |
| within the text segment of the DLL itself. */ |
| char *fn; |
| bfd_vma addr = (bfd_vma) (uintptr_t) current_event.u.Exception. |
| ExceptionRecord.ExceptionAddress; |
| if ((!cygwin_exceptions && (addr >= cygwin_load_start && addr < cygwin_load_end)) |
| || (find_pc_partial_function (addr, &fn, NULL, NULL) |
| && strncmp (fn, "KERNEL32!IsBad", strlen ("KERNEL32!IsBad")) == 0)) |
| return 0; |
| } |
| #endif |
| break; |
| case STATUS_STACK_OVERFLOW: |
| DEBUG_EXCEPTION_SIMPLE ("STATUS_STACK_OVERFLOW"); |
| ourstatus->value.sig = TARGET_SIGNAL_SEGV; |
| break; |
| case STATUS_FLOAT_DENORMAL_OPERAND: |
| DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_DENORMAL_OPERAND"); |
| ourstatus->value.sig = TARGET_SIGNAL_FPE; |
| break; |
| case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: |
| DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ARRAY_BOUNDS_EXCEEDED"); |
| ourstatus->value.sig = TARGET_SIGNAL_FPE; |
| break; |
| case STATUS_FLOAT_INEXACT_RESULT: |
| DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_INEXACT_RESULT"); |
| ourstatus->value.sig = TARGET_SIGNAL_FPE; |
| break; |
| case STATUS_FLOAT_INVALID_OPERATION: |
| DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_INVALID_OPERATION"); |
| ourstatus->value.sig = TARGET_SIGNAL_FPE; |
| break; |
| case STATUS_FLOAT_OVERFLOW: |
| DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_OVERFLOW"); |
| ourstatus->value.sig = TARGET_SIGNAL_FPE; |
| break; |
| case STATUS_FLOAT_STACK_CHECK: |
| DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_STACK_CHECK"); |
| ourstatus->value.sig = TARGET_SIGNAL_FPE; |
| break; |
| case STATUS_FLOAT_UNDERFLOW: |
| DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_UNDERFLOW"); |
| ourstatus->value.sig = TARGET_SIGNAL_FPE; |
| break; |
| case STATUS_FLOAT_DIVIDE_BY_ZERO: |
| DEBUG_EXCEPTION_SIMPLE ("STATUS_FLOAT_DIVIDE_BY_ZERO"); |
| ourstatus->value.sig = TARGET_SIGNAL_FPE; |
| break; |
| case STATUS_INTEGER_DIVIDE_BY_ZERO: |
| DEBUG_EXCEPTION_SIMPLE ("STATUS_INTEGER_DIVIDE_BY_ZERO"); |
| ourstatus->value.sig = TARGET_SIGNAL_FPE; |
| break; |
| case STATUS_INTEGER_OVERFLOW: |
| DEBUG_EXCEPTION_SIMPLE ("STATUS_INTEGER_OVERFLOW"); |
| ourstatus->value.sig = TARGET_SIGNAL_FPE; |
| break; |
| case EXCEPTION_BREAKPOINT: |
| DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_BREAKPOINT"); |
| ourstatus->value.sig = TARGET_SIGNAL_TRAP; |
| break; |
| case DBG_CONTROL_C: |
| DEBUG_EXCEPTION_SIMPLE ("DBG_CONTROL_C"); |
| ourstatus->value.sig = TARGET_SIGNAL_INT; |
| break; |
| case DBG_CONTROL_BREAK: |
| DEBUG_EXCEPTION_SIMPLE ("DBG_CONTROL_BREAK"); |
| ourstatus->value.sig = TARGET_SIGNAL_INT; |
| break; |
| case EXCEPTION_SINGLE_STEP: |
| DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_SINGLE_STEP"); |
| ourstatus->value.sig = TARGET_SIGNAL_TRAP; |
| break; |
| case EXCEPTION_ILLEGAL_INSTRUCTION: |
| DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ILLEGAL_INSTRUCTION"); |
| ourstatus->value.sig = TARGET_SIGNAL_ILL; |
| break; |
| case EXCEPTION_PRIV_INSTRUCTION: |
| DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_PRIV_INSTRUCTION"); |
| ourstatus->value.sig = TARGET_SIGNAL_ILL; |
| break; |
| case EXCEPTION_NONCONTINUABLE_EXCEPTION: |
| DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_NONCONTINUABLE_EXCEPTION"); |
| ourstatus->value.sig = TARGET_SIGNAL_ILL; |
| break; |
| default: |
| /* Treat unhandled first chance exceptions specially. */ |
| if (current_event.u.Exception.dwFirstChance) |
| return -1; |
| printf_unfiltered ("gdb: unknown target exception 0x%08lx at 0x%08lx\n", |
| current_event.u.Exception.ExceptionRecord.ExceptionCode, |
| (DWORD) current_event.u.Exception.ExceptionRecord.ExceptionAddress); |
| ourstatus->value.sig = TARGET_SIGNAL_UNKNOWN; |
| break; |
| } |
| exception_count++; |
| last_sig = ourstatus->value.sig; |
| return 1; |
| } |
| |
| /* Resume all artificially suspended threads if we are continuing |
| execution */ |
| static BOOL |
| win32_continue (DWORD continue_status, int id) |
| { |
| int i; |
| thread_info *th; |
| BOOL res; |
| |
| DEBUG_EVENTS (("ContinueDebugEvent (cpid=%ld, ctid=%ld, %s);\n", |
| current_event.dwProcessId, current_event.dwThreadId, |
| continue_status == DBG_CONTINUE ? |
| "DBG_CONTINUE" : "DBG_EXCEPTION_NOT_HANDLED")); |
| |
| for (th = &thread_head; (th = th->next) != NULL;) |
| if ((id == -1 || id == (int) th->id) |
| && th->suspended) |
| { |
| if (debug_registers_changed) |
| { |
| th->context.ContextFlags |= CONTEXT_DEBUG_REGISTERS; |
| th->context.Dr0 = dr[0]; |
| th->context.Dr1 = dr[1]; |
| th->context.Dr2 = dr[2]; |
| th->context.Dr3 = dr[3]; |
| th->context.Dr6 = DR6_CLEAR_VALUE; |
| th->context.Dr7 = dr[7]; |
| } |
| if (th->context.ContextFlags) |
| { |
| CHECK (SetThreadContext (th->h, &th->context)); |
| th->context.ContextFlags = 0; |
| } |
| if (th->suspended > 0) |
| (void) ResumeThread (th->h); |
| th->suspended = 0; |
| } |
| |
| res = ContinueDebugEvent (current_event.dwProcessId, |
| current_event.dwThreadId, |
| continue_status); |
| |
| debug_registers_changed = 0; |
| return res; |
| } |
| |
| /* Called in pathological case where Windows fails to send a |
| CREATE_PROCESS_DEBUG_EVENT after an attach. */ |
| static DWORD |
| fake_create_process (void) |
| { |
| current_process_handle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, |
| current_event.dwProcessId); |
| if (current_process_handle != NULL) |
| open_process_used = 1; |
| else |
| { |
| error (_("OpenProcess call failed, GetLastError = %lud\n"), |
| GetLastError ()); |
| /* We can not debug anything in that case. */ |
| } |
| main_thread_id = current_event.dwThreadId; |
| current_thread = win32_add_thread (ptid_build (current_event.dwProcessId, 0, |
| current_event.dwThreadId), |
| current_event.u.CreateThread.hThread); |
| return main_thread_id; |
| } |
| |
| static void |
| win32_resume (ptid_t ptid, int step, enum target_signal sig) |
| { |
| thread_info *th; |
| DWORD continue_status = DBG_CONTINUE; |
| |
| /* A specific PTID means `step only this thread id'. */ |
| int resume_all = ptid_equal (ptid, minus_one_ptid); |
| |
| /* If we're continuing all threads, it's the current inferior that |
| should be handled specially. */ |
| if (resume_all) |
| ptid = inferior_ptid; |
| |
| if (sig != TARGET_SIGNAL_0) |
| { |
| if (current_event.dwDebugEventCode != EXCEPTION_DEBUG_EVENT) |
| { |
| DEBUG_EXCEPT(("Cannot continue with signal %d here.\n",sig)); |
| } |
| else if (sig == last_sig) |
| continue_status = DBG_EXCEPTION_NOT_HANDLED; |
| else |
| #if 0 |
| /* This code does not seem to work, because |
| the kernel does probably not consider changes in the ExceptionRecord |
| structure when passing the exception to the inferior. |
| Note that this seems possible in the exception handler itself. */ |
| { |
| int i; |
| for (i = 0; xlate[i].them != -1; i++) |
| if (xlate[i].us == sig) |
| { |
| current_event.u.Exception.ExceptionRecord.ExceptionCode = |
| xlate[i].them; |
| continue_status = DBG_EXCEPTION_NOT_HANDLED; |
| break; |
| } |
| if (continue_status == DBG_CONTINUE) |
| { |
| DEBUG_EXCEPT(("Cannot continue with signal %d.\n",sig)); |
| } |
| } |
| #endif |
| DEBUG_EXCEPT(("Can only continue with recieved signal %d.\n", |
| last_sig)); |
| } |
| |
| last_sig = TARGET_SIGNAL_0; |
| |
| DEBUG_EXEC (("gdb: win32_resume (pid=%d, tid=%ld, step=%d, sig=%d);\n", |
| ptid_get_pid (ptid), ptid_get_tid (ptid), step, sig)); |
| |
| /* Get context for currently selected thread */ |
| th = thread_rec (ptid_get_tid (inferior_ptid), FALSE); |
| if (th) |
| { |
| if (step) |
| { |
| /* Single step by setting t bit */ |
| win32_fetch_inferior_registers (get_current_regcache (), |
| gdbarch_ps_regnum (current_gdbarch)); |
| th->context.EFlags |= FLAG_TRACE_BIT; |
| } |
| |
| if (th->context.ContextFlags) |
| { |
| if (debug_registers_changed) |
| { |
| th->context.Dr0 = dr[0]; |
| th->context.Dr1 = dr[1]; |
| th->context.Dr2 = dr[2]; |
| th->context.Dr3 = dr[3]; |
| th->context.Dr6 = DR6_CLEAR_VALUE; |
| th->context.Dr7 = dr[7]; |
| } |
| CHECK (SetThreadContext (th->h, &th->context)); |
| th->context.ContextFlags = 0; |
| } |
| } |
| |
| /* Allow continuing with the same signal that interrupted us. |
| Otherwise complain. */ |
| |
| if (resume_all) |
| win32_continue (continue_status, -1); |
| else |
| win32_continue (continue_status, ptid_get_tid (ptid)); |
| } |
| |
| /* Get the next event from the child. Return 1 if the event requires |
| handling by WFI (or whatever). |
| */ |
| static int |
| get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) |
| { |
| BOOL debug_event; |
| DWORD continue_status, event_code; |
| thread_info *th; |
| static thread_info dummy_thread_info; |
| int retval = 0; |
| |
| last_sig = TARGET_SIGNAL_0; |
| |
| if (!(debug_event = WaitForDebugEvent (¤t_event, 1000))) |
| goto out; |
| |
| event_count++; |
| continue_status = DBG_CONTINUE; |
| |
| event_code = current_event.dwDebugEventCode; |
| ourstatus->kind = TARGET_WAITKIND_SPURIOUS; |
| th = NULL; |
| have_saved_context = 0; |
| |
| switch (event_code) |
| { |
| case CREATE_THREAD_DEBUG_EVENT: |
| DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%x code=%s)\n", |
| (unsigned) current_event.dwProcessId, |
| (unsigned) current_event.dwThreadId, |
| "CREATE_THREAD_DEBUG_EVENT")); |
| if (saw_create != 1) |
| { |
| struct inferior *inf; |
| inf = find_inferior_pid (current_event.dwProcessId); |
| if (!saw_create && inf->attach_flag) |
| { |
| /* Kludge around a Windows bug where first event is a create |
| thread event. Caused when attached process does not have |
| a main thread. */ |
| retval = fake_create_process (); |
| if (retval) |
| saw_create++; |
| } |
| break; |
| } |
| /* Record the existence of this thread */ |
| retval = current_event.dwThreadId; |
| th = win32_add_thread (ptid_build (current_event.dwProcessId, 0, |
| current_event.dwThreadId), |
| current_event.u.CreateThread.hThread); |
| break; |
| |
| case EXIT_THREAD_DEBUG_EVENT: |
| DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", |
| (unsigned) current_event.dwProcessId, |
| (unsigned) current_event.dwThreadId, |
| "EXIT_THREAD_DEBUG_EVENT")); |
| if (current_event.dwThreadId != main_thread_id) |
| { |
| win32_delete_thread (ptid_build (current_event.dwProcessId, 0, |
| current_event.dwThreadId)); |
| th = &dummy_thread_info; |
| } |
| break; |
| |
| case CREATE_PROCESS_DEBUG_EVENT: |
| DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", |
| (unsigned) current_event.dwProcessId, |
| (unsigned) current_event.dwThreadId, |
| "CREATE_PROCESS_DEBUG_EVENT")); |
| CloseHandle (current_event.u.CreateProcessInfo.hFile); |
| if (++saw_create != 1) |
| break; |
| |
| current_process_handle = current_event.u.CreateProcessInfo.hProcess; |
| if (main_thread_id) |
| win32_delete_thread (ptid_build (current_event.dwProcessId, 0, |
| main_thread_id)); |
| main_thread_id = current_event.dwThreadId; |
| /* Add the main thread */ |
| th = win32_add_thread (ptid_build (current_event.dwProcessId, 0, |
| current_event.dwThreadId), |
| current_event.u.CreateProcessInfo.hThread); |
| retval = current_event.dwThreadId; |
| break; |
| |
| case EXIT_PROCESS_DEBUG_EVENT: |
| DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", |
| (unsigned) current_event.dwProcessId, |
| (unsigned) current_event.dwThreadId, |
| "EXIT_PROCESS_DEBUG_EVENT")); |
| if (saw_create != 1) |
| break; |
| ourstatus->kind = TARGET_WAITKIND_EXITED; |
| ourstatus->value.integer = current_event.u.ExitProcess.dwExitCode; |
| retval = main_thread_id; |
| break; |
| |
| case LOAD_DLL_DEBUG_EVENT: |
| DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", |
| (unsigned) current_event.dwProcessId, |
| (unsigned) current_event.dwThreadId, |
| "LOAD_DLL_DEBUG_EVENT")); |
| CloseHandle (current_event.u.LoadDll.hFile); |
| if (saw_create != 1) |
| break; |
| catch_errors (handle_load_dll, NULL, (char *) "", RETURN_MASK_ALL); |
| ourstatus->kind = TARGET_WAITKIND_LOADED; |
| ourstatus->value.integer = 0; |
| retval = main_thread_id; |
| break; |
| |
| case UNLOAD_DLL_DEBUG_EVENT: |
| DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", |
| (unsigned) current_event.dwProcessId, |
| (unsigned) current_event.dwThreadId, |
| "UNLOAD_DLL_DEBUG_EVENT")); |
| if (saw_create != 1) |
| break; |
| catch_errors (handle_unload_dll, NULL, (char *) "", RETURN_MASK_ALL); |
| ourstatus->kind = TARGET_WAITKIND_LOADED; |
| ourstatus->value.integer = 0; |
| retval = main_thread_id; |
| break; |
| |
| case EXCEPTION_DEBUG_EVENT: |
| DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", |
| (unsigned) current_event.dwProcessId, |
| (unsigned) current_event.dwThreadId, |
| "EXCEPTION_DEBUG_EVENT")); |
| if (saw_create != 1) |
| break; |
| switch (handle_exception (ourstatus)) |
| { |
| case 0: |
| continue_status = DBG_EXCEPTION_NOT_HANDLED; |
| break; |
| case 1: |
| retval = current_event.dwThreadId; |
| break; |
| case -1: |
| last_sig = 1; |
| continue_status = -1; |
| break; |
| } |
| break; |
| |
| case OUTPUT_DEBUG_STRING_EVENT: /* message from the kernel */ |
| DEBUG_EVENTS (("gdb: kernel event for pid=%d tid=%d code=%s)\n", |
| (unsigned) current_event.dwProcessId, |
| (unsigned) current_event.dwThreadId, |
| "OUTPUT_DEBUG_STRING_EVENT")); |
| if (saw_create != 1) |
| break; |
| retval = handle_output_debug_string (ourstatus); |
| break; |
| |
| default: |
| if (saw_create != 1) |
| break; |
| printf_unfiltered ("gdb: kernel event for pid=%ld tid=%ld\n", |
| (DWORD) current_event.dwProcessId, |
| (DWORD) current_event.dwThreadId); |
| printf_unfiltered (" unknown event code %ld\n", |
| current_event.dwDebugEventCode); |
| break; |
| } |
| |
| if (!retval || saw_create != 1) |
| { |
| if (continue_status == -1) |
| win32_resume (minus_one_ptid, 0, 1); |
| else |
| CHECK (win32_continue (continue_status, -1)); |
| } |
| else |
| { |
| inferior_ptid = ptid_build (current_event.dwProcessId, 0, |
| retval); |
| current_thread = th ?: thread_rec (current_event.dwThreadId, TRUE); |
| } |
| |
| out: |
| return retval; |
| } |
| |
| /* Wait for interesting events to occur in the target process. */ |
| static ptid_t |
| win32_wait (ptid_t ptid, struct target_waitstatus *ourstatus) |
| { |
| int pid = -1; |
| |
| target_terminal_ours (); |
| |
| /* We loop when we get a non-standard exception rather than return |
| with a SPURIOUS because resume can try and step or modify things, |
| which needs a current_thread->h. But some of these exceptions mark |
| the birth or death of threads, which mean that the current thread |
| isn't necessarily what you think it is. */ |
| |
| while (1) |
| { |
| int retval; |
| |
| /* Ignore CTRL+C signals while waiting for a debug event. |
| FIXME: brobecker/2008-05-20: When the user presses CTRL+C while |
| the inferior is running, both the inferior and GDB receive the |
| associated signal. If the inferior receives the signal first |
| and the delay until GDB receives that signal is sufficiently long, |
| GDB can sometimes receive the SIGINT after we have unblocked |
| the CTRL+C handler. This would lead to the debugger to stop |
| prematurely while handling the new-thread event that comes |
| with the handling of the SIGINT inside the inferior, and then |
| stop again immediately when the user tries to resume the execution |
| in the inferior. This is a classic race, and it would be nice |
| to find a better solution to that problem. But in the meantime, |
| the current approach already greatly mitigate this issue. */ |
| SetConsoleCtrlHandler (NULL, TRUE); |
| retval = get_win32_debug_event (pid, ourstatus); |
| SetConsoleCtrlHandler (NULL, FALSE); |
| |
| if (retval) |
| return ptid_build (current_event.dwProcessId, 0, retval); |
| else |
| { |
| int detach = 0; |
| |
| if (deprecated_ui_loop_hook != NULL) |
| detach = deprecated_ui_loop_hook (0); |
| |
| if (detach) |
| win32_kill_inferior (); |
| } |
| } |
| } |
| |
| static void |
| do_initial_win32_stuff (DWORD pid, int attaching) |
| { |
| extern int stop_after_trap; |
| int i; |
| struct inferior *inf; |
| struct thread_info *tp; |
| |
| last_sig = TARGET_SIGNAL_0; |
| event_count = 0; |
| exception_count = 0; |
| open_process_used = 0; |
| debug_registers_changed = 0; |
| debug_registers_used = 0; |
| for (i = 0; i < sizeof (dr) / sizeof (dr[0]); i++) |
| dr[i] = 0; |
| #ifdef __CYGWIN__ |
| cygwin_load_start = cygwin_load_end = 0; |
| #endif |
| current_event.dwProcessId = pid; |
| memset (¤t_event, 0, sizeof (current_event)); |
| push_target (&win32_ops); |
| disable_breakpoints_in_shlibs (); |
| win32_clear_solib (); |
| clear_proceed_status (); |
| init_wait_for_inferior (); |
| |
| inf = add_inferior (pid); |
| inf->attach_flag = attaching; |
| |
| /* Make the new process the current inferior, so terminal handling |
| can rely on it. When attaching, we don't know about any thread |
| id here, but that's OK --- nothing should be referencing the |
| current thread until we report an event out of win32_wait. */ |
| inferior_ptid = pid_to_ptid (pid); |
| |
| terminal_init_inferior_with_pgrp (pid); |
| target_terminal_inferior (); |
| |
| inf->stop_soon = STOP_QUIETLY; |
| while (1) |
| { |
| stop_after_trap = 1; |
| wait_for_inferior (0); |
| tp = inferior_thread (); |
| if (tp->stop_signal != TARGET_SIGNAL_TRAP) |
| resume (0, tp->stop_signal); |
| else |
| break; |
| } |
| |
| inf->stop_soon = NO_STOP_QUIETLY; |
| stop_after_trap = 0; |
| return; |
| } |
| |
| /* Since Windows XP, detaching from a process is supported by Windows. |
| The following code tries loading the appropriate functions dynamically. |
| If loading these functions succeeds use them to actually detach from |
| the inferior process, otherwise behave as usual, pretending that |
| detach has worked. */ |
| static BOOL WINAPI (*DebugSetProcessKillOnExit)(BOOL); |
| static BOOL WINAPI (*DebugActiveProcessStop)(DWORD); |
| |
| static int |
| has_detach_ability (void) |
| { |
| static HMODULE kernel32 = NULL; |
| |
| if (!kernel32) |
| kernel32 = LoadLibrary ("kernel32.dll"); |
| if (kernel32) |
| { |
| if (!DebugSetProcessKillOnExit) |
| DebugSetProcessKillOnExit = GetProcAddress (kernel32, |
| "DebugSetProcessKillOnExit"); |
| if (!DebugActiveProcessStop) |
| DebugActiveProcessStop = GetProcAddress (kernel32, |
| "DebugActiveProcessStop"); |
| if (DebugSetProcessKillOnExit && DebugActiveProcessStop) |
| return 1; |
| } |
| return 0; |
| } |
| |
| /* Try to set or remove a user privilege to the current process. Return -1 |
| if that fails, the previous setting of that privilege otherwise. |
| |
| This code is copied from the Cygwin source code and rearranged to allow |
| dynamically loading of the needed symbols from advapi32 which is only |
| available on NT/2K/XP. */ |
| static int |
| set_process_privilege (const char *privilege, BOOL enable) |
| { |
| static HMODULE advapi32 = NULL; |
| static BOOL WINAPI (*OpenProcessToken)(HANDLE, DWORD, PHANDLE); |
| static BOOL WINAPI (*LookupPrivilegeValue)(LPCSTR, LPCSTR, PLUID); |
| static BOOL WINAPI (*AdjustTokenPrivileges)(HANDLE, BOOL, PTOKEN_PRIVILEGES, |
| DWORD, PTOKEN_PRIVILEGES, PDWORD); |
| |
| HANDLE token_hdl = NULL; |
| LUID restore_priv; |
| TOKEN_PRIVILEGES new_priv, orig_priv; |
| int ret = -1; |
| DWORD size; |
| |
| if (GetVersion () >= 0x80000000) /* No security availbale on 9x/Me */ |
| return 0; |
| |
| if (!advapi32) |
| { |
| if (!(advapi32 = LoadLibrary ("advapi32.dll"))) |
| goto out; |
| if (!OpenProcessToken) |
| OpenProcessToken = GetProcAddress (advapi32, "OpenProcessToken"); |
| if (!LookupPrivilegeValue) |
| LookupPrivilegeValue = GetProcAddress (advapi32, |
| "LookupPrivilegeValueA"); |
| if (!AdjustTokenPrivileges) |
| AdjustTokenPrivileges = GetProcAddress (advapi32, |
| "AdjustTokenPrivileges"); |
| if (!OpenProcessToken || !LookupPrivilegeValue || !AdjustTokenPrivileges) |
| { |
| advapi32 = NULL; |
| goto out; |
| } |
| } |
| |
| if (!OpenProcessToken (GetCurrentProcess (), |
| TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, |
| &token_hdl)) |
| goto out; |
| |
| if (!LookupPrivilegeValue (NULL, privilege, &restore_priv)) |
| goto out; |
| |
| new_priv.PrivilegeCount = 1; |
| new_priv.Privileges[0].Luid = restore_priv; |
| new_priv.Privileges[0].Attributes = enable ? SE_PRIVILEGE_ENABLED : 0; |
| |
| if (!AdjustTokenPrivileges (token_hdl, FALSE, &new_priv, |
| sizeof orig_priv, &orig_priv, &size)) |
| goto out; |
| #if 0 |
| /* Disabled, otherwise every `attach' in an unprivileged user session |
| would raise the "Failed to get SE_DEBUG_NAME privilege" warning in |
| win32_attach(). */ |
| /* AdjustTokenPrivileges returns TRUE even if the privilege could not |
| be enabled. GetLastError () returns an correct error code, though. */ |
| if (enable && GetLastError () == ERROR_NOT_ALL_ASSIGNED) |
| goto out; |
| #endif |
| |
| ret = orig_priv.Privileges[0].Attributes == SE_PRIVILEGE_ENABLED ? 1 : 0; |
| |
| out: |
| if (token_hdl) |
| CloseHandle (token_hdl); |
| |
| return ret; |
| } |
| |
| /* Attach to process PID, then initialize for debugging it. */ |
| static void |
| win32_attach (struct target_ops *ops, char *args, int from_tty) |
| { |
| BOOL ok; |
| DWORD pid; |
| |
| if (!args) |
| error_no_arg (_("process-id to attach")); |
| |
| if (set_process_privilege (SE_DEBUG_NAME, TRUE) < 0) |
| { |
| printf_unfiltered ("Warning: Failed to get SE_DEBUG_NAME privilege\n"); |
| printf_unfiltered ("This can cause attach to fail on Windows NT/2K/XP\n"); |
| } |
| |
| pid = strtoul (args, 0, 0); /* Windows pid */ |
| |
| win32_init_thread_list (); |
| ok = DebugActiveProcess (pid); |
| saw_create = 0; |
| |
| #ifdef __CYGWIN__ |
| if (!ok) |
| { |
| /* Try fall back to Cygwin pid */ |
| pid = cygwin_internal (CW_CYGWIN_PID_TO_WINPID, pid); |
| |
| if (pid > 0) |
| ok = DebugActiveProcess (pid); |
| } |
| #endif |
| |
| if (!ok) |
| error (_("Can't attach to process.")); |
| |
| if (has_detach_ability ()) |
| DebugSetProcessKillOnExit (FALSE); |
| |
| if (from_tty) |
| { |
| char *exec_file = (char *) get_exec_file (0); |
| |
| if (exec_file) |
| printf_unfiltered ("Attaching to program `%s', %s\n", exec_file, |
| target_pid_to_str (pid_to_ptid (pid))); |
| else |
| printf_unfiltered ("Attaching to %s\n", |
| target_pid_to_str (pid_to_ptid (pid))); |
| |
| gdb_flush (gdb_stdout); |
| } |
| |
| do_initial_win32_stuff (pid, 1); |
| target_terminal_ours (); |
| } |
| |
| static void |
| win32_detach (struct target_ops *ops, char *args, int from_tty) |
| { |
| int detached = 1; |
| |
| if (has_detach_ability ()) |
| { |
| ptid_t ptid = {-1}; |
| win32_resume (ptid, 0, TARGET_SIGNAL_0); |
| |
| if (!DebugActiveProcessStop (current_event.dwProcessId)) |
| { |
| error (_("Can't detach process %lu (error %lu)"), |
| current_event.dwProcessId, GetLastError ()); |
| detached = 0; |
| } |
| DebugSetProcessKillOnExit (FALSE); |
| } |
| if (detached && from_tty) |
| { |
| char *exec_file = get_exec_file (0); |
| if (exec_file == 0) |
| exec_file = ""; |
| printf_unfiltered ("Detaching from program: %s, Pid %lu\n", exec_file, |
| current_event.dwProcessId); |
| gdb_flush (gdb_stdout); |
| } |
| |
| inferior_ptid = null_ptid; |
| detach_inferior (current_event.dwProcessId); |
| |
| unpush_target (&win32_ops); |
| } |
| |
| static char * |
| win32_pid_to_exec_file (int pid) |
| { |
| static char path[MAX_PATH + 1]; |
| |
| #ifdef __CYGWIN__ |
| /* Try to find exe name as symlink target of /proc/<pid>/exe */ |
| int nchars; |
| char procexe[sizeof ("/proc/4294967295/exe")]; |
| sprintf (procexe, "/proc/%u/exe", pid); |
| nchars = readlink (procexe, path, sizeof(path)); |
| if (nchars > 0 && nchars < sizeof (path)) |
| { |
| path[nchars] = '\0'; /* Got it */ |
| return path; |
| } |
| #endif |
| |
| /* If we get here then either Cygwin is hosed, this isn't a Cygwin version |
| of gdb, or we're trying to debug a non-Cygwin windows executable. */ |
| if (!get_module_name (0, path)) |
| path[0] = '\0'; |
| |
| return path; |
| } |
| |
| /* Print status information about what we're accessing. */ |
| |
| static void |
| win32_files_info (struct target_ops *ignore) |
| { |
| struct inferior *inf = current_inferior (); |
| |
| printf_unfiltered ("\tUsing the running image of %s %s.\n", |
| inf->attach_flag ? "attached" : "child", |
| target_pid_to_str (inferior_ptid)); |
| } |
| |
| static void |
| win32_open (char *arg, int from_tty) |
| { |
| error (_("Use the \"run\" command to start a Unix child process.")); |
| } |
| |
| /* Start an inferior win32 child process and sets inferior_ptid to its pid. |
| EXEC_FILE is the file to run. |
| ALLARGS is a string containing the arguments to the program. |
| ENV is the environment vector to pass. Errors reported with error(). */ |
| |
| static void |
| win32_create_inferior (struct target_ops *ops, char *exec_file, |
| char *allargs, char **in_env, int from_tty) |
| { |
| STARTUPINFO si; |
| PROCESS_INFORMATION pi; |
| BOOL ret; |
| DWORD flags; |
| char *args; |
| char real_path[MAXPATHLEN]; |
| char *toexec; |
| char shell[MAX_PATH + 1]; /* Path to shell */ |
| const char *sh; |
| int tty; |
| int ostdin, ostdout, ostderr; |
| const char *inferior_io_terminal = get_inferior_io_terminal (); |
| |
| if (!exec_file) |
| error (_("No executable specified, use `target exec'.")); |
| |
| memset (&si, 0, sizeof (si)); |
| si.cb = sizeof (si); |
| |
| #ifdef __CYGWIN__ |
| if (!useshell) |
| { |
| flags = DEBUG_ONLY_THIS_PROCESS; |
| cygwin_conv_to_win32_path (exec_file, real_path); |
| toexec = real_path; |
| } |
| else |
| { |
| char *newallargs; |
| sh = getenv ("SHELL"); |
| if (!sh) |
| sh = "/bin/sh"; |
| cygwin_conv_to_win32_path (sh, shell); |
| newallargs = alloca (sizeof (" -c 'exec '") + strlen (exec_file) |
| + strlen (allargs) + 2); |
| sprintf (newallargs, " -c 'exec %s %s'", exec_file, allargs); |
| allargs = newallargs; |
| toexec = shell; |
| flags = DEBUG_PROCESS; |
| } |
| #else |
| toexec = exec_file; |
| flags = DEBUG_ONLY_THIS_PROCESS; |
| #endif |
| |
| if (new_group) |
| flags |= CREATE_NEW_PROCESS_GROUP; |
| |
| if (new_console) |
| flags |= CREATE_NEW_CONSOLE; |
| |
| args = alloca (strlen (toexec) + strlen (allargs) + 2); |
| strcpy (args, toexec); |
| strcat (args, " "); |
| strcat (args, allargs); |
| |
| #ifdef __CYGWIN__ |
| /* Prepare the environment vars for CreateProcess. */ |
| cygwin_internal (CW_SYNC_WINENV); |
| |
| if (!inferior_io_terminal) |
| tty = ostdin = ostdout = ostderr = -1; |
| else |
| { |
| tty = open (inferior_io_terminal, O_RDWR | O_NOCTTY); |
| if (tty < 0) |
| { |
| print_sys_errmsg (inferior_io_terminal, errno); |
| ostdin = ostdout = ostderr = -1; |
| } |
| else |
| { |
| ostdin = dup (0); |
| ostdout = dup (1); |
| ostderr = dup (2); |
| dup2 (tty, 0); |
| dup2 (tty, 1); |
| dup2 (tty, 2); |
| } |
| } |
| #endif |
| |
| win32_init_thread_list (); |
| ret = CreateProcess (0, |
| args, /* command line */ |
| NULL, /* Security */ |
| NULL, /* thread */ |
| TRUE, /* inherit handles */ |
| flags, /* start flags */ |
| NULL, /* environment */ |
| NULL, /* current directory */ |
| &si, |
| &pi); |
| |
| #ifdef __CYGWIN__ |
| if (tty >= 0) |
| { |
| close (tty); |
| dup2 (ostdin, 0); |
| dup2 (ostdout, 1); |
| dup2 (ostderr, 2); |
| close (ostdin); |
| close (ostdout); |
| close (ostderr); |
| } |
| #endif |
| |
| if (!ret) |
| error (_("Error creating process %s, (error %d)."), |
| exec_file, (unsigned) GetLastError ()); |
| |
| CloseHandle (pi.hThread); |
| CloseHandle (pi.hProcess); |
| |
| if (useshell && shell[0] != '\0') |
| saw_create = -1; |
| else |
| saw_create = 0; |
| |
| do_initial_win32_stuff (pi.dwProcessId, 0); |
| |
| /* win32_continue (DBG_CONTINUE, -1); */ |
| } |
| |
| static void |
| win32_mourn_inferior (struct target_ops *ops) |
| { |
| (void) win32_continue (DBG_CONTINUE, -1); |
| i386_cleanup_dregs(); |
| if (open_process_used) |
| { |
| CHECK (CloseHandle (current_process_handle)); |
| open_process_used = 0; |
| } |
| unpush_target (&win32_ops); |
| generic_mourn_inferior (); |
| } |
| |
| /* Send a SIGINT to the process group. This acts just like the user typed a |
| ^C on the controlling terminal. */ |
| |
| static void |
| win32_stop (ptid_t ptid) |
| { |
| DEBUG_EVENTS (("gdb: GenerateConsoleCtrlEvent (CTRLC_EVENT, 0)\n")); |
| CHECK (GenerateConsoleCtrlEvent (CTRL_C_EVENT, current_event.dwProcessId)); |
| registers_changed (); /* refresh register state */ |
| } |
| |
| static int |
| win32_xfer_memory (CORE_ADDR memaddr, gdb_byte *our, int len, |
| int write, struct mem_attrib *mem, |
| struct target_ops *target) |
| { |
| DWORD done = 0; |
| if (write) |
| { |
| DEBUG_MEM (("gdb: write target memory, %d bytes at 0x%08lx\n", |
| len, (DWORD) (uintptr_t) memaddr)); |
| if (!WriteProcessMemory (current_process_handle, |
| (LPVOID) (uintptr_t) memaddr, our, |
| len, &done)) |
| done = 0; |
| FlushInstructionCache (current_process_handle, |
| (LPCVOID) (uintptr_t) memaddr, len); |
| } |
| else |
| { |
| DEBUG_MEM (("gdb: read target memory, %d bytes at 0x%08lx\n", |
| len, (DWORD) (uintptr_t) memaddr)); |
| if (!ReadProcessMemory (current_process_handle, |
| (LPCVOID) (uintptr_t) memaddr, our, |
| len, &done)) |
| done = 0; |
| } |
| return done; |
| } |
| |
| static void |
| win32_kill_inferior (void) |
| { |
| CHECK (TerminateProcess (current_process_handle, 0)); |
| |
| for (;;) |
| { |
| if (!win32_continue (DBG_CONTINUE, -1)) |
| break; |
| if (!WaitForDebugEvent (¤t_event, INFINITE)) |
| break; |
| if (current_event.dwDebugEventCode == EXIT_PROCESS_DEBUG_EVENT) |
| break; |
| } |
| |
| target_mourn_inferior (); /* or just win32_mourn_inferior? */ |
| } |
| |
| static void |
| win32_prepare_to_store (struct regcache *regcache) |
| { |
| /* Do nothing, since we can store individual regs */ |
| } |
| |
| static int |
| win32_can_run (void) |
| { |
| return 1; |
| } |
| |
| static void |
| win32_close (int x) |
| { |
| DEBUG_EVENTS (("gdb: win32_close, inferior_ptid=%d\n", |
| PIDGET (inferior_ptid))); |
| } |
| |
| /* Convert pid to printable format. */ |
| static char * |
| win32_pid_to_str (ptid_t ptid) |
| { |
| static char buf[80]; |
| |
| if (ptid_get_tid (ptid) != 0) |
| { |
| snprintf (buf, sizeof (buf), "Thread %d.0x%lx", |
| ptid_get_pid (ptid), ptid_get_tid (ptid)); |
| return buf; |
| } |
| |
| return normal_pid_to_str (ptid); |
| } |
| |
| static LONGEST |
| win32_xfer_shared_libraries (struct target_ops *ops, |
| enum target_object object, const char *annex, |
| gdb_byte *readbuf, const gdb_byte *writebuf, |
| ULONGEST offset, LONGEST len) |
| { |
| struct obstack obstack; |
| const char *buf; |
| LONGEST len_avail; |
| struct so_list *so; |
| |
| if (writebuf) |
| return -1; |
| |
| obstack_init (&obstack); |
| obstack_grow_str (&obstack, "<library-list>\n"); |
| for (so = solib_start.next; so; so = so->next) |
| win32_xfer_shared_library (so->so_name, so->lm_info->load_addr, &obstack); |
| obstack_grow_str0 (&obstack, "</library-list>\n"); |
| |
| buf = obstack_finish (&obstack); |
| len_avail = strlen (buf); |
| if (offset >= len_avail) |
| return 0; |
| |
| if (len > len_avail - offset) |
| len = len_avail - offset; |
| memcpy (readbuf, buf + offset, len); |
| |
| obstack_free (&obstack, NULL); |
| return len; |
| } |
| |
| static LONGEST |
| win32_xfer_partial (struct target_ops *ops, enum target_object object, |
| const char *annex, gdb_byte *readbuf, |
| const gdb_byte *writebuf, ULONGEST offset, LONGEST len) |
| { |
| switch (object) |
| { |
| case TARGET_OBJECT_MEMORY: |
| if (readbuf) |
| return (*ops->deprecated_xfer_memory) (offset, readbuf, |
| len, 0/*read*/, NULL, ops); |
| if (writebuf) |
| return (*ops->deprecated_xfer_memory) (offset, (gdb_byte *) writebuf, |
| len, 1/*write*/, NULL, ops); |
| return -1; |
| |
| case TARGET_OBJECT_LIBRARIES: |
| return win32_xfer_shared_libraries (ops, object, annex, readbuf, |
| writebuf, offset, len); |
| |
| default: |
| if (ops->beneath != NULL) |
| return ops->beneath->to_xfer_partial (ops->beneath, object, annex, |
| readbuf, writebuf, offset, len); |
| return -1; |
| } |
| } |
| |
| static void |
| init_win32_ops (void) |
| { |
| win32_ops.to_shortname = "child"; |
| win32_ops.to_longname = "Win32 child process"; |
| win32_ops.to_doc = "Win32 child process (started by the \"run\" command)."; |
| win32_ops.to_open = win32_open; |
| win32_ops.to_close = win32_close; |
| win32_ops.to_attach = win32_attach; |
| win32_ops.to_attach_no_wait = 1; |
| win32_ops.to_detach = win32_detach; |
| win32_ops.to_resume = win32_resume; |
| win32_ops.to_wait = win32_wait; |
| win32_ops.to_fetch_registers = win32_fetch_inferior_registers; |
| win32_ops.to_store_registers = win32_store_inferior_registers; |
| win32_ops.to_prepare_to_store = win32_prepare_to_store; |
| win32_ops.deprecated_xfer_memory = win32_xfer_memory; |
| win32_ops.to_xfer_partial = win32_xfer_partial; |
| win32_ops.to_files_info = win32_files_info; |
| win32_ops.to_insert_breakpoint = memory_insert_breakpoint; |
| win32_ops.to_remove_breakpoint = memory_remove_breakpoint; |
| win32_ops.to_terminal_init = terminal_init_inferior; |
| win32_ops.to_terminal_inferior = terminal_inferior; |
| win32_ops.to_terminal_ours_for_output = terminal_ours_for_output; |
| win32_ops.to_terminal_ours = terminal_ours; |
| win32_ops.to_terminal_save_ours = terminal_save_ours; |
| win32_ops.to_terminal_info = child_terminal_info; |
| win32_ops.to_kill = win32_kill_inferior; |
| win32_ops.to_create_inferior = win32_create_inferior; |
| win32_ops.to_mourn_inferior = win32_mourn_inferior; |
| win32_ops.to_can_run = win32_can_run; |
| win32_ops.to_thread_alive = win32_win32_thread_alive; |
| win32_ops.to_pid_to_str = win32_pid_to_str; |
| win32_ops.to_stop = win32_stop; |
| win32_ops.to_stratum = process_stratum; |
| win32_ops.to_has_all_memory = 1; |
| win32_ops.to_has_memory = 1; |
| win32_ops.to_has_stack = 1; |
| win32_ops.to_has_registers = 1; |
| win32_ops.to_has_execution = 1; |
| win32_ops.to_pid_to_exec_file = win32_pid_to_exec_file; |
| win32_ops.to_magic = OPS_MAGIC; |
| } |
| |
| static void |
| set_win32_aliases (char *argv0) |
| { |
| add_info_alias ("dll", "sharedlibrary", 1); |
| } |
| |
| void |
| _initialize_win32_nat (void) |
| { |
| struct cmd_list_element *c; |
| |
| init_win32_ops (); |
| |
| c = add_com ("dll-symbols", class_files, dll_symbol_command, |
| _("Load dll library symbols from FILE.")); |
| set_cmd_completer (c, filename_completer); |
| |
| add_com_alias ("sharedlibrary", "dll-symbols", class_alias, 1); |
| |
| #ifdef __CYGWIN__ |
| add_setshow_boolean_cmd ("shell", class_support, &useshell, _("\ |
| Set use of shell to start subprocess."), _("\ |
| Show use of shell to start subprocess."), NULL, |
| NULL, |
| NULL, /* FIXME: i18n: */ |
| &setlist, &showlist); |
| |
| add_setshow_boolean_cmd ("cygwin-exceptions", class_support, &cygwin_exceptions, _("\ |
| Break when an exception is detected in the Cygwin DLL itself."), _("\ |
| Show whether gdb breaks on exceptions in the Cygwin DLL itself."), NULL, |
| NULL, |
| NULL, /* FIXME: i18n: */ |
| &setlist, &showlist); |
| #endif |
| |
| add_setshow_boolean_cmd ("new-console", class_support, &new_console, _("\ |
| Set creation of new console when creating child process."), _("\ |
| Show creation of new console when creating child process."), NULL, |
| NULL, |
| NULL, /* FIXME: i18n: */ |
| &setlist, &showlist); |
| |
| add_setshow_boolean_cmd ("new-group", class_support, &new_group, _("\ |
| Set creation of new group when creating child process."), _("\ |
| Show creation of new group when creating child process."), NULL, |
| NULL, |
| NULL, /* FIXME: i18n: */ |
| &setlist, &showlist); |
| |
| add_setshow_boolean_cmd ("debugexec", class_support, &debug_exec, _("\ |
| Set whether to display execution in child process."), _("\ |
| Show whether to display execution in child process."), NULL, |
| NULL, |
| NULL, /* FIXME: i18n: */ |
| &setlist, &showlist); |
| |
| add_setshow_boolean_cmd ("debugevents", class_support, &debug_events, _("\ |
| Set whether to display kernel events in child process."), _("\ |
| Show whether to display kernel events in child process."), NULL, |
| NULL, |
| NULL, /* FIXME: i18n: */ |
| &setlist, &showlist); |
| |
| add_setshow_boolean_cmd ("debugmemory", class_support, &debug_memory, _("\ |
| Set whether to display memory accesses in child process."), _("\ |
| Show whether to display memory accesses in child process."), NULL, |
| NULL, |
| NULL, /* FIXME: i18n: */ |
| &setlist, &showlist); |
| |
| add_setshow_boolean_cmd ("debugexceptions", class_support, |
| &debug_exceptions, _("\ |
| Set whether to display kernel exceptions in child process."), _("\ |
| Show whether to display kernel exceptions in child process."), NULL, |
| NULL, |
| NULL, /* FIXME: i18n: */ |
| &setlist, &showlist); |
| |
| add_prefix_cmd ("w32", class_info, info_w32_command, |
| _("Print information specific to Win32 debugging."), |
| &info_w32_cmdlist, "info w32 ", 0, &infolist); |
| |
| add_cmd ("selector", class_info, display_selectors, |
| _("Display selectors infos."), |
| &info_w32_cmdlist); |
| add_target (&win32_ops); |
| deprecated_init_ui_hook = set_win32_aliases; |
| } |
| |
| /* Hardware watchpoint support, adapted from go32-nat.c code. */ |
| |
| /* Pass the address ADDR to the inferior in the I'th debug register. |
| Here we just store the address in dr array, the registers will be |
| actually set up when win32_continue is called. */ |
| void |
| cygwin_set_dr (int i, CORE_ADDR addr) |
| { |
| if (i < 0 || i > 3) |
| internal_error (__FILE__, __LINE__, |
| _("Invalid register %d in cygwin_set_dr.\n"), i); |
| dr[i] = (unsigned) addr; |
| debug_registers_changed = 1; |
| debug_registers_used = 1; |
| } |
| |
| /* Pass the value VAL to the inferior in the DR7 debug control |
| register. Here we just store the address in D_REGS, the watchpoint |
| will be actually set up in win32_wait. */ |
| void |
| cygwin_set_dr7 (unsigned val) |
| { |
| dr[7] = val; |
| debug_registers_changed = 1; |
| debug_registers_used = 1; |
| } |
| |
| /* Get the value of the DR6 debug status register from the inferior. |
| Here we just return the value stored in dr[6] |
| by the last call to thread_rec for current_event.dwThreadId id. */ |
| unsigned |
| cygwin_get_dr6 (void) |
| { |
| return dr[6]; |
| } |
| |
| /* Determine if the thread referenced by "ptid" is alive |
| by "polling" it. If WaitForSingleObject returns WAIT_OBJECT_0 |
| it means that the thread has died. Otherwise it is assumed to be alive. */ |
| static int |
| win32_win32_thread_alive (ptid_t ptid) |
| { |
| int tid; |
| |
| gdb_assert (ptid_get_tid (ptid) != 0); |
| tid = ptid_get_tid (ptid); |
| |
| return WaitForSingleObject (thread_rec (tid, FALSE)->h, 0) == WAIT_OBJECT_0 ? |
| FALSE : TRUE; |
| } |
| |
| void |
| _initialize_check_for_gdb_ini (void) |
| { |
| char *homedir; |
| if (inhibit_gdbinit) |
| return; |
| |
| homedir = getenv ("HOME"); |
| if (homedir) |
| { |
| char *p; |
| char *oldini = (char *) alloca (strlen (homedir) + |
| sizeof ("/gdb.ini")); |
| strcpy (oldini, homedir); |
| p = strchr (oldini, '\0'); |
| if (p > oldini && p[-1] != '/') |
| *p++ = '/'; |
| strcpy (p, "gdb.ini"); |
| if (access (oldini, 0) == 0) |
| { |
| int len = strlen (oldini); |
| char *newini = alloca (len + 1); |
| sprintf (newini, "%.*s.gdbinit", |
| (int) (len - (sizeof ("gdb.ini") - 1)), oldini); |
| warning (_("obsolete '%s' found. Rename to '%s'."), oldini, newini); |
| } |
| } |
| } |
| |
| void |
| _initialize_psapi (void) |
| { |
| /* Load optional functions used for retrieving filename information |
| associated with the currently debugged process or its dlls. */ |
| if (!psapi_loaded) |
| { |
| HMODULE psapi_module_handle; |
| |
| psapi_loaded = -1; |
| |
| psapi_module_handle = LoadLibrary ("psapi.dll"); |
| if (psapi_module_handle) |
| { |
| psapi_EnumProcessModules = (void *) GetProcAddress (psapi_module_handle, "EnumProcessModules"); |
| psapi_GetModuleInformation = (void *) GetProcAddress (psapi_module_handle, "GetModuleInformation"); |
| psapi_GetModuleFileNameExA = (void *) GetProcAddress (psapi_module_handle, "GetModuleFileNameExA"); |
| |
| if (psapi_EnumProcessModules != NULL |
| && psapi_GetModuleInformation != NULL |
| && psapi_GetModuleFileNameExA != NULL) |
| psapi_loaded = 1; |
| } |
| } |
| |
| /* This will probably fail on Windows 9x/Me. Let the user know that we're |
| missing some functionality. */ |
| if (psapi_loaded < 0) |
| warning(_("cannot automatically find executable file or library to read symbols. Use \"file\" or \"dll\" command to load executable/libraries directly.")); |
| } |