Rework signal frame probing for FreeBSD/x86
authorJohn Baldwin <jhb@freebsd.org>
Wed, 25 Feb 2015 14:51:42 +0000 (09:51 -0500)
committerPedro Alves <palves@redhat.com>
Thu, 26 Feb 2015 11:10:25 +0000 (11:10 +0000)
- Use signal frame sniffers that look for the signal trampoline
  instruction sequence to detect most signal frames.

- FreeBSD kernels between 9.2 and 10.1 inclusive do not include the
  signal trampoline code in process core dumps.  To detect signal
  frames for core dumps under these kernels, use the
  kern.proc.sigtramp.<pid> sysctl to fetch the location of the signal
  trampoline in the gdb process and assume that PC values within this
  location are signal frames.  This depends on that location being
  identical for all binaries.

gdb/ChangeLog:
2015-02-25  John Baldwin  <jhb@FreeBSD.org>

* amd64fbsd-nat.c: Include sys/user.h.
(_initialize_amd64fbsd_nat): Use the KERN_PROC_SIGTRAMP sysctl
instead of KERN_PS_STRINGS to locate the signal trampoline.
* i386fbsd-nat.c: Include sys/user.h.
(_initialize_i386fbsd_nat): Use the KERN_PROC_SIGTRAMP sysctl
instead of KERN_PS_STRINGS to locate the signal trampoline.
* amd64fbsd-tdep.c (amd64fbsd_sigtramp_code): New.
(amd64fbsd_sigtramp_p): New.
(amd64fbsd_sigtramp_start_addr, amd64fbsd_sigtramp_end_addr): No
longer set default values.
(amd64fbsd_init_abi): Set "sigtramp_p" to "amd64fbsd_sigtramp_p".
* i386fbsd-tdep.c (i386fbsd_sigtramp_start)
(i386fbsd_sigtramp_middle, i386fbsd_sigtramp_end)
(i386fbsd_freebsd4_sigtramp_start)
(i386fbsd_freebsd4_sigtramp_middle)
(i386fbsd_freebsd4_sigtramp_end, i386fbsd_osigtramp_start)
(i386fbsd_osigtramp_middle, i386fbsd_osigtramp_end): New.
(i386fbsd_sigtramp_p): New.
(i386fbsd_sigtramp_start_addr, i386fbsd_sigtramp_end_addr): No
longer set default values.
(i386fbsd_init_abi): Set "sigtramp_p" to "i386fbsd_sigtramp_p".

gdb/ChangeLog
gdb/amd64fbsd-nat.c
gdb/amd64fbsd-tdep.c
gdb/i386fbsd-nat.c
gdb/i386fbsd-tdep.c

index b024d231c0e1659136f21157205168cdbe68fa4d..9934346cf22de245a296f10d00fd4eaa75036568 100644 (file)
@@ -1,3 +1,27 @@
+2015-02-25  John Baldwin  <jhb@FreeBSD.org>
+
+       * amd64fbsd-nat.c: Include sys/user.h.
+       (_initialize_amd64fbsd_nat): Use the KERN_PROC_SIGTRAMP sysctl
+       instead of KERN_PS_STRINGS to locate the signal trampoline.
+       * i386fbsd-nat.c: Include sys/user.h.
+       (_initialize_i386fbsd_nat): Use the KERN_PROC_SIGTRAMP sysctl
+       instead of KERN_PS_STRINGS to locate the signal trampoline.
+       * amd64fbsd-tdep.c (amd64fbsd_sigtramp_code): New.
+       (amd64fbsd_sigtramp_p): New.
+       (amd64fbsd_sigtramp_start_addr, amd64fbsd_sigtramp_end_addr): No
+       longer set default values.
+       (amd64fbsd_init_abi): Set "sigtramp_p" to "amd64fbsd_sigtramp_p".
+       * i386fbsd-tdep.c (i386fbsd_sigtramp_start)
+       (i386fbsd_sigtramp_middle, i386fbsd_sigtramp_end)
+       (i386fbsd_freebsd4_sigtramp_start)
+       (i386fbsd_freebsd4_sigtramp_middle)
+       (i386fbsd_freebsd4_sigtramp_end, i386fbsd_osigtramp_start)
+       (i386fbsd_osigtramp_middle, i386fbsd_osigtramp_end): New.
+       (i386fbsd_sigtramp_p): New.
+       (i386fbsd_sigtramp_start_addr, i386fbsd_sigtramp_end_addr): No
+       longer set default values.
+       (i386fbsd_init_abi): Set "sigtramp_p" to "i386fbsd_sigtramp_p".
+
 2015-02-25  John Baldwin  <jhb@freebsd.org>
 
        * amd64fbsd-tdep.c (amd64fbsd_sigcontext_addr): Use
index 1c396e22411ee584308187757d558f3cec18c0cb..b1b261c19610d5424f93448fa0054af4e6cad480 100644 (file)
@@ -26,6 +26,7 @@
 #include <sys/types.h>
 #include <sys/ptrace.h>
 #include <sys/sysctl.h>
+#include <sys/user.h>
 #include <machine/reg.h>
 
 #include "fbsd-nat.h"
@@ -244,24 +245,31 @@ Please report this to <bug-gdb@gnu.org>."),
 
   SC_RBP_OFFSET = offset;
 
-  /* FreeBSD provides a kern.ps_strings sysctl that we can use to
-     locate the sigtramp.  That way we can still recognize a sigtramp
-     if its location is changed in a new kernel.  Of course this is
-     still based on the assumption that the sigtramp is placed
-     directly under the location where the program arguments and
-     environment can be found.  */
+#ifdef KERN_PROC_SIGTRAMP
+  /* Normally signal frames are detected via amd64fbsd_sigtramp_p.
+     However, FreeBSD 9.2 through 10.1 do not include the page holding
+     the signal code in core dumps.  These releases do provide a
+     kern.proc.sigtramp.<pid> sysctl that returns the location of the
+     signal trampoline for a running process.  We fetch the location
+     of the current (gdb) process and use this to identify signal
+     frames in core dumps from these releases.  Note that this only
+     works for core dumps of 64-bit (FreeBSD/amd64) processes and does
+     not handle core dumps of 32-bit (FreeBSD/i386) processes.  */
   {
-    int mib[2];
-    long ps_strings;
+    int mib[4];
+    struct kinfo_sigtramp kst;
     size_t len;
 
     mib[0] = CTL_KERN;
-    mib[1] = KERN_PS_STRINGS;
-    len = sizeof (ps_strings);
-    if (sysctl (mib, 2, &ps_strings, &len, NULL, 0) == 0)
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_SIGTRAMP;
+    mib[3] = getpid ();
+    len = sizeof (kst);
+    if (sysctl (mib, 4, &kst, &len, NULL, 0) == 0)
       {
-       amd64fbsd_sigtramp_start_addr = ps_strings - 32;
-       amd64fbsd_sigtramp_end_addr = ps_strings;
+       amd64fbsd_sigtramp_start_addr = (uintptr_t) kst.ksigtramp_start;
+       amd64fbsd_sigtramp_end_addr = (uintptr_t) kst.ksigtramp_end;
       }
   }
+#endif
 }
index abb0cab960258b7d23a1f6577dd4d67152db2c58..e11b0f371ecab9b3ea32262b3c07a14aafd8ab4c 100644 (file)
 
 /* Support for signal handlers.  */
 
+/* Return whether THIS_FRAME corresponds to a FreeBSD sigtramp
+   routine.  */
+
+static const gdb_byte amd64fbsd_sigtramp_code[] =
+{
+  0x48, 0x8d, 0x7c, 0x24, 0x10, /* lea     SIGF_UC(%rsp),%rdi */
+  0x6a, 0x00,                  /* pushq   $0 */
+  0x48, 0xc7, 0xc0, 0xa1, 0x01, 0x00, 0x00,
+                               /* movq    $SYS_sigreturn,%rax */
+  0x0f, 0x05                    /* syscall */
+};
+
+static int
+amd64fbsd_sigtramp_p (struct frame_info *this_frame)
+{
+  CORE_ADDR pc = get_frame_pc (this_frame);
+  gdb_byte buf[sizeof amd64fbsd_sigtramp_code];
+
+  if (!safe_frame_unwind_memory (this_frame, pc, buf, sizeof buf))
+    return 0;
+  if (memcmp (buf, amd64fbsd_sigtramp_code, sizeof amd64fbsd_sigtramp_code) !=
+      0)
+    return 0;
+
+  return 1;
+}
+
 /* Assuming THIS_FRAME is for a BSD sigtramp routine, return the
    address of the associated sigcontext structure.  */
 
@@ -88,8 +115,8 @@ static int amd64fbsd_r_reg_offset[] =
 };
 
 /* Location of the signal trampoline.  */
-CORE_ADDR amd64fbsd_sigtramp_start_addr = 0x7fffffffffc0ULL;
-CORE_ADDR amd64fbsd_sigtramp_end_addr = 0x7fffffffffe0ULL;
+CORE_ADDR amd64fbsd_sigtramp_start_addr;
+CORE_ADDR amd64fbsd_sigtramp_end_addr;
 
 /* From <machine/signal.h>.  */
 int amd64fbsd_sc_reg_offset[] =
@@ -199,6 +226,7 @@ amd64fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
 
   amd64_init_abi (info, gdbarch);
 
+  tdep->sigtramp_p = amd64fbsd_sigtramp_p;
   tdep->sigtramp_start = amd64fbsd_sigtramp_start_addr;
   tdep->sigtramp_end = amd64fbsd_sigtramp_end_addr;
   tdep->sigcontext_addr = amd64fbsd_sigcontext_addr;
index f4951d1912c97e4594e3455f05576df46b0bba7e..ad439e3d44e384729699e8aa1117c072ac75d418 100644 (file)
@@ -25,6 +25,7 @@
 #include <sys/types.h>
 #include <sys/ptrace.h>
 #include <sys/sysctl.h>
+#include <sys/user.h>
 
 #include "fbsd-nat.h"
 #include "i386-tdep.h"
@@ -148,25 +149,28 @@ _initialize_i386fbsd_nat (void)
   /* Support debugging kernel virtual memory images.  */
   bsd_kvm_add_target (i386fbsd_supply_pcb);
 
-  /* FreeBSD provides a kern.ps_strings sysctl that we can use to
-     locate the sigtramp.  That way we can still recognize a sigtramp
-     if its location is changed in a new kernel.  Of course this is
-     still based on the assumption that the sigtramp is placed
-     directly under the location where the program arguments and
-     environment can be found.  */
-#ifdef KERN_PS_STRINGS
+#ifdef KERN_PROC_SIGTRAMP
+  /* Normally signal frames are detected via i386fbsd_sigtramp_p.
+     However, FreeBSD 9.2 through 10.1 do not include the page holding
+     the signal code in core dumps.  These releases do provide a
+     kern.proc.sigtramp.<pid> sysctl that returns the location of the
+     signal trampoline for a running process.  We fetch the location
+     of the current (gdb) process and use this to identify signal
+     frames in core dumps from these releases.  */
   {
-    int mib[2];
-    u_long ps_strings;
+    int mib[4];
+    struct kinfo_sigtramp kst;
     size_t len;
 
     mib[0] = CTL_KERN;
-    mib[1] = KERN_PS_STRINGS;
-    len = sizeof (ps_strings);
-    if (sysctl (mib, 2, &ps_strings, &len, NULL, 0) == 0)
+    mib[1] = KERN_PROC;
+    mib[2] = KERN_PROC_SIGTRAMP;
+    mib[3] = getpid ();
+    len = sizeof (kst);
+    if (sysctl (mib, 4, &kst, &len, NULL, 0) == 0)
       {
-       i386fbsd_sigtramp_start_addr = ps_strings - 128;
-       i386fbsd_sigtramp_end_addr = ps_strings;
+       i386fbsd_sigtramp_start_addr = (uintptr_t) kst.ksigtramp_start;
+       i386fbsd_sigtramp_end_addr = (uintptr_t) kst.ksigtramp_end;
       }
   }
 #endif
index 8d237f0ee2e9a86cc9aacb4816bbceaa9869cd45..d4516ee137b8709fced0ba5b16431255213f31f9 100644 (file)
 #include "fbsd-tdep.h"
 #include "solib-svr4.h"
 
+/* Support for signal handlers.  */
+
+/* Return whether THIS_FRAME corresponds to a FreeBSD sigtramp
+   routine.  */
+
+/* FreeBSD/i386 supports three different signal trampolines, one for
+   versions before 4.0, a second for 4.x, and a third for 5.0 and
+   later.  To complicate matters, FreeBSD/i386 binaries running under
+   an amd64 kernel use a different set of trampolines.  These
+   trampolines differ from the i386 kernel trampolines in that they
+   omit a middle section that conditionally restores %gs.  */
+
+static const gdb_byte i386fbsd_sigtramp_start[] =
+{
+  0x8d, 0x44, 0x24, 0x20,       /* lea     SIGF_UC(%esp),%eax */
+  0x50                         /* pushl   %eax */
+};
+
+static const gdb_byte i386fbsd_sigtramp_middle[] =
+{
+  0xf7, 0x40, 0x54, 0x00, 0x00, 0x02, 0x00,
+                               /* testl   $PSL_VM,UC_EFLAGS(%eax) */
+  0x75, 0x03,                  /* jne     +3 */
+  0x8e, 0x68, 0x14             /* mov     UC_GS(%eax),%gs */
+};
+
+static const gdb_byte i386fbsd_sigtramp_end[] =
+{
+  0xb8, 0xa1, 0x01, 0x00, 0x00, /* movl   $SYS_sigreturn,%eax */
+  0x50,                        /* pushl   %eax */
+  0xcd, 0x80                   /* int     $0x80 */
+};
+
+static const gdb_byte i386fbsd_freebsd4_sigtramp_start[] =
+{
+  0x8d, 0x44, 0x24, 0x14,      /* lea     SIGF_UC4(%esp),%eax */
+  0x50                         /* pushl   %eax */
+};
+
+static const gdb_byte i386fbsd_freebsd4_sigtramp_middle[] =
+{
+  0xf7, 0x40, 0x54, 0x00, 0x00, 0x02, 0x00,
+                               /* testl   $PSL_VM,UC4_EFLAGS(%eax) */
+  0x75, 0x03,                  /* jne     +3 */
+  0x8e, 0x68, 0x14             /* mov     UC4_GS(%eax),%gs */
+};
+
+static const gdb_byte i386fbsd_freebsd4_sigtramp_end[] =
+{
+  0xb8, 0x58, 0x01, 0x00, 0x00, /* movl    $344,%eax */
+  0x50,                        /* pushl   %eax */
+  0xcd, 0x80                   /* int     $0x80 */
+};
+
+static const gdb_byte i386fbsd_osigtramp_start[] =
+{
+  0x8d, 0x44, 0x24, 0x14,      /* lea     SIGF_SC(%esp),%eax */
+  0x50                         /* pushl   %eax */
+};
+
+static const gdb_byte i386fbsd_osigtramp_middle[] =
+{
+  0xf7, 0x40, 0x18, 0x00, 0x00, 0x02, 0x00,
+                               /* testl   $PSL_VM,SC_PS(%eax) */
+  0x75, 0x03,                  /* jne     +3 */
+  0x8e, 0x68, 0x44             /* mov     SC_GS(%eax),%gs */
+};
+
+static const gdb_byte i386fbsd_osigtramp_end[] =
+{
+  0xb8, 0x67, 0x00, 0x00, 0x00, /* movl    $103,%eax */
+  0x50,                        /* pushl   %eax */
+  0xcd, 0x80                   /* int     $0x80 */
+};
+
+/* The three different trampolines are all the same size.  */
+gdb_static_assert (sizeof i386fbsd_sigtramp_start ==
+                  sizeof i386fbsd_freebsd4_sigtramp_start);
+gdb_static_assert (sizeof i386fbsd_sigtramp_start ==
+                  sizeof i386fbsd_osigtramp_start);
+gdb_static_assert (sizeof i386fbsd_sigtramp_middle ==
+                  sizeof i386fbsd_freebsd4_sigtramp_middle);
+gdb_static_assert (sizeof i386fbsd_sigtramp_middle ==
+                  sizeof i386fbsd_osigtramp_middle);
+gdb_static_assert (sizeof i386fbsd_sigtramp_end ==
+                  sizeof i386fbsd_freebsd4_sigtramp_end);
+gdb_static_assert (sizeof i386fbsd_sigtramp_end ==
+                  sizeof i386fbsd_osigtramp_end);
+
+/* We assume that the middle is the largest chunk below.  */
+gdb_static_assert (sizeof i386fbsd_sigtramp_middle >
+                  sizeof i386fbsd_sigtramp_start);
+gdb_static_assert (sizeof i386fbsd_sigtramp_middle >
+                  sizeof i386fbsd_sigtramp_end);
+
+static int
+i386fbsd_sigtramp_p (struct frame_info *this_frame)
+{
+  CORE_ADDR pc = get_frame_pc (this_frame);
+  gdb_byte buf[sizeof i386fbsd_sigtramp_middle];
+  const gdb_byte *middle, *end;
+
+  /* Look for a matching start.  */
+  if (!safe_frame_unwind_memory (this_frame, pc, buf,
+                                sizeof i386fbsd_sigtramp_start))
+    return 0;
+  if (memcmp (buf, i386fbsd_sigtramp_start, sizeof i386fbsd_sigtramp_start) ==
+      0) {
+    middle = i386fbsd_sigtramp_middle;
+    end = i386fbsd_sigtramp_end;
+  } else if (memcmp (buf, i386fbsd_freebsd4_sigtramp_start,
+                    sizeof i386fbsd_freebsd4_sigtramp_start) == 0) {
+    middle = i386fbsd_freebsd4_sigtramp_middle;
+    end = i386fbsd_freebsd4_sigtramp_end;
+  } else if (memcmp (buf, i386fbsd_osigtramp_start,
+                    sizeof i386fbsd_osigtramp_start) == 0) {
+    middle = i386fbsd_osigtramp_middle;
+    end = i386fbsd_osigtramp_end;
+  } else
+    return 0;
+
+  /* Since the end is shorter than the middle, check for a matching end
+     next.  */
+  pc += sizeof i386fbsd_sigtramp_start;
+  if (!safe_frame_unwind_memory (this_frame, pc, buf,
+                                sizeof i386fbsd_sigtramp_end))
+    return 0;
+  if (memcmp (buf, end, sizeof i386fbsd_sigtramp_end) == 0)
+    return 1;
+
+  /* If the end didn't match, check for a matching middle.  */
+  if (!safe_frame_unwind_memory (this_frame, pc, buf,
+                                sizeof i386fbsd_sigtramp_middle))
+    return 0;
+  if (memcmp (buf, middle, sizeof i386fbsd_sigtramp_middle) != 0)
+    return 0;
+
+  /* The middle matched, check for a matching end.  */
+  pc += sizeof i386fbsd_sigtramp_middle;
+  if (!safe_frame_unwind_memory (this_frame, pc, buf,
+                                sizeof i386fbsd_sigtramp_end))
+    return 0;
+  if (memcmp (buf, end, sizeof i386fbsd_sigtramp_end) != 0)
+    return 0;
+
+  return 1;
+}
+
 /* FreeBSD 3.0-RELEASE or later.  */
 
 /* From <machine/reg.h>.  */
@@ -43,8 +191,8 @@ static int i386fbsd_r_reg_offset[] =
 };
 
 /* Sigtramp routine location.  */
-CORE_ADDR i386fbsd_sigtramp_start_addr = 0xbfbfdf20;
-CORE_ADDR i386fbsd_sigtramp_end_addr = 0xbfbfdff0;
+CORE_ADDR i386fbsd_sigtramp_start_addr;
+CORE_ADDR i386fbsd_sigtramp_end_addr;
 
 /* From <machine/signal.h>.  */
 int i386fbsd_sc_reg_offset[] =
@@ -139,6 +287,8 @@ i386fbsdaout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
   /* FreeBSD uses -freg-struct-return by default.  */
   tdep->struct_return = reg_struct_return;
 
+  tdep->sigtramp_p = i386fbsd_sigtramp_p;
+
   /* FreeBSD uses a different memory layout.  */
   tdep->sigtramp_start = i386fbsd_sigtramp_start_addr;
   tdep->sigtramp_end = i386fbsd_sigtramp_end_addr;