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

John Baldwin
2015-02-10T14:51:06Z

On Wednesday, February 04, 2015 10:47:07 AM John Baldwin wrote:
> This patch fixes two issues with signal frame unwinding on recent
> FreeBSD/x86:
>
> First, FreeBSD moved the signal trampoline code into a global shared page
> exported by the kernel a few releases ago. To try to make this easier to
> detect, FreeBSD added a new per-process sysctl node that exports the
> starting and ending address of the signal trampoline code. This sysctl
> works on all platforms that FreeBSD supports no matter where the signal
> trampoline lives, but I have only updated i386 and amd64 in this patch.
>
> Second, amd64fbsd_sigcontext_addr was using frame_unwind_register_unsigned
> to fetch the stack pointer which resulted in infinite recursion. I've
> changed it to use get_frame_register() to match the sigcontext_addr methods
> in the i386-bsd and amd64-linux targets instead.

Does anyone have any comments on this patch or is there anything I need to fix
in it?

> Changelog:
>
> 2015-xx-xx John Baldwin <jhb@FreeBSD.org>
>
> * amd64fbsd-nat.c (_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): Fix infinite recursion.
>
> diff --git a/gdb/amd64fbsd-nat.c b/gdb/amd64fbsd-nat.c
> index 1c396e2..6227681 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
> + /* Newer versions of FreeBSD 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;
> }
> \f
> diff --git a/gdb/i386fbsd-nat.c b/gdb/i386fbsd-nat.c
> index f4951d1..5293f0f 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
> + /* Newer versions of FreeBSD 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;

--
John Baldwin