diff --git a/gdb/ChangeLog b/gdb/ChangeLog index b024d231c0..9934346cf2 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,27 @@ +2015-02-25 John Baldwin + + * 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 * amd64fbsd-tdep.c (amd64fbsd_sigcontext_addr): Use diff --git a/gdb/amd64fbsd-nat.c b/gdb/amd64fbsd-nat.c index 1c396e2241..b1b261c196 100644 --- a/gdb/amd64fbsd-nat.c +++ b/gdb/amd64fbsd-nat.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include "fbsd-nat.h" @@ -244,24 +245,31 @@ Please report this to ."), 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. 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 } diff --git a/gdb/amd64fbsd-tdep.c b/gdb/amd64fbsd-tdep.c index abb0cab960..e11b0f371e 100644 --- a/gdb/amd64fbsd-tdep.c +++ b/gdb/amd64fbsd-tdep.c @@ -31,6 +31,33 @@ /* 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 . */ 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; diff --git a/gdb/i386fbsd-nat.c b/gdb/i386fbsd-nat.c index f4951d1912..ad439e3d44 100644 --- a/gdb/i386fbsd-nat.c +++ b/gdb/i386fbsd-nat.c @@ -25,6 +25,7 @@ #include #include #include +#include #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. 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 diff --git a/gdb/i386fbsd-tdep.c b/gdb/i386fbsd-tdep.c index 8d237f0ee2..d4516ee137 100644 --- a/gdb/i386fbsd-tdep.c +++ b/gdb/i386fbsd-tdep.c @@ -29,6 +29,154 @@ #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 . */ @@ -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 . */ 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;