X-Recipient: archive-cygwin AT delorie DOT com DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id; q=dns; s= default; b=pL/imUXYVBGE7vrha5RVc9Qi7kmj940vPPgmLthQVn6xSIwIinBUH 5HXiG8Bm1A2bjPm1WcZEa2gJQ4uG7da40KcScn35JeOVl4f7g6gLL3r9fQSuJnBF N3lpbClVfngR5/fEEkDWbGSGMk7z/HgzwTLeWIegS0hRROZaYksomg= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:from:to:cc:subject:date:message-id; s=default; bh=N8pZyY6hEbYviS+6f+ArXyyYvgQ=; b=dJIhT2ORsvcBB+oFICrThTxUYUut 7/ZQp4gZftwnCQpQtUTa7cm0R68KYKiHBIkkrKYImHu2aKP5kejJaB4QEELQ5EwZ Gk9M/DQ5O/7GzWgMS8RRdOETrYC4aWTpCUndY0s8TPlg1si4U/y2V5ajzAGU3DLx cLd6jg+wZz0HGN8= Mailing-List: contact cygwin-help AT cygwin DOT com; run by ezmlm List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: cygwin-owner AT cygwin DOT com Mail-Followup-To: cygwin AT cygwin DOT com Delivered-To: mailing list cygwin AT cygwin DOT com Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.2 required=5.0 tests=AWL,BAYES_00,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 X-HELO: rgout0503.bt.lon5.cpcloud.co.uk X-OWM-Source-IP: 31.51.205.191(GB) X-OWM-Env-Sender: jonturney AT btinternet DOT com X-CTCH-RefID: str=0001.0A090202.54EB6E42.0049,ss=1,re=0.001,recu=0.000,reip=0.000,cl=1,cld=1,fgs=0 X-Junkmail-Premium-Raw: score=27/50,refid=2.7.2:2015.2.23.164222:17:27.888,ip=31.51.205.191,rules=__HAS_FROM, __TO_MALFORMED_2, __TO_NO_NAME, __SUBJ_ALPHA_END, __HAS_MSGID, __SANE_MSGID, __HAS_X_MAILER, __ANY_URI, URI_ENDS_IN_HTML, __URI_NO_PATH, __MIME_TEXT_ONLY, RDNS_GENERIC_POOLED, __URI_NS, SXL_IP_DYNAMIC[191.205.51.31.fur], HTML_00_01, HTML_00_10, RDNS_SUSP_GENERIC, RDNS_SUSP X-CTCH-Spam: Unknown From: Jon TURNEY To: cygwin AT cygwin DOT com Cc: Jon TURNEY Subject: [PATCH] Teach gdb how to unwind cygwin _sigbe frames Date: Mon, 23 Feb 2015 18:15:01 +0000 Message-Id: <1424715301-13516-1-git-send-email-jon.turney@dronecode.org.uk> I really wanted to do this by adding some DWARF CFI to the generated sigfe.s file, but there doesn't seem to currently be a way to correctly describe _sigbe's frame using that. So instead, write a custom unwinder for _sigbe frames, which gets the return address from the sigstack. Implemented for i386 and amd64. Testcases: 1. Set a breakpoint on a _sigfe wrapped function in a program which calls it. When the breakpoint is hit, examine the backtrace. 2. With 'set cygwin-exceptions on', try the sample from: https://www.sourceware.org/ml/gdb/2013-10/msg00167.html When the exception is hit, examine the backtrace. Issues: 1. This should detect if we are in _sigbe after the return address has been popped off the sigstack, and if so, fetch the return address from the register it's been popped into instead. 2. If a signal has actually arrived while inside the sigfe-wrapped function, we will return via sigdelayed which has been pushed onto the sigstack, rather than directly to the caller. We probably need to give sigdelayed's stack frame the same special handling, so that we can backtrace from the signal handler correctly. 3. If there are multiple sigbe or sigdelayed stack frames to be unwound, this only unwinds the first one correctly, because we don't unwind the value of sigstackptr itself. This is no worse than currently, when we can't even unwind one sigbe frame correctly, but isn't quite correct. Presumably that can happen if a signal handler calls a cygwin DLL function which is wrapped by _sigfe. If we care about that, I guess we would need to define a pseduo-register to track it's value as we unwind the stack. 4. This unfortunately ends up hardcoding into gdb the offset of sigstackptr in the cygwin TLS area from the top of stack, and so will break if that changes. Hopefully that doesn't happen to often. Signed-off-by: Jon TURNEY --- gdb/amd64-windows-tdep.c | 2 + gdb/i386-cygwin-tdep.c | 3 + gdb/windows-tdep.c | 139 +++++++++++++++++++++++++++++++++++++++++++++++ gdb/windows-tdep.h | 3 + 4 files changed, 147 insertions(+) diff --git a/gdb/amd64-windows-tdep.c b/gdb/amd64-windows-tdep.c index 331ce77..18bace4 100644 --- a/gdb/amd64-windows-tdep.c +++ b/gdb/amd64-windows-tdep.c @@ -1171,6 +1171,8 @@ amd64_windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + frame_unwind_append_unwinder (gdbarch, &cygwin_sigwrapper_frame_unwind); + /* The dwarf2 unwinder (appended very early by i386_gdbarch_init) is preferred over the SEH one. The reasons are: - binaries without SEH but with dwarf2 debug info are correcly handled diff --git a/gdb/i386-cygwin-tdep.c b/gdb/i386-cygwin-tdep.c index a23f80e..e9280f4 100644 --- a/gdb/i386-cygwin-tdep.c +++ b/gdb/i386-cygwin-tdep.c @@ -27,6 +27,7 @@ #include "xml-support.h" #include "gdbcore.h" #include "inferior.h" +#include "frame-unwind.h" /* Core file support. */ @@ -224,6 +225,8 @@ i386_cygwin_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); + frame_unwind_append_unwinder (gdbarch, &cygwin_sigwrapper_frame_unwind); + windows_init_abi (info, gdbarch); set_gdbarch_skip_trampoline_code (gdbarch, i386_cygwin_skip_trampoline_code); diff --git a/gdb/windows-tdep.c b/gdb/windows-tdep.c index 32007f6..2846dc8 100644 --- a/gdb/windows-tdep.c +++ b/gdb/windows-tdep.c @@ -33,6 +33,8 @@ #include "complaints.h" #include "solib.h" #include "solib-target.h" +#include "frame-unwind.h" +#include "gdbcore.h" struct cmd_list_element *info_w32_cmdlist; @@ -539,3 +541,140 @@ even if their meaning is unknown."), isn't another convenience variable of the same name. */ create_internalvar_type_lazy ("_tlb", &tlb_funcs, NULL); } + +struct cygwin_sigwrapper_frame_cache +{ + CORE_ADDR sp; + CORE_ADDR pc; + CORE_ADDR prev_pc; +}; + +/* Fill THIS_CACHE using the cygwin sigwrapper unwinding data + for THIS_FRAME. */ + +static struct cygwin_sigwrapper_frame_cache * +cygwin_sigwrapper_frame_cache (struct frame_info *this_frame, void **this_cache) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + enum bfd_endian byte_order = gdbarch_byte_order (gdbarch); + struct cygwin_sigwrapper_frame_cache *cache; + ptid_t ptid; + CORE_ADDR thread_local_base; + CORE_ADDR stacktop; + CORE_ADDR signalstackptr; + CORE_ADDR ra; + const int len = gdbarch_addr_bit (gdbarch)/8; + gdb_byte buf[len]; + const int tlsoffset = (gdbarch_ptr_bit (gdbarch) == 64 ? 0x1da0 : 0x24d4); + + if (*this_cache) + return *this_cache; + + cache = FRAME_OBSTACK_ZALLOC (struct cygwin_sigwrapper_frame_cache); + *this_cache = cache; + + /* Get current PC and SP. */ + cache->pc = get_frame_pc (this_frame); + get_frame_register (this_frame, gdbarch_sp_regnum(gdbarch), buf); + cache->sp = extract_unsigned_integer (buf, len, byte_order); + + /* + XXX: this isn't quite correct: if we are in the epilogue, the return + address has already been popped from sigstack and is in r11 + */ + + /* Get address of top of stack from thread information block */ + ptid = inferior_ptid; + target_get_tib_address (ptid, &thread_local_base); + + read_memory(thread_local_base + len, buf, len); + stacktop = extract_unsigned_integer(buf, len, byte_order); + + if (frame_debug) + fprintf_unfiltered (gdb_stdlog, + "cygwin_sigwrapper_frame_prev_register TEB.stacktop=%s\n", + paddress (gdbarch, stacktop )); + + /* Find cygtls, relative to stacktop, and read signalstackptr from cygtls */ + read_memory(stacktop - tlsoffset, buf, len); + signalstackptr = extract_unsigned_integer(buf, len, byte_order); + + if (frame_debug) + fprintf_unfiltered (gdb_stdlog, + "cygwin_sigwrapper_frame_prev_register sigsp=%s\n", + paddress (gdbarch, signalstackptr)); + + /* Read return address from signal stack */ + read_memory (signalstackptr - len, buf, len); + cache->prev_pc = extract_unsigned_integer (buf, len, byte_order); + + if (frame_debug) + fprintf_unfiltered (gdb_stdlog, + "cygwin_sigwrapper_frame_prev_register ra=%s\n", + paddress (gdbarch, cache->prev_pc)); + + return cache; +} + +static struct value * +cygwin_sigwrapper_frame_prev_register (struct frame_info *this_frame, void **this_cache, + int regnum) +{ + struct gdbarch *gdbarch = get_frame_arch (this_frame); + struct cygwin_sigwrapper_frame_cache *cache = + cygwin_sigwrapper_frame_cache (this_frame, this_cache); + + if (frame_debug) + fprintf_unfiltered (gdb_stdlog, + "cygwin_sigwrapper_frame_prev_register %s for pc=%s\n", + gdbarch_register_name (gdbarch, regnum), + paddress (gdbarch, cache->prev_pc)); + + if (regnum == gdbarch_pc_regnum (gdbarch)) + return frame_unwind_got_address (this_frame, regnum, cache->prev_pc); + + return frame_unwind_got_register (this_frame, regnum, regnum); +} + +static void +cygwin_sigwrapper_frame_this_id (struct frame_info *this_frame, void **this_cache, + struct frame_id *this_id) +{ + struct cygwin_sigwrapper_frame_cache *cache = + cygwin_sigwrapper_frame_cache (this_frame, this_cache); + + *this_id = frame_id_build_unavailable_stack (get_frame_func (this_frame)); +} + +/* Return whether PC points inside the signal wrapper. */ + +static int +in_sigwrapper_p (CORE_ADDR pc) +{ + const char *name; + + find_pc_partial_function (pc, &name, NULL, NULL); + + return name && + ((strcmp(name, "_sigbe") == 0) || (strcmp(name, "__sigbe") == 0)); +} + +static int +cygwin_sigwrapper_frame_sniffer (const struct frame_unwind *self, + struct frame_info *this_frame, + void **this_cache) +{ + return in_sigwrapper_p (get_frame_pc (this_frame)); +} + +/* Cygwin sigwapper unwinder. */ + +const struct frame_unwind cygwin_sigwrapper_frame_unwind = +{ + NORMAL_FRAME, + default_frame_unwind_stop_reason, + &cygwin_sigwrapper_frame_this_id, + &cygwin_sigwrapper_frame_prev_register, + NULL, + &cygwin_sigwrapper_frame_sniffer +}; diff --git a/gdb/windows-tdep.h b/gdb/windows-tdep.h index 7704c82..82cf826 100644 --- a/gdb/windows-tdep.h +++ b/gdb/windows-tdep.h @@ -32,4 +32,7 @@ extern void windows_xfer_shared_library (const char* so_name, extern void windows_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch); + +extern const struct frame_unwind cygwin_sigwrapper_frame_unwind; + #endif -- 2.1.4 -- Problem reports: http://cygwin.com/problems.html FAQ: http://cygwin.com/faq/ Documentation: http://cygwin.com/docs.html Unsubscribe info: http://cygwin.com/ml/#unsubscribe-simple