diff options
author | Christopher Faylor <me+cygwin@cgf.cx> | 2006-02-20 05:10:51 +0000 |
---|---|---|
committer | Christopher Faylor <me+cygwin@cgf.cx> | 2006-02-20 05:10:51 +0000 |
commit | a244bdca2582450e5113ffeedbcd2d603e09275e (patch) | |
tree | 686f59957eae2dc877be251804a40b39e8c99255 /gdb | |
parent | e78254f677bdddd394242b8592849ddc0471fd97 (diff) | |
download | binutils-gdb-a244bdca2582450e5113ffeedbcd2d603e09275e.tar.gz |
* win32_nat.c (cygwin_load_start): New variable.
(cygwin_load_end): Ditto.
(have_saved_context): Ditto.
(saved_context): Ditto.
(max_dll_name_len): Delete obsolete variable.
(do_win32_fetch_inferior_registers): Use context saved from cygwin1.dll if we
are in a cygwin signal rather than a windows signal.
(solib_symbols_add): Detect and store beginning and end of cygwin DLL if dll
being loaded is the cygwin DLL.
(register_loaded_dll): Remove calculation of max_dll_name_len.
(win32_clear_solib): Ditto.
(handle_load_dll): Delete obsolete variable. Remove unneeded call to
solib_add.
(handle_output_debug_string): Detect and store signal information sent by
Cygwin here.
(handle_exception): Silently pass on errors in the cygwin DLL. Return -1 on
first pass exception.
(win32_continue): Remove spurious clearing of continue_status.
(get_win32_debug_event): Deal differently first chance exception.
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 22 | ||||
-rw-r--r-- | gdb/win32-nat.c | 308 | ||||
-rw-r--r-- | gdb/windows-nat.c | 308 |
3 files changed, 388 insertions, 250 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 52f2d500807..7040b481614 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,25 @@ +2006-02-19 Christopher Faylor <cgf@timesys.com> + + * win32_nat.c (cygwin_load_start): New variable. + (cygwin_load_end): Ditto. + (have_saved_context): Ditto. + (saved_context): Ditto. + (max_dll_name_len): Delete obsolete variable. + (do_win32_fetch_inferior_registers): Use context saved from cygwin1.dll + if we are in a cygwin signal rather than a windows signal. + (solib_symbols_add): Detect and store beginning and end of cygwin DLL + if dll being loaded is the cygwin DLL. + (register_loaded_dll): Remove calculation of max_dll_name_len. + (win32_clear_solib): Ditto. + (handle_load_dll): Delete obsolete variable. Remove unneeded call to + solib_add. + (handle_output_debug_string): Detect and store signal information sent + by Cygwin here. + (handle_exception): Silently pass on errors in the cygwin DLL. Return + -1 on first pass exception. + (win32_continue): Remove spurious clearing of continue_status. + (get_win32_debug_event): Deal differently first chance exception. + 2006-02-19 Randolph Chung <tausq@debian.org> * hppa-tdep.c (hppa_in_solib_call_trampoline): Only use if no diff --git a/gdb/win32-nat.c b/gdb/win32-nat.c index f20ccd3c173..42488580a4e 100644 --- a/gdb/win32-nat.c +++ b/gdb/win32-nat.c @@ -44,6 +44,7 @@ #include <windows.h> #include <imagehlp.h> #include <sys/cygwin.h> +#include <signal.h> #include "buildsym.h" #include "symfile.h" @@ -63,6 +64,13 @@ static struct target_ops win32_ops; static struct target_so_ops win32_so_ops; +/* The starting and ending address of the cygwin1.dll text segment. */ +static bfd_vma cygwin_load_start; +static bfd_vma cygwin_load_end; + +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... */ @@ -346,16 +354,27 @@ do_win32_fetch_inferior_registers (int r) if (current_thread->reload_context) { - thread_info *th = current_thread; - th->context.ContextFlags = CONTEXT_DEBUGGER_DR; - GetThreadContext (th->h, &th->context); - /* Copy dr values from that thread. */ - dr[0] = th->context.Dr0; - dr[1] = th->context.Dr1; - dr[2] = th->context.Dr2; - dr[3] = th->context.Dr3; - dr[6] = th->context.Dr6; - dr[7] = th->context.Dr7; + 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 + { + thread_info *th = current_thread; + th->context.ContextFlags = CONTEXT_DEBUGGER_DR; + GetThreadContext (th->h, &th->context); + /* Copy dr values from that thread. */ + dr[0] = th->context.Dr0; + dr[1] = th->context.Dr1; + dr[2] = th->context.Dr2; + dr[3] = th->context.Dr3; + dr[6] = th->context.Dr6; + dr[7] = th->context.Dr7; + } current_thread->reload_context = 0; } @@ -596,12 +615,10 @@ get_relocated_section_addrs (bfd *abfd, CORE_ADDR text_load) { /* Couldn't get the .text section. Weird. */ } - else if (text_load == (text_vma = bfd_get_section_vma (abfd, text_section))) { /* DLL wasn't relocated. */ } - else { /* Figure out all sections' loaded addresses. The offset here is @@ -640,6 +657,7 @@ solib_symbols_add (struct so_list *so, CORE_ADDR load_addr) static struct objfile *result = NULL; char *name = so->so_name; bfd *abfd = NULL; + char *p; /* The symbols in a dll are offset by 0x1000, which is the the offset from 0 of the first byte in an image - because @@ -660,8 +678,6 @@ solib_symbols_add (struct so_list *so, CORE_ADDR load_addr) { if (bfd_check_format (abfd, bfd_object)) addrs = get_relocated_section_addrs (abfd, load_addr); - - bfd_close (abfd); } if (addrs) @@ -683,13 +699,20 @@ solib_symbols_add (struct so_list *so, CORE_ADDR load_addr) do_cleanups (my_cleanups); } + p = strchr (so->so_name, '\0') - (sizeof ("/cygwin1.dll") - 1); + if (p >= so->so_name && strcasecmp (p, "/cygwin1.dll") == 0) + { + asection *text = bfd_get_section_by_name (abfd, ".text"); + cygwin_load_start = bfd_section_vma (abfd, text); + cygwin_load_end = cygwin_load_start + bfd_section_size (abfd, text); + } + + bfd_close (abfd); + so->symbols_loaded = !!result; return; } -/* Remember the maximum DLL length for printing in info dll command. */ -static int max_dll_name_len; - static char * register_loaded_dll (const char *name, DWORD load_addr, int readsyms) { @@ -733,10 +756,9 @@ register_loaded_dll (const char *name, DWORD load_addr, int readsyms) solib_end->next = so; solib_end = so; len = strlen (so->so_name); - if (len > max_dll_name_len) - max_dll_name_len = len; if (readsyms) solib_symbols_add (so, (CORE_ADDR) load_addr); + return so->so_name; } @@ -789,7 +811,6 @@ handle_load_dll (void *dummy) LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll; char dll_buf[MAX_PATH + 1]; char *dll_name = NULL; - char *p; dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0'; @@ -804,7 +825,6 @@ handle_load_dll (void *dummy) return 1; register_loaded_dll (dll_name, (DWORD) event->lpBaseOfDll + 0x1000, auto_solib_add); - solib_add (NULL, 0, NULL, auto_solib_add); return 1; } @@ -860,7 +880,6 @@ win32_clear_solib (void) { solib_start.next = NULL; solib_end = &solib_start; - max_dll_name_len = sizeof ("DLL Name") - 1; } static void @@ -897,31 +916,49 @@ dll_symbol_command (char *args, int from_tty) static int handle_output_debug_string (struct target_waitstatus *ourstatus) { - char *s; - int gotasig = FALSE; + char *s = NULL; + int retval = 0; if (!target_read_string ((CORE_ADDR) current_event.u.DebugString.lpDebugStringData, &s, 1024, 0) || !s || !*s) - return gotasig; - - if (strncmp (s, _CYGWIN_SIGNAL_STRING, sizeof (_CYGWIN_SIGNAL_STRING) - 1) != 0) + /* nothing to do */; + else if (strncmp (s, _CYGWIN_SIGNAL_STRING, sizeof (_CYGWIN_SIGNAL_STRING) - 1) != 0) { if (strncmp (s, "cYg", 3) != 0) warning (("%s"), s); } 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); - gotasig = target_signal_from_host (sig); + int gotasig = target_signal_from_host (sig); ourstatus->value.sig = gotasig; if (gotasig) - ourstatus->kind = TARGET_WAITKIND_STOPPED; + { + 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; + } } - xfree (s); - return gotasig; + if (s) + xfree (s); + return retval; } static int @@ -1065,11 +1102,17 @@ handle_exception (struct target_waitstatus *ourstatus) DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ACCESS_VIOLATION"); ourstatus->value.sig = TARGET_SIGNAL_SEGV; { + /* 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; - if (find_pc_partial_function ((CORE_ADDR) current_event.u.Exception - .ExceptionRecord.ExceptionAddress, - &fn, NULL, NULL) - && strncmp (fn, "KERNEL32!IsBad", strlen ("KERNEL32!IsBad")) == 0) + bfd_vma addr = (bfd_vma) current_event.u.Exception.ExceptionRecord.ExceptionAddress; + if ((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; } break; @@ -1146,8 +1189,9 @@ handle_exception (struct target_waitstatus *ourstatus) ourstatus->value.sig = TARGET_SIGNAL_ILL; break; default: + /* Treat unhandled first chance exceptions specially. */ if (current_event.u.Exception.dwFirstChance) - return 0; + 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); @@ -1175,7 +1219,6 @@ win32_continue (DWORD continue_status, int id) res = ContinueDebugEvent (current_event.dwProcessId, current_event.dwThreadId, continue_status); - continue_status = 0; if (res) for (th = &thread_head; (th = th->next) != NULL;) if (((id == -1) || (id == (int) th->id)) && th->suspend_count) @@ -1217,6 +1260,87 @@ fake_create_process (void) 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; + + int pid = PIDGET (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, step=%d, sig=%d);\n", + pid, step, sig)); + + /* Get context for currently selected thread */ + th = thread_rec (current_event.dwThreadId, FALSE); + if (th) + { + if (step) + { + /* Single step by setting t bit */ + win32_fetch_inferior_registers (PS_REGNUM); + 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 = dr[6]; + FIXME: should we set dr6 also ?? */ + 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. */ + + win32_continue (continue_status, pid); +} + /* Get the next event from the child. Return 1 if the event requires handling by WFI (or whatever). */ @@ -1228,6 +1352,7 @@ get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) thread_info *th; static thread_info dummy_thread_info; int retval = 0; + ptid_t ptid = {-1}; last_sig = TARGET_SIGNAL_0; @@ -1240,6 +1365,7 @@ get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) event_code = current_event.dwDebugEventCode; ourstatus->kind = TARGET_WAITKIND_SPURIOUS; th = NULL; + have_saved_context = 0; switch (event_code) { @@ -1353,10 +1479,19 @@ get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) "EXCEPTION_DEBUG_EVENT")); if (saw_create != 1) break; - if (handle_exception (ourstatus)) - retval = current_event.dwThreadId; - else - continue_status = DBG_EXCEPTION_NOT_HANDLED; + 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 */ @@ -1366,8 +1501,7 @@ get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) "OUTPUT_DEBUG_STRING_EVENT")); if (saw_create != 1) break; - if (handle_output_debug_string (ourstatus)) - retval = main_thread_id; + retval = handle_output_debug_string (ourstatus); break; default: @@ -1382,7 +1516,12 @@ get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) } if (!retval || saw_create != 1) - CHECK (win32_continue (continue_status, -1)); + { + if (continue_status == -1) + win32_resume (ptid, 0, 1); + else + CHECK (win32_continue (continue_status, -1)); + } else { inferior_ptid = pid_to_ptid (retval); @@ -1982,87 +2121,6 @@ win32_kill_inferior (void) } static void -win32_resume (ptid_t ptid, int step, enum target_signal sig) -{ - thread_info *th; - DWORD continue_status = DBG_CONTINUE; - - int pid = PIDGET (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, step=%d, sig=%d);\n", - pid, step, sig)); - - /* Get context for currently selected thread */ - th = thread_rec (current_event.dwThreadId, FALSE); - if (th) - { - if (step) - { - /* Single step by setting t bit */ - win32_fetch_inferior_registers (PS_REGNUM); - 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 = dr[6]; - FIXME: should we set dr6 also ?? */ - 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. */ - - win32_continue (continue_status, pid); -} - -static void win32_prepare_to_store (void) { /* Do nothing, since we can store individual regs */ diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c index f20ccd3c173..42488580a4e 100644 --- a/gdb/windows-nat.c +++ b/gdb/windows-nat.c @@ -44,6 +44,7 @@ #include <windows.h> #include <imagehlp.h> #include <sys/cygwin.h> +#include <signal.h> #include "buildsym.h" #include "symfile.h" @@ -63,6 +64,13 @@ static struct target_ops win32_ops; static struct target_so_ops win32_so_ops; +/* The starting and ending address of the cygwin1.dll text segment. */ +static bfd_vma cygwin_load_start; +static bfd_vma cygwin_load_end; + +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... */ @@ -346,16 +354,27 @@ do_win32_fetch_inferior_registers (int r) if (current_thread->reload_context) { - thread_info *th = current_thread; - th->context.ContextFlags = CONTEXT_DEBUGGER_DR; - GetThreadContext (th->h, &th->context); - /* Copy dr values from that thread. */ - dr[0] = th->context.Dr0; - dr[1] = th->context.Dr1; - dr[2] = th->context.Dr2; - dr[3] = th->context.Dr3; - dr[6] = th->context.Dr6; - dr[7] = th->context.Dr7; + 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 + { + thread_info *th = current_thread; + th->context.ContextFlags = CONTEXT_DEBUGGER_DR; + GetThreadContext (th->h, &th->context); + /* Copy dr values from that thread. */ + dr[0] = th->context.Dr0; + dr[1] = th->context.Dr1; + dr[2] = th->context.Dr2; + dr[3] = th->context.Dr3; + dr[6] = th->context.Dr6; + dr[7] = th->context.Dr7; + } current_thread->reload_context = 0; } @@ -596,12 +615,10 @@ get_relocated_section_addrs (bfd *abfd, CORE_ADDR text_load) { /* Couldn't get the .text section. Weird. */ } - else if (text_load == (text_vma = bfd_get_section_vma (abfd, text_section))) { /* DLL wasn't relocated. */ } - else { /* Figure out all sections' loaded addresses. The offset here is @@ -640,6 +657,7 @@ solib_symbols_add (struct so_list *so, CORE_ADDR load_addr) static struct objfile *result = NULL; char *name = so->so_name; bfd *abfd = NULL; + char *p; /* The symbols in a dll are offset by 0x1000, which is the the offset from 0 of the first byte in an image - because @@ -660,8 +678,6 @@ solib_symbols_add (struct so_list *so, CORE_ADDR load_addr) { if (bfd_check_format (abfd, bfd_object)) addrs = get_relocated_section_addrs (abfd, load_addr); - - bfd_close (abfd); } if (addrs) @@ -683,13 +699,20 @@ solib_symbols_add (struct so_list *so, CORE_ADDR load_addr) do_cleanups (my_cleanups); } + p = strchr (so->so_name, '\0') - (sizeof ("/cygwin1.dll") - 1); + if (p >= so->so_name && strcasecmp (p, "/cygwin1.dll") == 0) + { + asection *text = bfd_get_section_by_name (abfd, ".text"); + cygwin_load_start = bfd_section_vma (abfd, text); + cygwin_load_end = cygwin_load_start + bfd_section_size (abfd, text); + } + + bfd_close (abfd); + so->symbols_loaded = !!result; return; } -/* Remember the maximum DLL length for printing in info dll command. */ -static int max_dll_name_len; - static char * register_loaded_dll (const char *name, DWORD load_addr, int readsyms) { @@ -733,10 +756,9 @@ register_loaded_dll (const char *name, DWORD load_addr, int readsyms) solib_end->next = so; solib_end = so; len = strlen (so->so_name); - if (len > max_dll_name_len) - max_dll_name_len = len; if (readsyms) solib_symbols_add (so, (CORE_ADDR) load_addr); + return so->so_name; } @@ -789,7 +811,6 @@ handle_load_dll (void *dummy) LOAD_DLL_DEBUG_INFO *event = ¤t_event.u.LoadDll; char dll_buf[MAX_PATH + 1]; char *dll_name = NULL; - char *p; dll_buf[0] = dll_buf[sizeof (dll_buf) - 1] = '\0'; @@ -804,7 +825,6 @@ handle_load_dll (void *dummy) return 1; register_loaded_dll (dll_name, (DWORD) event->lpBaseOfDll + 0x1000, auto_solib_add); - solib_add (NULL, 0, NULL, auto_solib_add); return 1; } @@ -860,7 +880,6 @@ win32_clear_solib (void) { solib_start.next = NULL; solib_end = &solib_start; - max_dll_name_len = sizeof ("DLL Name") - 1; } static void @@ -897,31 +916,49 @@ dll_symbol_command (char *args, int from_tty) static int handle_output_debug_string (struct target_waitstatus *ourstatus) { - char *s; - int gotasig = FALSE; + char *s = NULL; + int retval = 0; if (!target_read_string ((CORE_ADDR) current_event.u.DebugString.lpDebugStringData, &s, 1024, 0) || !s || !*s) - return gotasig; - - if (strncmp (s, _CYGWIN_SIGNAL_STRING, sizeof (_CYGWIN_SIGNAL_STRING) - 1) != 0) + /* nothing to do */; + else if (strncmp (s, _CYGWIN_SIGNAL_STRING, sizeof (_CYGWIN_SIGNAL_STRING) - 1) != 0) { if (strncmp (s, "cYg", 3) != 0) warning (("%s"), s); } 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); - gotasig = target_signal_from_host (sig); + int gotasig = target_signal_from_host (sig); ourstatus->value.sig = gotasig; if (gotasig) - ourstatus->kind = TARGET_WAITKIND_STOPPED; + { + 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; + } } - xfree (s); - return gotasig; + if (s) + xfree (s); + return retval; } static int @@ -1065,11 +1102,17 @@ handle_exception (struct target_waitstatus *ourstatus) DEBUG_EXCEPTION_SIMPLE ("EXCEPTION_ACCESS_VIOLATION"); ourstatus->value.sig = TARGET_SIGNAL_SEGV; { + /* 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; - if (find_pc_partial_function ((CORE_ADDR) current_event.u.Exception - .ExceptionRecord.ExceptionAddress, - &fn, NULL, NULL) - && strncmp (fn, "KERNEL32!IsBad", strlen ("KERNEL32!IsBad")) == 0) + bfd_vma addr = (bfd_vma) current_event.u.Exception.ExceptionRecord.ExceptionAddress; + if ((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; } break; @@ -1146,8 +1189,9 @@ handle_exception (struct target_waitstatus *ourstatus) ourstatus->value.sig = TARGET_SIGNAL_ILL; break; default: + /* Treat unhandled first chance exceptions specially. */ if (current_event.u.Exception.dwFirstChance) - return 0; + 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); @@ -1175,7 +1219,6 @@ win32_continue (DWORD continue_status, int id) res = ContinueDebugEvent (current_event.dwProcessId, current_event.dwThreadId, continue_status); - continue_status = 0; if (res) for (th = &thread_head; (th = th->next) != NULL;) if (((id == -1) || (id == (int) th->id)) && th->suspend_count) @@ -1217,6 +1260,87 @@ fake_create_process (void) 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; + + int pid = PIDGET (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, step=%d, sig=%d);\n", + pid, step, sig)); + + /* Get context for currently selected thread */ + th = thread_rec (current_event.dwThreadId, FALSE); + if (th) + { + if (step) + { + /* Single step by setting t bit */ + win32_fetch_inferior_registers (PS_REGNUM); + 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 = dr[6]; + FIXME: should we set dr6 also ?? */ + 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. */ + + win32_continue (continue_status, pid); +} + /* Get the next event from the child. Return 1 if the event requires handling by WFI (or whatever). */ @@ -1228,6 +1352,7 @@ get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) thread_info *th; static thread_info dummy_thread_info; int retval = 0; + ptid_t ptid = {-1}; last_sig = TARGET_SIGNAL_0; @@ -1240,6 +1365,7 @@ get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) event_code = current_event.dwDebugEventCode; ourstatus->kind = TARGET_WAITKIND_SPURIOUS; th = NULL; + have_saved_context = 0; switch (event_code) { @@ -1353,10 +1479,19 @@ get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) "EXCEPTION_DEBUG_EVENT")); if (saw_create != 1) break; - if (handle_exception (ourstatus)) - retval = current_event.dwThreadId; - else - continue_status = DBG_EXCEPTION_NOT_HANDLED; + 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 */ @@ -1366,8 +1501,7 @@ get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) "OUTPUT_DEBUG_STRING_EVENT")); if (saw_create != 1) break; - if (handle_output_debug_string (ourstatus)) - retval = main_thread_id; + retval = handle_output_debug_string (ourstatus); break; default: @@ -1382,7 +1516,12 @@ get_win32_debug_event (int pid, struct target_waitstatus *ourstatus) } if (!retval || saw_create != 1) - CHECK (win32_continue (continue_status, -1)); + { + if (continue_status == -1) + win32_resume (ptid, 0, 1); + else + CHECK (win32_continue (continue_status, -1)); + } else { inferior_ptid = pid_to_ptid (retval); @@ -1982,87 +2121,6 @@ win32_kill_inferior (void) } static void -win32_resume (ptid_t ptid, int step, enum target_signal sig) -{ - thread_info *th; - DWORD continue_status = DBG_CONTINUE; - - int pid = PIDGET (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, step=%d, sig=%d);\n", - pid, step, sig)); - - /* Get context for currently selected thread */ - th = thread_rec (current_event.dwThreadId, FALSE); - if (th) - { - if (step) - { - /* Single step by setting t bit */ - win32_fetch_inferior_registers (PS_REGNUM); - 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 = dr[6]; - FIXME: should we set dr6 also ?? */ - 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. */ - - win32_continue (continue_status, pid); -} - -static void win32_prepare_to_store (void) { /* Do nothing, since we can store individual regs */ |