/* Target-dependent code for NetBSD/i386. Copyright 1988, 1989, 1991, 1992, 1994, 1996, 2000, 2001, 2002, 2003, 2004 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 (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 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. */ #include "defs.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" /* From . */ static int i386nbsd_r_reg_offset[] = { 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 */ }; static void i386nbsd_aout_supply_regset (const struct regset *regset, struct regcache *regcache, int regnum, const void *regs, size_t len) { const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch); 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 const struct regset * i386nbsd_aout_regset_from_core_section (struct gdbarch *gdbarch, const char *sect_name, size_t sect_size) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); /* NetBSD a.out core dumps don't use seperate register sets for the general-purpose and floating-point registers. */ if (strcmp (sect_name, ".reg") == 0 && sect_size >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE) { if (tdep->gregset == NULL) tdep->gregset = regset_alloc (gdbarch, i386nbsd_aout_supply_regset, NULL); return tdep->gregset; } 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: leal 0x10(%esp), %eax pushl %eax pushl %eax movl $0x127, %eax # __sigreturn14 int $0x80 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 #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) { 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 RETCODE_INSN1: off = 0; break; 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 off = RETCODE_INSN3_OFF; break; case RETCODE_INSN4: off = RETCODE_INSN4_OFF; break; 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; } /* Return whether the frame preceding NEXT_FRAME corresponds to a NetBSD sigtramp routine. */ static int i386nbsd_sigtramp_p (struct frame_info *next_frame) { 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); } /* From . */ int i386nbsd_sc_reg_offset[] = { 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_gdbarch_in_solib_call_trampoline (gdbarch, generic_in_solib_call_trampoline); 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) { 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); }