diff mbox

Fix signal trampoline detection/unwinding on recent FreeBSD/i386 and FreeBSD/amd64

Message ID 1778386.0IyHlhpa5R@ralph.baldwin.cx
State New
Headers show

Commit Message

John Baldwin Feb. 10, 2015, 7:14 p.m. UTC
On Tuesday, February 10, 2015 05:08:14 PM Pedro Alves wrote:
> On 02/10/2015 02:50 PM, John Baldwin wrote:
> >> +     sysctl that returns the location of the signal trampoline.
> >> +     Note that this fetches the address for the current (gdb) process.
> >> +     This will be correct for other 64-bit processes, but the signal
> >> +     trampoline location is not properly set for 32-bit processes. */
> 
> I'm not sure I understand what does "but the signal trampoline
> location is not properly set for 32-bit processes" means.  You mean
> it's not properly set because GDB is 64-bit; or it's not properly set
> in the kernel; or something else?

The sysctl is designed to be used against the target process, but I did not
see an easy way to hook into each run and ptrace attach to invoke the sysctl
against the inferior directly.  Instead, the sysctl is invoked on the running
gdb process under the assumption that all binaries using the same OSABI will
use the same signal trampoline location (which is true under FreeBSD).
However, the case that breaks is if I'm using a 64-bit gdb to debug a 32-bit
process.  That is, I don't have an easy way to set the i386fbsd_sigtramp_*
variables from amd64fbsd-nat.c as there isn't a specific PID I know I can
safely query these values from.  All that said, that's also a corner case and
this patch will at least fix the more common 64-on-64 and 32-on-32 cases.

> >> +      }
> >> +  }
> >> +#else
> 
> Did you consider making GDB fallback to the #else block at runtime, for the
> case GDB that was built against newer FreeBSD headers but is actually
> running on older FreeBSD ?

In general FreeBSD only supports backwards compatibility rather than forwards
compatibility (so a binary built on an older version should generally work on
newer versions, but the opposite is not guaranteed).  Given that the Os
doesn't really provide for newer binaries to run against older kernels, I
don't think it is worth extra work in gdb to tailor to that case.

I think the updated version below addresses your other comments (thanks!):

ChangeLog:

2015-xx-xx  John Baldwin  <jhb@FreeBSD.org>

	* amd64fbsd-nat.c: Include sys/user.h.
	(_initialize_amd64fbsd_nat): Use the KERN_PROC_SIGTRAMP sysctl to
	locate the signal trampoline.
	* i386fbsd-nat.c (_initialize_i386fbsd_nat): Likewise.
	* amd64fbsd-tdep.c (amd64fbsd_sigcontext_addr): Use get_frame_register.
diff mbox

Patch

diff --git a/gdb/amd64fbsd-nat.c b/gdb/amd64fbsd-nat.c
index 1c396e2..762b13f 100644
--- a/gdb/amd64fbsd-nat.c
+++ b/gdb/amd64fbsd-nat.c
@@ -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,6 +245,29 @@  Please report this to <bug-gdb@gnu.org>."),
 
   SC_RBP_OFFSET = offset;
 
+#ifdef KERN_PROC_SIGTRAMP
+  /* FreeBSD 9.2 and later provide a kern.proc.sigtramp.<pid>
+     sysctl that returns the location of the signal trampoline.
+     Note that this fetches the address for the current (gdb) process.
+     This will be correct for other 64-bit processes, but the signal
+     trampoline location is not properly set for 32-bit processes.  */
+  {
+    int mib[4];
+    struct kinfo_sigtramp kst;
+    size_t len;
+
+    mib[0] = CTL_KERN;
+    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 = (uintptr_t) kst.ksigtramp_start;
+	amd64fbsd_sigtramp_end_addr = (uintptr_t) kst.ksigtramp_end;
+      }
+  }
+#else
   /* 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
@@ -264,4 +288,5 @@  Please report this to <bug-gdb@gnu.org>."),
 	amd64fbsd_sigtramp_end_addr = ps_strings;
       }
   }
+#endif
 }
diff --git a/gdb/amd64fbsd-tdep.c b/gdb/amd64fbsd-tdep.c
index 2d49cdf..abb0cab 100644
--- a/gdb/amd64fbsd-tdep.c
+++ b/gdb/amd64fbsd-tdep.c
@@ -37,12 +37,16 @@ 
 static CORE_ADDR
 amd64fbsd_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 sp;
+  gdb_byte buf[8];
 
   /* The `struct sigcontext' (which really is an `ucontext_t' on
      FreeBSD/amd64) lives at a fixed offset in the signal frame.  See
      <machine/sigframe.h>.  */
-  sp = frame_unwind_register_unsigned (this_frame, AMD64_RSP_REGNUM);
+  get_frame_register (this_frame, AMD64_RSP_REGNUM, buf);
+  sp = extract_unsigned_integer (buf, 8, byte_order);
   return sp + 16;
 }
 
diff --git a/gdb/i386fbsd-nat.c b/gdb/i386fbsd-nat.c
index f4951d1..26123d6 100644
--- a/gdb/i386fbsd-nat.c
+++ b/gdb/i386fbsd-nat.c
@@ -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,13 +149,34 @@  _initialize_i386fbsd_nat (void)
   /* Support debugging kernel virtual memory images.  */
   bsd_kvm_add_target (i386fbsd_supply_pcb);
 
+#ifdef KERN_PROC_SIGTRAMP
+  /* FreeBSD 9.2 and later provide a kern.proc.sigtramp.<pid>
+     sysctl that returns the location of the signal trampoline.
+     Note that this fetches the address for the current (gdb) process,
+     but should be correct for other processes.  */
+  {
+    int mib[4];
+    struct kinfo_sigtramp kst;
+    size_t len;
+
+    mib[0] = CTL_KERN;
+    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 = (uintptr_t) kst.ksigtramp_start;
+	i386fbsd_sigtramp_end_addr = (uintptr_t) kst.ksigtramp_end;
+      }
+  }
+#elif defined(KERN_PS_STRINGS)
   /* 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
   {
     int mib[2];
     u_long ps_strings;