X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fi386obsd-tdep.c;h=38058bfeaafd6f0681d4407b5fec6b69a5a69daf;hb=5a0e3bd0ac4a38a58c10e9eaaa28f916f0a2309e;hp=5eb0444a8b9009b71ed6e93536ff15a6ec2325c8;hpb=a28109e0ab1ab391794dea2be9a7ee2f0e7b9278;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/i386obsd-tdep.c b/gdb/i386obsd-tdep.c index 5eb0444a8b..38058bfeaa 100644 --- a/gdb/i386obsd-tdep.c +++ b/gdb/i386obsd-tdep.c @@ -1,8 +1,7 @@ /* Target-dependent code for OpenBSD/i386. - Copyright 1988, 1989, 1991, 1992, 1994, 1996, 2000, 2001, 2002, - 2003, 2004, 2005 - Free Software Foundation, Inc. + Copyright (C) 1988, 1989, 1991, 1992, 1994, 1996, 2000, 2001, 2002, 2003, + 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of GDB. @@ -18,12 +17,13 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. */ + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ #include "defs.h" #include "arch-utils.h" #include "frame.h" +#include "frame-unwind.h" #include "gdbcore.h" #include "regcache.h" #include "regset.h" @@ -31,6 +31,7 @@ #include "objfiles.h" #include "osabi.h" #include "target.h" +#include "trad-frame.h" #include "gdb_assert.h" #include "gdb_string.h" @@ -45,13 +46,25 @@ /* Since OpenBSD 3.2, the sigtramp routine is mapped at a random page in virtual memory. The randomness makes it somewhat tricky to detect it, but fortunately we can rely on the fact that the start - of the sigtramp routine is page-aligned. By the way, the mapping - is read-only, so you cannot place a breakpoint in the signal - trampoline. */ + of the sigtramp routine is page-aligned. We recognize the + trampoline by looking for the code that invokes the sigreturn + system call. The offset where we can find that code varies from + release to release. + + By the way, the mapping mentioned above is read-only, so you cannot + place a breakpoint in the signal trampoline. */ /* Default page size. */ static const int i386obsd_page_size = 4096; +/* Offset for sigreturn(2). */ +static const int i386obsd_sigreturn_offset[] = { + 0x0a, /* OpenBSD 3.2 */ + 0x14, /* OpenBSD 3.6 */ + 0x3a, /* OpenBSD 3.8 */ + -1 +}; + /* Return whether the frame preceding NEXT_FRAME corresponds to an OpenBSD sigtramp routine. */ @@ -60,14 +73,17 @@ i386obsd_sigtramp_p (struct frame_info *next_frame) { CORE_ADDR pc = frame_pc_unwind (next_frame); CORE_ADDR start_pc = (pc & ~(i386obsd_page_size - 1)); - const char sigreturn[] = + /* The call sequence invoking sigreturn(2). */ + const gdb_byte sigreturn[] = { 0xb8, 0x67, 0x00, 0x00, 0x00, /* movl $SYS_sigreturn, %eax */ 0xcd, 0x80 /* int $0x80 */ }; size_t buflen = sizeof sigreturn; - char *name, *buf; + const int *offset; + gdb_byte *buf; + char *name; /* If the function has a valid symbol name, it isn't a trampoline. */ @@ -83,21 +99,18 @@ i386obsd_sigtramp_p (struct frame_info *next_frame) /* Allocate buffer. */ buf = alloca (buflen); - /* If we can't read the instructions at START_PC, return zero. */ - if (!safe_frame_unwind_memory (next_frame, start_pc + 0x0a, buf, buflen)) - return 0; - - /* Check for sigreturn(2). */ - if (memcmp (buf, sigreturn, buflen) == 0) - return 1; - - /* If we can't read the instructions at START_PC, return zero. */ - if (!safe_frame_unwind_memory (next_frame, start_pc + 0x14, buf, buflen)) - return 0; - - /* Check for sigreturn(2) (again). */ - if (memcmp (buf, sigreturn, buflen) == 0) - return 1; + /* Loop over all offsets. */ + for (offset = i386obsd_sigreturn_offset; *offset != -1; offset++) + { + /* If we can't read the instructions, return zero. */ + if (!safe_frame_unwind_memory (next_frame, start_pc + *offset, + buf, buflen)) + return 0; + + /* Check for sigreturn(2). */ + if (memcmp (buf, sigreturn, buflen) == 0) + return 1; + } return 0; } @@ -132,11 +145,12 @@ i386obsd_aout_supply_regset (const struct regset *regset, const void *regs, size_t len) { const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch); + const gdb_byte *gregs = regs; gdb_assert (len >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE); i386_supply_gregset (regset, regcache, regnum, regs, tdep->sizeof_gregset); - i387_supply_fsave (regcache, regnum, (char *) regs + tdep->sizeof_gregset); + i387_supply_fsave (regcache, regnum, gregs + tdep->sizeof_gregset); } static const struct regset * @@ -218,7 +232,7 @@ i386obsd_supply_uthread (struct regcache *regcache, { CORE_ADDR sp_addr = addr + I386OBSD_UTHREAD_ESP_OFFSET; CORE_ADDR sp = 0; - char buf[4]; + gdb_byte buf[4]; int i; gdb_assert (regnum >= -1); @@ -252,7 +266,6 @@ i386obsd_supply_uthread (struct regcache *regcache, regcache_raw_supply (regcache, i, buf); } } - } static void @@ -261,7 +274,7 @@ i386obsd_collect_uthread (const struct regcache *regcache, { CORE_ADDR sp_addr = addr + I386OBSD_UTHREAD_ESP_OFFSET; CORE_ADDR sp = 0; - char buf[4]; + gdb_byte buf[4]; int i; gdb_assert (regnum >= -1); @@ -300,6 +313,134 @@ i386obsd_collect_uthread (const struct regcache *regcache, } } } + +/* Kernel debugging support. */ + +/* From . Note that %esp and %ess are only saved in + a trap frame when entering the kernel from user space. */ +static int i386obsd_tf_reg_offset[] = +{ + 10 * 4, /* %eax */ + 9 * 4, /* %ecx */ + 8 * 4, /* %edx */ + 7 * 4, /* %ebx */ + -1, /* %esp */ + 6 * 4, /* %ebp */ + 5 * 4, /* %esi */ + 4 * 4, /* %edi */ + 13 * 4, /* %eip */ + 15 * 4, /* %eflags */ + 14 * 4, /* %cs */ + -1, /* %ss */ + 3 * 4, /* %ds */ + 2 * 4, /* %es */ + 0 * 4, /* %fs */ + 1 * 4 /* %gs */ +}; + +static struct trad_frame_cache * +i386obsd_trapframe_cache(struct frame_info *next_frame, void **this_cache) +{ + struct trad_frame_cache *cache; + CORE_ADDR func, sp, addr; + ULONGEST cs; + char *name; + int i; + + if (*this_cache) + return *this_cache; + + cache = trad_frame_cache_zalloc (next_frame); + *this_cache = cache; + + /* NORMAL_FRAME matches the type in i386obsd_trapframe_unwind, but + SIGTRAMP_FRAME might be more appropriate. */ + func = frame_func_unwind (next_frame, NORMAL_FRAME); + sp = frame_unwind_register_unsigned (next_frame, I386_ESP_REGNUM); + + find_pc_partial_function (func, &name, NULL, NULL); + if (name && strncmp (name, "Xintr", 5) == 0) + addr = sp + 8; /* It's an interrupt frame. */ + else + addr = sp; + + for (i = 0; i < ARRAY_SIZE (i386obsd_tf_reg_offset); i++) + if (i386obsd_tf_reg_offset[i] != -1) + trad_frame_set_reg_addr (cache, i, addr + i386obsd_tf_reg_offset[i]); + + /* Read %cs from trap frame. */ + addr += i386obsd_tf_reg_offset[I386_CS_REGNUM]; + cs = read_memory_unsigned_integer (addr, 4); + if ((cs & I386_SEL_RPL) == I386_SEL_UPL) + { + /* Trap from user space; terminate backtrace. */ + trad_frame_set_id (cache, null_frame_id); + } + else + { + /* Construct the frame ID using the function start. */ + trad_frame_set_id (cache, frame_id_build (sp + 8, func)); + } + + return cache; +} + +static void +i386obsd_trapframe_this_id (struct frame_info *next_frame, + void **this_cache, struct frame_id *this_id) +{ + struct trad_frame_cache *cache = + i386obsd_trapframe_cache (next_frame, this_cache); + + trad_frame_get_id (cache, this_id); +} + +static void +i386obsd_trapframe_prev_register (struct frame_info *next_frame, + void **this_cache, int regnum, + int *optimizedp, enum lval_type *lvalp, + CORE_ADDR *addrp, int *realnump, + gdb_byte *valuep) +{ + struct trad_frame_cache *cache = + i386obsd_trapframe_cache (next_frame, this_cache); + + trad_frame_get_register (cache, next_frame, regnum, + optimizedp, lvalp, addrp, realnump, valuep); +} + +static int +i386obsd_trapframe_sniffer (const struct frame_unwind *self, + struct frame_info *next_frame, + void **this_prologue_cache) +{ + ULONGEST cs; + char *name; + + /* Check Current Privilege Level and bail out if we're not executing + in kernel space. */ + cs = frame_unwind_register_unsigned (next_frame, I386_CS_REGNUM); + if ((cs & I386_SEL_RPL) == I386_SEL_UPL) + return 0; + + find_pc_partial_function (frame_pc_unwind (next_frame), &name, NULL, NULL); + return (name && (strcmp (name, "calltrap") == 0 + || strcmp (name, "syscall1") == 0 + || strncmp (name, "Xintr", 5) == 0 + || strncmp (name, "Xsoft", 5) == 0)); +} + +static const struct frame_unwind i386obsd_trapframe_unwind = { + /* FIXME: kettenis/20051219: This really is more like an interrupt + frame, but SIGTRAMP_FRAME would print , + which really is not what we want here. */ + NORMAL_FRAME, + i386obsd_trapframe_this_id, + i386obsd_trapframe_prev_register, + NULL, + i386obsd_trapframe_sniffer +}; + static void i386obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) @@ -330,6 +471,9 @@ i386obsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) /* OpenBSD provides a user-level threads implementation. */ bsd_uthread_set_supply_uthread (gdbarch, i386obsd_supply_uthread); bsd_uthread_set_collect_uthread (gdbarch, i386obsd_collect_uthread); + + /* Unwind kernel trap frames correctly. */ + frame_unwind_prepend_unwinder (gdbarch, &i386obsd_trapframe_unwind); } /* OpenBSD a.out. */