/* 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, 2008 Free Software Foundation, Inc.
This file is part of GDB.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
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. */
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include "defs.h"
#include "arch-utils.h"
#include "frame.h"
+#include "frame-unwind.h"
#include "gdbcore.h"
#include "regcache.h"
#include "regset.h"
#include "objfiles.h"
#include "osabi.h"
#include "target.h"
+#include "trad-frame.h"
#include "gdb_assert.h"
#include "gdb_string.h"
/* 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. */
{
CORE_ADDR pc = frame_pc_unwind (next_frame);
CORE_ADDR start_pc = (pc & ~(i386obsd_page_size - 1));
+ /* The call sequence invoking sigreturn(2). */
const gdb_byte sigreturn[] =
{
0xb8,
0xcd, 0x80 /* int $0x80 */
};
size_t buflen = sizeof sigreturn;
+ const int *offset;
gdb_byte *buf;
char *name;
/* 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;
}
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 *
regcache_raw_supply (regcache, i, buf);
}
}
-
}
static void
}
}
}
+\f
+/* Kernel debugging support. */
+
+/* From <machine/frame.h>. 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 <signal handler called>,
+ which really is not what we want here. */
+ NORMAL_FRAME,
+ i386obsd_trapframe_this_id,
+ i386obsd_trapframe_prev_register,
+ NULL,
+ i386obsd_trapframe_sniffer
+};
+\f
static void
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. */