]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blobdiff - gdb/i386nbsd-tdep.c
Copyright updates for 2007.
[thirdparty/binutils-gdb.git] / gdb / i386nbsd-tdep.c
index c327d426de4c58b8ffbf7e27e3a3163bf23d08aa..c1a263aa79781eabc6927c2b94bef038f237cc54 100644 (file)
@@ -1,6 +1,7 @@
-/* Target-dependent code for NetBSD/i386, for GDB.
-   Copyright 1988, 1989, 1991, 1992, 1994, 1996, 2000, 2001, 2002
-   Free Software Foundation, Inc.
+/* Target-dependent code for NetBSD/i386.
+
+   Copyright (C) 1988, 1989, 1991, 1992, 1994, 1996, 2000, 2001, 2002, 2003,
+   2004, 2007 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
 
    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 "gdbtypes.h"
+#include "arch-utils.h"
+#include "frame.h"
 #include "gdbcore.h"
 #include "regcache.h"
+#include "regset.h"
+#include "osabi.h"
+#include "symtab.h"
+
+#include "gdb_assert.h"
+#include "gdb_string.h"
 
+#include "i386-tdep.h"
 #include "i387-tdep.h"
+#include "nbsd-tdep.h"
+#include "solib-svr4.h"
 
-/* Map a GDB register number to an offset in the reg structure.  */
-static int regmap[] =
+/* From <machine/reg.h>.  */
+static int i386nbsd_r_reg_offset[] =
 {
-  ( 0 * 4),            /* %eax */
-  ( 1 * 4),            /* %ecx */
-  ( 2 * 4),            /* %edx */
-  ( 3 * 4),            /* %ebx */
-  ( 4 * 4),            /* %esp */
-  ( 5 * 4),            /* %epb */
-  ( 6 * 4),            /* %esi */
-  ( 7 * 4),            /* %edi */
-  ( 8 * 4),            /* %eip */
-  ( 9 * 4),            /* %eflags */
-  (10 * 4),            /* %cs */
-  (11 * 4),            /* %ss */
-  (12 * 4),            /* %ds */
-  (13 * 4),            /* %es */
-  (14 * 4),            /* %fs */
-  (15 * 4),            /* %gs */
+  0 * 4,                       /* %eax */
+  1 * 4,                       /* %ecx */
+  2 * 4,                       /* %edx */
+  3 * 4,                       /* %ebx */
+  4 * 4,                       /* %esp */
+  5 * 4,                       /* %ebp */
+  6 * 4,                       /* %esi */
+  7 * 4,                       /* %edi */
+  8 * 4,                       /* %eip */
+  9 * 4,                       /* %eflags */
+  10 * 4,                      /* %cs */
+  11 * 4,                      /* %ss */
+  12 * 4,                      /* %ds */
+  13 * 4,                      /* %es */
+  14 * 4,                      /* %fs */
+  15 * 4                       /* %gs */
 };
 
-#define SIZEOF_STRUCT_REG      (16 * 4)
-
 static void
-i386nbsd_supply_reg (char *regs, int regno)
+i386nbsd_aout_supply_regset (const struct regset *regset,
+                            struct regcache *regcache, int regnum,
+                            const void *regs, size_t len)
 {
-  int i;
+  const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch);
 
-  for (i = 0; i <= 15; i++)
-    if (regno == i || regno == -1)
-      supply_register (i, regs + regmap[i]);
+  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);
 }
 
-static void
-fetch_core_registers (char *core_reg_sect, unsigned core_reg_size, int which,
-                      CORE_ADDR ignore)
+static const struct regset *
+i386nbsd_aout_regset_from_core_section (struct gdbarch *gdbarch,
+                                       const char *sect_name,
+                                       size_t sect_size)
 {
-  char *regs, *fsave;
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
 
-  /* We get everything from one section.  */
-  if (which != 0)
-    return;
+  /* NetBSD a.out core dumps don't use seperate register sets for the
+     general-purpose and floating-point registers.  */
 
-  if (core_reg_size < (SIZEOF_STRUCT_REG + 108))
+  if (strcmp (sect_name, ".reg") == 0
+      && sect_size >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE)
     {
-      warning ("Wrong size register set in core file.");
-      return;
+      if (tdep->gregset == NULL)
+        tdep->gregset =
+         regset_alloc (gdbarch, i386nbsd_aout_supply_regset, NULL);
+      return tdep->gregset;
     }
 
-  regs = core_reg_sect;
-  fsave = core_reg_sect + SIZEOF_STRUCT_REG;
+  return NULL;
+}
+
+/* Under NetBSD/i386, signal handler invocations can be identified by the
+   designated code sequence that is used to return from a signal handler.
+   In particular, the return address of a signal handler points to the
+   following code sequence:
 
-  /* Integer registers.  */
-  i386nbsd_supply_reg (regs, -1);
+       leal    0x10(%esp), %eax
+       pushl   %eax
+       pushl   %eax
+       movl    $0x127, %eax            # __sigreturn14
+       int     $0x80
 
-  /* Floating point registers.  */
-  i387_supply_fsave (fsave);
-}
+   Each instruction has a unique encoding, so we simply attempt to match
+   the instruction the PC is pointing to with any of the above instructions.
+   If there is a hit, we know the offset to the start of the designated
+   sequence and can then check whether we really are executing in the
+   signal trampoline.  If not, -1 is returned, otherwise the offset from the
+   start of the return sequence is returned.  */
+#define RETCODE_INSN1          0x8d
+#define RETCODE_INSN2          0x50
+#define RETCODE_INSN3          0x50
+#define RETCODE_INSN4          0xb8
+#define RETCODE_INSN5          0xcd
 
-static void
-fetch_elfcore_registers (char *core_reg_sect, unsigned core_reg_size, int which,
-                         CORE_ADDR ignore)
+#define RETCODE_INSN2_OFF      4
+#define RETCODE_INSN3_OFF      5
+#define RETCODE_INSN4_OFF      6
+#define RETCODE_INSN5_OFF      11
+
+static const unsigned char sigtramp_retcode[] =
+{
+  RETCODE_INSN1, 0x44, 0x24, 0x10,
+  RETCODE_INSN2,
+  RETCODE_INSN3,
+  RETCODE_INSN4, 0x27, 0x01, 0x00, 0x00,
+  RETCODE_INSN5, 0x80,
+};
+
+static LONGEST
+i386nbsd_sigtramp_offset (struct frame_info *next_frame)
 {
-  switch (which)
+  CORE_ADDR pc = frame_pc_unwind (next_frame);
+  unsigned char ret[sizeof(sigtramp_retcode)], insn;
+  LONGEST off;
+  int i;
+
+  if (!safe_frame_unwind_memory (next_frame, pc, &insn, 1))
+    return -1;
+
+  switch (insn)
     {
-    case 0:  /* Integer registers.  */
-      if (core_reg_size != SIZEOF_STRUCT_REG)
-       warning ("Wrong size register set in core file.");
-      else
-       i386nbsd_supply_reg (core_reg_sect, -1);
+    case RETCODE_INSN1:
+      off = 0;
       break;
 
-    case 2:  /* Floating point registers.  */
-      if (core_reg_size != 108)
-       warning ("Wrong size FP register set in core file."); 
+    case RETCODE_INSN2:
+      /* INSN2 and INSN3 are the same.  Read at the location of PC+1
+        to determine if we're actually looking at INSN2 or INSN3.  */
+      if (!safe_frame_unwind_memory (next_frame, pc + 1, &insn, 1))
+       return -1;
+
+      if (insn == RETCODE_INSN3)
+       off = RETCODE_INSN2_OFF;
       else
-       i387_supply_fsave (core_reg_sect);  
+       off = RETCODE_INSN3_OFF;
       break;
 
-    case 3:  /* "Extended" floating point registers.  This is gdb-speak
-               for SSE/SSE2. */
-      if (core_reg_size != 512)
-       warning ("Wrong size XMM register set in core file.");
-      else
-       i387_supply_fxsave (core_reg_sect);
+    case RETCODE_INSN4:
+      off = RETCODE_INSN4_OFF;
       break;
 
-    default:
-      /* Don't know what kind of register request this is; just ignore it.  */
+    case RETCODE_INSN5:
+      off = RETCODE_INSN5_OFF;
       break;
+
+    default:
+      return -1;
     }
+
+  pc -= off;
+
+  if (!safe_frame_unwind_memory (next_frame, pc, ret, sizeof (ret)))
+    return -1;
+
+  if (memcmp (ret, sigtramp_retcode, sizeof (ret)) == 0)
+    return off;
+
+  return -1;
 }
 
-static struct core_fns i386nbsd_core_fns =
+/* Return whether the frame preceding NEXT_FRAME corresponds to a
+   NetBSD sigtramp routine.  */
+
+static int
+i386nbsd_sigtramp_p (struct frame_info *next_frame)
 {
-  bfd_target_unknown_flavour,          /* core_flavour */
-  default_check_format,                        /* check_format */
-  default_core_sniffer,                        /* core_sniffer */
-  fetch_core_registers,                        /* core_read_registers */
-  NULL                                 /* next */
-};
+  CORE_ADDR pc = frame_pc_unwind (next_frame);
+  char *name;
+
+  find_pc_partial_function (pc, &name, NULL, NULL);
+  return (nbsd_pc_in_sigtramp (pc, name)
+         || i386nbsd_sigtramp_offset (next_frame) >= 0);
+}
 
-static struct core_fns i386nbsd_elfcore_fns =
+/* From <machine/signal.h>.  */
+int i386nbsd_sc_reg_offset[] =
 {
-  bfd_target_elf_flavour,              /* core_flavour */
-  default_check_format,                        /* check_format */
-  default_core_sniffer,                        /* core_sniffer */
-  fetch_elfcore_registers,             /* core_read_registers */
-  NULL                                 /* next */
+  10 * 4,                      /* %eax */
+  9 * 4,                       /* %ecx */
+  8 * 4,                       /* %edx */
+  7 * 4,                       /* %ebx */
+  14 * 4,                      /* %esp */
+  6 * 4,                       /* %ebp */
+  5 * 4,                       /* %esi */
+  4 * 4,                       /* %edi */
+  11 * 4,                      /* %eip */
+  13 * 4,                      /* %eflags */
+  12 * 4,                      /* %cs */
+  15 * 4,                      /* %ss */
+  3 * 4,                       /* %ds */
+  2 * 4,                       /* %es */
+  1 * 4,                       /* %fs */
+  0 * 4                                /* %gs */
 };
 
+static void 
+i386nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  /* Obviously NetBSD is BSD-based.  */
+  i386bsd_init_abi (info, gdbarch);
+
+  /* NetBSD has a different `struct reg'.  */
+  tdep->gregset_reg_offset = i386nbsd_r_reg_offset;
+  tdep->gregset_num_regs = ARRAY_SIZE (i386nbsd_r_reg_offset);
+  tdep->sizeof_gregset = 16 * 4;
+
+  /* NetBSD has different signal trampoline conventions.  */
+  tdep->sigtramp_start = 0;
+  tdep->sigtramp_end = 0;
+  tdep->sigtramp_p = i386nbsd_sigtramp_p;
+
+  /* NetBSD uses -freg-struct-return by default.  */
+  tdep->struct_return = reg_struct_return;
+
+  /* NetBSD has a `struct sigcontext' that's different from the
+     original 4.3 BSD.  */
+  tdep->sc_reg_offset = i386nbsd_sc_reg_offset;
+  tdep->sc_num_regs = ARRAY_SIZE (i386nbsd_sc_reg_offset);
+}
+
+/* NetBSD a.out.  */
+
+static void
+i386nbsdaout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  i386nbsd_init_abi (info, gdbarch);
+
+  /* NetBSD a.out has a single register set.  */
+  set_gdbarch_regset_from_core_section
+    (gdbarch, i386nbsd_aout_regset_from_core_section);
+}
+
+/* NetBSD ELF.  */
+
+static void
+i386nbsdelf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+
+  /* It's still NetBSD.  */
+  i386nbsd_init_abi (info, gdbarch);
+
+  /* But ELF-based.  */
+  i386_elf_init_abi (info, gdbarch);
+
+  /* NetBSD ELF uses SVR4-style shared libraries.  */
+  set_solib_svr4_fetch_link_map_offsets
+    (gdbarch, svr4_ilp32_fetch_link_map_offsets);
+
+  /* NetBSD ELF uses -fpcc-struct-return by default.  */
+  tdep->struct_return = pcc_struct_return;
+}
+
 void
 _initialize_i386nbsd_tdep (void)
 {
-  add_core_fns (&i386nbsd_core_fns);
-  add_core_fns (&i386nbsd_elfcore_fns);
+  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_AOUT,
+                         i386nbsdaout_init_abi);
+  gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_ELF,
+                         i386nbsdelf_init_abi);
 }