[PATCH] hurd: unwinding support over signal trampolines

Samuel Thibault samuel.thibault@gnu.org
Fri May 29 23:00:49 GMT 2020


gdb/ChangeLog:

2020-05-29  Samuel Thibault  <samuel.thibault@ens-lyon.org>

	* i386-gnu-tdep.c: Include "gdbcore.h"
	(gnu_sigtramp_code, i386_gnu_sc_reg_offset): New arrays.
        (GNU_SIGTRAMP_LEN, GNU_SIGTRAMP_TAIL,
        I386_GNU_SIGCONTEXT_THREAD_STATE_OFFSET): New macros
        (i386_gnu_sigtramp_start, i386_gnu_sigtramp_p,
        i386_gnu_sigcontext_addr): New functions
        (i386gnu_init_abi): Register i386_gnu_sigtramp_p,
        i386_gnu_sigcontext_addr, and i386_gnu_sc_reg_offset in the gdbarch
        tdep.

Index: binutils-gdb/gdb/i386-gnu-tdep.c
===================================================================
--- binutils-gdb.orig/gdb/i386-gnu-tdep.c
+++ binutils-gdb/gdb/i386-gnu-tdep.c
@@ -17,11 +17,138 @@
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
+#include "gdbcore.h"
 #include "osabi.h"
 #include "solib-svr4.h"
 
 #include "i386-tdep.h"
 
+/* Recognizing signal handler frames.  */
+
+/* When the GNU/Hurd libc calls a signal handler, the return address points
+   inside the trampoline assembly snippet.
+
+   If the trampoline function name can not be identified, we resort to reading
+   memory from the process in order to identify it.  */
+
+static const gdb_byte gnu_sigtramp_code[] =
+{
+/* rpc_wait_trampoline: */
+  0xb8, 0xe7, 0xff, 0xff, 0xff,			/* mov    $-25,%eax */
+  0x9a, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00,	/* lcall  $7,$0 */
+  0x89, 0x01,					/* movl   %eax, (%ecx) */
+  0x89, 0xdc,					/* movl   %ebx, %esp */
+
+/* trampoline: */
+  0xff, 0xd2,					/* call   *%edx */
+/* RA HERE */
+  0x83, 0xc4, 0x0c,				/* addl   $12, %esp */
+  0xc3,						/* ret */
+
+/* firewall: */
+  0xf4,						/* hlt */
+};
+
+#define GNU_SIGTRAMP_LEN (sizeof gnu_sigtramp_code)
+#define GNU_SIGTRAMP_TAIL 5			/* length of tail after RA */
+
+/* If THIS_FRAME is a sigtramp routine, return the address of the
+   start of the routine.  Otherwise, return 0.  */
+
+static CORE_ADDR
+i386_gnu_sigtramp_start (struct frame_info *this_frame)
+{
+  CORE_ADDR pc = get_frame_pc (this_frame);
+  gdb_byte buf[GNU_SIGTRAMP_LEN];
+
+  if (!safe_frame_unwind_memory (this_frame,
+				 pc + GNU_SIGTRAMP_TAIL - GNU_SIGTRAMP_LEN,
+				 buf, GNU_SIGTRAMP_LEN))
+    return 0;
+
+  if (memcmp (buf, gnu_sigtramp_code, GNU_SIGTRAMP_LEN) != 0)
+    return 0;
+
+  return pc;
+}
+
+/* Return whether THIS_FRAME corresponds to a GNU/Linux sigtramp
+   routine.  */
+
+static int
+i386_gnu_sigtramp_p (struct frame_info *this_frame)
+{
+  CORE_ADDR pc = get_frame_pc (this_frame);
+  const char *name;
+
+  find_pc_partial_function (pc, &name, NULL, NULL);
+
+  /* If we have a NAME, we can check for the trampoline function */
+  if (name != NULL && strcmp (name, "trampoline") == 0)
+    return 1;
+
+  return i386_gnu_sigtramp_start (this_frame) != 0;
+}
+
+/* Offset to sc_i386_thread_state in sigcontext, from <bits/sigcontext.h>.  */
+#define I386_GNU_SIGCONTEXT_THREAD_STATE_OFFSET 20
+
+/* Assuming THIS_FRAME is a GNU/Linux sigtramp routine, return the
+   address of the associated sigcontext structure.  */
+
+static CORE_ADDR
+i386_gnu_sigcontext_addr (struct frame_info *this_frame)
+{
+  struct gdbarch *gdbarch = get_frame_arch (this_frame);
+  enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+  CORE_ADDR pc;
+  CORE_ADDR sp;
+  gdb_byte buf[4];
+
+  get_frame_register (this_frame, I386_ESP_REGNUM, buf);
+  sp = extract_unsigned_integer (buf, 4, byte_order);
+
+  pc = i386_gnu_sigtramp_start (this_frame);
+  if (pc)
+    {
+      CORE_ADDR sigcontext_addr;
+
+      /* The sigcontext structure address is passed as the third argument to
+	 the signal handler. */
+      read_memory (sp + 8, buf, 4);
+      sigcontext_addr = extract_unsigned_integer (buf, 4, byte_order);
+      return sigcontext_addr + I386_GNU_SIGCONTEXT_THREAD_STATE_OFFSET;
+    }
+
+  error (_("Couldn't recognize signal trampoline."));
+  return 0;
+}
+
+/* Mapping between the general-purpose registers in `struct
+   sigcontext' format (starting at sc_i386_thread_state)
+   and GDB's register cache layout.  */
+
+/* From <bits/sigcontext.h>.  */
+static int i386_gnu_sc_reg_offset[] =
+{
+  11 * 4,			/* %eax */
+  10 * 4,			/* %ecx */
+  9 * 4,			/* %edx */
+  8 * 4,			/* %ebx */
+  7 * 4,			/* %esp */
+  6 * 4,			/* %ebp */
+  5 * 4,			/* %esi */
+  4 * 4,			/* %edi */
+  12 * 4,			/* %eip */
+  14 * 4,			/* %eflags */
+  13 * 4,			/* %cs */
+  16 * 4,			/* %ss */
+  3 * 4,			/* %ds */
+  2 * 4,			/* %es */
+  1 * 4,			/* %fs */
+  0 * 4				/* %gs */
+};
+
 /* From <sys/ucontext.h>.  */
 static int i386gnu_gregset_reg_offset[] =
 {
@@ -59,6 +186,11 @@ i386gnu_init_abi (struct gdbarch_info in
   tdep->sizeof_gregset = 19 * 4;
 
   tdep->jb_pc_offset = 20;	/* From <bits/setjmp.h>.  */
+
+  tdep->sigtramp_p = i386_gnu_sigtramp_p;
+  tdep->sigcontext_addr = i386_gnu_sigcontext_addr;
+  tdep->sc_reg_offset = i386_gnu_sc_reg_offset;
+  tdep->sc_num_regs = ARRAY_SIZE (i386_gnu_sc_reg_offset);
 }
 
 void _initialize_i386gnu_tdep ();


More information about the Gdb-patches mailing list