1/* Target-dependent code for NetBSD/i386. 2 3 Copyright (C) 1988, 1989, 1991, 1992, 1994, 1996, 2000, 2001, 2002, 2003, 4 2004, 2007 Free Software Foundation, Inc. 5 6 This file is part of GDB. 7 8 This program is free software; you can redistribute it and/or modify 9 it under the terms of the GNU General Public License as published by 10 the Free Software Foundation; either version 3 of the License, or 11 (at your option) any later version. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program. If not, see <http://www.gnu.org/licenses/>. */ 20 21#include "defs.h" 22#include "arch-utils.h" 23#include "frame.h" 24#include "gdbcore.h" 25#include "regcache.h" 26#include "regset.h" 27#include "osabi.h" 28#include "symtab.h" 29 30#include "gdb_assert.h" 31#include "gdb_string.h" 32 33#include "i386-tdep.h" 34#include "i387-tdep.h" 35#include "nbsd-tdep.h" 36#include "solib-svr4.h" 37 38/* From <machine/reg.h>. */ 39static int i386nbsd_r_reg_offset[] = 40{ 41 0 * 4, /* %eax */ 42 1 * 4, /* %ecx */ 43 2 * 4, /* %edx */ 44 3 * 4, /* %ebx */ 45 4 * 4, /* %esp */ 46 5 * 4, /* %ebp */ 47 6 * 4, /* %esi */ 48 7 * 4, /* %edi */ 49 8 * 4, /* %eip */ 50 9 * 4, /* %eflags */ 51 10 * 4, /* %cs */ 52 11 * 4, /* %ss */ 53 12 * 4, /* %ds */ 54 13 * 4, /* %es */ 55 14 * 4, /* %fs */ 56 15 * 4 /* %gs */ 57}; 58 59static void 60i386nbsd_aout_supply_regset (const struct regset *regset, 61 struct regcache *regcache, int regnum, 62 const void *regs, size_t len) 63{ 64 const struct gdbarch_tdep *tdep = gdbarch_tdep (regset->arch); 65 66 gdb_assert (len >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE); 67 68 i386_supply_gregset (regset, regcache, regnum, regs, tdep->sizeof_gregset); 69 i387_supply_fsave (regcache, regnum, (char *) regs + tdep->sizeof_gregset); 70} 71 72static const struct regset * 73i386nbsd_aout_regset_from_core_section (struct gdbarch *gdbarch, 74 const char *sect_name, 75 size_t sect_size) 76{ 77 struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); 78 79 /* NetBSD a.out core dumps don't use seperate register sets for the 80 general-purpose and floating-point registers. */ 81 82 if (strcmp (sect_name, ".reg") == 0 83 && sect_size >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE) 84 { 85 if (tdep->gregset == NULL) 86 tdep->gregset = 87 regset_alloc (gdbarch, i386nbsd_aout_supply_regset, NULL); 88 return tdep->gregset; 89 } 90 91 return NULL; 92} 93 94/* Under NetBSD/i386, signal handler invocations can be identified by the 95 designated code sequence that is used to return from a signal handler. 96 In particular, the return address of a signal handler points to the 97 following code sequence: 98 99 leal 0x10(%esp), %eax 100 pushl %eax 101 pushl %eax 102 movl $0x127, %eax # __sigreturn14 103 int $0x80 104 105 Each instruction has a unique encoding, so we simply attempt to match 106 the instruction the PC is pointing to with any of the above instructions. 107 If there is a hit, we know the offset to the start of the designated 108 sequence and can then check whether we really are executing in the 109 signal trampoline. If not, -1 is returned, otherwise the offset from the 110 start of the return sequence is returned. */ 111#define RETCODE_INSN1 0x8d 112#define RETCODE_INSN2 0x50 113#define RETCODE_INSN3 0x50 114#define RETCODE_INSN4 0xb8 115#define RETCODE_INSN5 0xcd 116 117#define RETCODE_INSN2_OFF 4 118#define RETCODE_INSN3_OFF 5 119#define RETCODE_INSN4_OFF 6 120#define RETCODE_INSN5_OFF 11 121 122static const unsigned char sigtramp_retcode[] = 123{ 124 RETCODE_INSN1, 0x44, 0x24, 0x10, 125 RETCODE_INSN2, 126 RETCODE_INSN3, 127 RETCODE_INSN4, 0x27, 0x01, 0x00, 0x00, 128 RETCODE_INSN5, 0x80, 129}; 130 131static LONGEST 132i386nbsd_sigtramp_offset (struct frame_info *next_frame) 133{ 134 CORE_ADDR pc = frame_pc_unwind (next_frame); 135 unsigned char ret[sizeof(sigtramp_retcode)], insn; 136 LONGEST off; 137 int i; 138 139 if (!safe_frame_unwind_memory (next_frame, pc, &insn, 1)) 140 return -1; 141 142 switch (insn) 143 { 144 case RETCODE_INSN1: 145 off = 0; 146 break; 147 148 case RETCODE_INSN2: 149 /* INSN2 and INSN3 are the same. Read at the location of PC+1 150 to determine if we're actually looking at INSN2 or INSN3. */ 151 if (!safe_frame_unwind_memory (next_frame, pc + 1, &insn, 1)) 152 return -1; 153 154 if (insn == RETCODE_INSN3) 155 off = RETCODE_INSN2_OFF; 156 else 157 off = RETCODE_INSN3_OFF; 158 break; 159 160 case RETCODE_INSN4: 161 off = RETCODE_INSN4_OFF; 162 break; 163 164 case RETCODE_INSN5: 165 off = RETCODE_INSN5_OFF; 166 break; 167 168 default: 169 return -1; 170 } 171 172 pc -= off; 173 174 if (!safe_frame_unwind_memory (next_frame, pc, ret, sizeof (ret))) 175 return -1; 176 177 if (memcmp (ret, sigtramp_retcode, sizeof (ret)) == 0) 178 return off; 179 180 return -1; 181} 182 183/* Return whether the frame preceding NEXT_FRAME corresponds to a 184 NetBSD sigtramp routine. */ 185 186static int 187i386nbsd_sigtramp_p (struct frame_info *next_frame) 188{ 189 CORE_ADDR pc = frame_pc_unwind (next_frame); 190 char *name; 191 192 find_pc_partial_function (pc, &name, NULL, NULL); 193 return (nbsd_pc_in_sigtramp (pc, name) 194 || i386nbsd_sigtramp_offset (next_frame) >= 0); 195} 196 197/* From <machine/signal.h>. */ 198int i386nbsd_sc_reg_offset[] = 199{ 200 10 * 4, /* %eax */ 201 9 * 4, /* %ecx */ 202 8 * 4, /* %edx */ 203 7 * 4, /* %ebx */ 204 14 * 4, /* %esp */ 205 6 * 4, /* %ebp */ 206 5 * 4, /* %esi */ 207 4 * 4, /* %edi */ 208 11 * 4, /* %eip */ 209 13 * 4, /* %eflags */ 210 12 * 4, /* %cs */ 211 15 * 4, /* %ss */ 212 3 * 4, /* %ds */ 213 2 * 4, /* %es */ 214 1 * 4, /* %fs */ 215 0 * 4 /* %gs */ 216}; 217 218static void 219i386nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) 220{ 221 struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); 222 223 /* Obviously NetBSD is BSD-based. */ 224 i386bsd_init_abi (info, gdbarch); 225 226 /* NetBSD has a different `struct reg'. */ 227 tdep->gregset_reg_offset = i386nbsd_r_reg_offset; 228 tdep->gregset_num_regs = ARRAY_SIZE (i386nbsd_r_reg_offset); 229 tdep->sizeof_gregset = 16 * 4; 230 231 /* NetBSD has different signal trampoline conventions. */ 232 tdep->sigtramp_start = 0; 233 tdep->sigtramp_end = 0; 234 tdep->sigtramp_p = i386nbsd_sigtramp_p; 235 236 /* NetBSD uses -freg-struct-return by default. */ 237 tdep->struct_return = reg_struct_return; 238 239 /* NetBSD has a `struct sigcontext' that's different from the 240 original 4.3 BSD. */ 241 tdep->sc_reg_offset = i386nbsd_sc_reg_offset; 242 tdep->sc_num_regs = ARRAY_SIZE (i386nbsd_sc_reg_offset); 243} 244 245/* NetBSD a.out. */ 246 247static void 248i386nbsdaout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) 249{ 250 i386nbsd_init_abi (info, gdbarch); 251 252 /* NetBSD a.out has a single register set. */ 253 set_gdbarch_regset_from_core_section 254 (gdbarch, i386nbsd_aout_regset_from_core_section); 255} 256 257/* NetBSD ELF. */ 258 259static void 260i386nbsdelf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) 261{ 262 struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); 263 264 /* It's still NetBSD. */ 265 i386nbsd_init_abi (info, gdbarch); 266 267 /* But ELF-based. */ 268 i386_elf_init_abi (info, gdbarch); 269 270 /* NetBSD ELF uses SVR4-style shared libraries. */ 271 set_solib_svr4_fetch_link_map_offsets 272 (gdbarch, svr4_ilp32_fetch_link_map_offsets); 273 274 /* NetBSD ELF uses -fpcc-struct-return by default. */ 275 tdep->struct_return = pcc_struct_return; 276} 277 278void 279_initialize_i386nbsd_tdep (void) 280{ 281 gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_AOUT, 282 i386nbsdaout_init_abi); 283 gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_ELF, 284 i386nbsdelf_init_abi); 285} 286