This is the mail archive of the cygwin mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[PATCH] Teach gdb how to unwind cygwin _sigbe frames


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 <jon.turney@dronecode.org.uk>
---
 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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]