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 "gdbcore.h" 27#include "regcache.h" 28#include "regset.h" 29#include "osabi.h" 30 31#include "gdb_assert.h" 32#include "gdb_string.h" 33 34#include "i386-tdep.h" 35#include "i387-tdep.h" 36#include "nbsd-tdep.h" 37#include "solib-svr4.h" 38 39/* From <machine/reg.h>. */ 40static int i386nbsd_r_reg_offset[] = 41{ 42 0 * 4, /* %eax */ 43 1 * 4, /* %ecx */ 44 2 * 4, /* %edx */ 45 3 * 4, /* %ebx */ 46 4 * 4, /* %esp */ 47 5 * 4, /* %ebp */ 48 6 * 4, /* %esi */ 49 7 * 4, /* %edi */ 50 8 * 4, /* %eip */ 51 9 * 4, /* %eflags */ 52 10 * 4, /* %cs */ 53 11 * 4, /* %ss */ 54 12 * 4, /* %ds */ 55 13 * 4, /* %es */ 56 14 * 4, /* %fs */ 57 15 * 4 /* %gs */ 58}; 59 60static void 61i386nbsd_aout_supply_regset (const struct regset *regset, 62 struct regcache *regcache, int regnum, 63 const void *regs, size_t len) 64{ 65 const struct gdbarch_tdep *tdep = regset->descr; 66 67 gdb_assert (len >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE); 68 69 i386_supply_gregset (regset, regcache, regnum, regs, tdep->sizeof_gregset); 70 i387_supply_fsave (regcache, regnum, (char *) regs + tdep->sizeof_gregset); 71} 72 73static const struct regset * 74i386nbsd_aout_regset_from_core_section (struct gdbarch *gdbarch, 75 const char *sect_name, 76 size_t sect_size) 77{ 78 struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); 79 80 /* NetBSD a.out core dumps don't use seperate register sets for the 81 general-purpose and floating-point registers. */ 82 83 if (strcmp (sect_name, ".reg") == 0 84 && sect_size >= tdep->sizeof_gregset + I387_SIZEOF_FSAVE) 85 { 86 if (tdep->gregset == NULL) 87 { 88 tdep->gregset = XMALLOC (struct regset); 89 tdep->gregset->descr = tdep; 90 tdep->gregset->supply_regset = i386nbsd_aout_supply_regset; 91 } 92 return tdep->gregset; 93 } 94 95 return NULL; 96} 97 98/* Under NetBSD/i386, signal handler invocations can be identified by the 99 designated code sequence that is used to return from a signal handler. 100 In particular, the return address of a signal handler points to the 101 following code sequence: 102 103 leal 0x10(%esp), %eax 104 pushl %eax 105 pushl %eax 106 movl $0x127, %eax # __sigreturn14 107 int $0x80 108 109 Each instruction has a unique encoding, so we simply attempt to match 110 the instruction the PC is pointing to with any of the above instructions. 111 If there is a hit, we know the offset to the start of the designated 112 sequence and can then check whether we really are executing in the 113 signal trampoline. If not, -1 is returned, otherwise the offset from the 114 start of the return sequence is returned. */ 115#define RETCODE_INSN1 0x8d 116#define RETCODE_INSN2 0x50 117#define RETCODE_INSN3 0x50 118#define RETCODE_INSN4 0xb8 119#define RETCODE_INSN5 0xcd 120 121#define RETCODE_INSN2_OFF 4 122#define RETCODE_INSN3_OFF 5 123#define RETCODE_INSN4_OFF 6 124#define RETCODE_INSN5_OFF 11 125 126static const unsigned char sigtramp_retcode[] = 127{ 128 RETCODE_INSN1, 0x44, 0x24, 0x10, 129 RETCODE_INSN2, 130 RETCODE_INSN3, 131 RETCODE_INSN4, 0x27, 0x01, 0x00, 0x00, 132 RETCODE_INSN5, 0x80, 133}; 134 135static LONGEST 136i386nbsd_sigtramp_offset (CORE_ADDR pc) 137{ 138 unsigned char ret[sizeof(sigtramp_retcode)], insn; 139 LONGEST off; 140 int i; 141 142 if (read_memory_nobpt (pc, &insn, 1) != 0) 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 (read_memory_nobpt (pc + 1, &insn, 1) != 0) 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 (read_memory_nobpt (pc, (char *) ret, sizeof (ret)) != 0) 178 return -1; 179 180 if (memcmp (ret, sigtramp_retcode, sizeof (ret)) == 0) 181 return off; 182 183 return -1; 184} 185 186static int 187i386nbsd_pc_in_sigtramp (CORE_ADDR pc, char *name) 188{ 189 return (nbsd_pc_in_sigtramp (pc, name) 190 || i386nbsd_sigtramp_offset (pc) >= 0); 191} 192 193/* From <machine/signal.h>. */ 194int i386nbsd_sc_reg_offset[] = 195{ 196 10 * 4, /* %eax */ 197 9 * 4, /* %ecx */ 198 8 * 4, /* %edx */ 199 7 * 4, /* %ebx */ 200 14 * 4, /* %esp */ 201 6 * 4, /* %ebp */ 202 5 * 4, /* %esi */ 203 4 * 4, /* %edi */ 204 11 * 4, /* %eip */ 205 13 * 4, /* %eflags */ 206 12 * 4, /* %cs */ 207 15 * 4, /* %ss */ 208 3 * 4, /* %ds */ 209 2 * 4, /* %es */ 210 1 * 4, /* %fs */ 211 0 * 4 /* %gs */ 212}; 213 214static void 215i386nbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) 216{ 217 struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); 218 219 /* Obviously NetBSD is BSD-based. */ 220 i386bsd_init_abi (info, gdbarch); 221 222 /* NetBSD has a different `struct reg'. */ 223 tdep->gregset_reg_offset = i386nbsd_r_reg_offset; 224 tdep->gregset_num_regs = ARRAY_SIZE (i386nbsd_r_reg_offset); 225 tdep->sizeof_gregset = 16 * 4; 226 227 /* NetBSD has different signal trampoline conventions. */ 228 set_gdbarch_pc_in_sigtramp (gdbarch, i386nbsd_pc_in_sigtramp); 229 /* FIXME: kettenis/20020906: We should probably provide 230 NetBSD-specific versions of these functions if we want to 231 recognize signal trampolines that live on the stack. */ 232 set_gdbarch_sigtramp_start (gdbarch, NULL); 233 set_gdbarch_sigtramp_end (gdbarch, NULL); 234 235 /* NetBSD uses -freg-struct-return by default. */ 236 tdep->struct_return = reg_struct_return; 237 238 /* NetBSD has a `struct sigcontext' that's different from the 239 origional 4.3 BSD. */ 240 tdep->sc_reg_offset = i386nbsd_sc_reg_offset; 241 tdep->sc_num_regs = ARRAY_SIZE (i386nbsd_sc_reg_offset); 242} 243 244/* NetBSD a.out. */ 245 246static void 247i386nbsdaout_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) 248{ 249 i386nbsd_init_abi (info, gdbarch); 250 251 /* NetBSD a.out has a single register set. */ 252 set_gdbarch_regset_from_core_section 253 (gdbarch, i386nbsd_aout_regset_from_core_section); 254} 255 256/* NetBSD ELF. */ 257 258static void 259i386nbsdelf_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) 260{ 261 struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); 262 263 /* It's still NetBSD. */ 264 i386nbsd_init_abi (info, gdbarch); 265 266 /* But ELF-based. */ 267 i386_elf_init_abi (info, gdbarch); 268 269 /* NetBSD ELF uses SVR4-style shared libraries. */ 270 set_gdbarch_in_solib_call_trampoline 271 (gdbarch, generic_in_solib_call_trampoline); 272 set_solib_svr4_fetch_link_map_offsets 273 (gdbarch, svr4_ilp32_fetch_link_map_offsets); 274 275 /* NetBSD ELF uses -fpcc-struct-return by default. */ 276 tdep->struct_return = pcc_struct_return; 277} 278 279void 280_initialize_i386nbsd_tdep (void) 281{ 282 gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_AOUT, 283 i386nbsdaout_init_abi); 284 gdbarch_register_osabi (bfd_arch_i386, 0, GDB_OSABI_NETBSD_ELF, 285 i386nbsdelf_init_abi); 286} 287