/* Target-dependent code for NetBSD/alpha. Copyright (C) 2002-2014 Free Software Foundation, Inc. Contributed by Wasabi Systems, 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 3 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, see . */ #include "defs.h" #include "frame.h" #include "gdbcore.h" #include "osabi.h" #include "regcache.h" #include "regset.h" #include "value.h" #include "alpha-tdep.h" #include "alphabsd-tdep.h" #include "nbsd-tdep.h" #include "solib-svr4.h" #include "target.h" /* Core file support. */ /* Even though NetBSD/alpha used ELF since day one, it used the traditional a.out-style core dump format before NetBSD 1.6. */ /* Sizeof `struct reg' in . */ #define ALPHANBSD_SIZEOF_GREGS (32 * 8) /* Sizeof `struct fpreg' in = ALPHANBSD_SIZEOF_FPREGS); for (i = ALPHA_FP0_REGNUM; i < ALPHA_FP0_REGNUM + 31; i++) { if (regnum == i || regnum == -1) regcache_raw_supply (regcache, i, regs + (i - ALPHA_FP0_REGNUM) * 8); } if (regnum == ALPHA_FPCR_REGNUM || regnum == -1) regcache_raw_supply (regcache, ALPHA_FPCR_REGNUM, regs + 32 * 8); } /* Supply register REGNUM from the buffer specified by GREGS and LEN in the general-purpose register set REGSET to register cache REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */ static void alphanbsd_supply_gregset (const struct regset *regset, struct regcache *regcache, int regnum, const void *gregs, size_t len) { const gdb_byte *regs = gregs; int i; gdb_assert (len >= ALPHANBSD_SIZEOF_GREGS); for (i = 0; i < ALPHA_ZERO_REGNUM; i++) { if (regnum == i || regnum == -1) regcache_raw_supply (regcache, i, regs + i * 8); } if (regnum == ALPHA_PC_REGNUM || regnum == -1) regcache_raw_supply (regcache, ALPHA_PC_REGNUM, regs + 31 * 8); } /* Supply register REGNUM from the buffer specified by GREGS and LEN in the general-purpose register set REGSET to register cache REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */ static void alphanbsd_aout_supply_gregset (const struct regset *regset, struct regcache *regcache, int regnum, const void *gregs, size_t len) { const gdb_byte *regs = gregs; int i; /* Table to map a GDB register number to a trapframe register index. */ static const int regmap[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 30, 31, 32, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 29, 26 }; gdb_assert (len >= ALPHANBSD_SIZEOF_GREGS); for (i = 0; i < ARRAY_SIZE(regmap); i++) { if (regnum == i || regnum == -1) regcache_raw_supply (regcache, i, regs + regmap[i] * 8); } if (regnum == ALPHA_PC_REGNUM || regnum == -1) regcache_raw_supply (regcache, ALPHA_PC_REGNUM, regs + 31 * 8); if (len >= ALPHANBSD_SIZEOF_GREGS + ALPHANBSD_SIZEOF_FPREGS) { regs += ALPHANBSD_SIZEOF_GREGS; len -= ALPHANBSD_SIZEOF_GREGS; alphanbsd_supply_fpregset (regset, regcache, regnum, regs, len); } } /* NetBSD/alpha register sets. */ static const struct regset alphanbsd_gregset = { NULL, alphanbsd_supply_gregset }; static const struct regset alphanbsd_fpregset = { NULL, alphanbsd_supply_fpregset }; static const struct regset alphanbsd_aout_gregset = { NULL, alphanbsd_aout_supply_gregset }; /* Return the appropriate register set for the core section identified by SECT_NAME and SECT_SIZE. */ const struct regset * alphanbsd_regset_from_core_section (struct gdbarch *gdbarch, const char *sect_name, size_t sect_size) { if (strcmp (sect_name, ".reg") == 0 && sect_size >= ALPHANBSD_SIZEOF_GREGS) { if (sect_size >= ALPHANBSD_SIZEOF_GREGS + ALPHANBSD_SIZEOF_FPREGS) return &alphanbsd_aout_gregset; else return &alphanbsd_gregset; } if (strcmp (sect_name, ".reg2") == 0 && sect_size >= ALPHANBSD_SIZEOF_FPREGS) return &alphanbsd_fpregset; return NULL; } /* Signal trampolines. */ /* Under NetBSD/alpha, 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: ldq a0, 0(sp) lda sp, 16(sp) lda v0, 295(zero) # __sigreturn14 call_pal callsys 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. */ static const gdb_byte sigtramp_retcode[] = { 0x00, 0x00, 0x1e, 0xa6, /* ldq a0, 0(sp) */ 0x10, 0x00, 0xde, 0x23, /* lda sp, 16(sp) */ 0x27, 0x01, 0x1f, 0x20, /* lda v0, 295(zero) */ 0x83, 0x00, 0x00, 0x00, /* call_pal callsys */ }; #define RETCODE_NWORDS 4 #define RETCODE_SIZE (RETCODE_NWORDS * 4) static LONGEST alphanbsd_sigtramp_offset (struct gdbarch *gdbarch, CORE_ADDR pc) { gdb_byte ret[RETCODE_SIZE], w[4]; LONGEST off; int i; if (target_read_memory (pc, w, 4) != 0) return -1; for (i = 0; i < RETCODE_NWORDS; i++) { if (memcmp (w, sigtramp_retcode + (i * 4), 4) == 0) break; } if (i == RETCODE_NWORDS) return (-1); off = i * 4; pc -= off; if (target_read_memory (pc, ret, sizeof (ret)) != 0) return -1; if (memcmp (ret, sigtramp_retcode, RETCODE_SIZE) == 0) return off; return -1; } static int alphanbsd_pc_in_sigtramp (struct gdbarch *gdbarch, CORE_ADDR pc, const char *func_name) { return (nbsd_pc_in_sigtramp (pc, func_name) || alphanbsd_sigtramp_offset (gdbarch, pc) >= 0); } static CORE_ADDR alphanbsd_sigcontext_addr (struct frame_info *frame) { /* FIXME: This is not correct for all versions of NetBSD/alpha. We will probably need to disassemble the trampoline to figure out which trampoline frame type we have. */ if (!get_next_frame (frame)) return 0; return get_frame_base (get_next_frame (frame)); } static void alphanbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch); /* Hook into the DWARF CFI frame unwinder. */ alpha_dwarf2_init_abi (info, gdbarch); /* Hook into the MDEBUG frame unwinder. */ alpha_mdebug_init_abi (info, gdbarch); /* NetBSD/alpha does not provide single step support via ptrace(2); we must use software single-stepping. */ set_gdbarch_software_single_step (gdbarch, alpha_software_single_step); /* NetBSD/alpha has SVR4-style shared libraries. */ set_solib_svr4_fetch_link_map_offsets (gdbarch, svr4_lp64_fetch_link_map_offsets); tdep->dynamic_sigtramp_offset = alphanbsd_sigtramp_offset; tdep->pc_in_sigtramp = alphanbsd_pc_in_sigtramp; tdep->sigcontext_addr = alphanbsd_sigcontext_addr; tdep->jb_pc = 2; tdep->jb_elt_size = 8; set_gdbarch_regset_from_core_section (gdbarch, alphanbsd_regset_from_core_section); } static enum gdb_osabi alphanbsd_core_osabi_sniffer (bfd *abfd) { if (strcmp (bfd_get_target (abfd), "netbsd-core") == 0) return GDB_OSABI_NETBSD_ELF; return GDB_OSABI_UNKNOWN; } /* Provide a prototype to silence -Wmissing-prototypes. */ void _initialize_alphanbsd_tdep (void); void _initialize_alphanbsd_tdep (void) { /* BFD doesn't set a flavour for NetBSD style a.out core files. */ gdbarch_register_osabi_sniffer (bfd_arch_alpha, bfd_target_unknown_flavour, alphanbsd_core_osabi_sniffer); gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_NETBSD_ELF, alphanbsd_init_abi); }