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