1 /* Target-dependent code for NetBSD/alpha. 2 3 Copyright (C) 2002-2023 Free Software Foundation, Inc. 4 5 Contributed by Wasabi Systems, 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 3 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, see <http://www.gnu.org/licenses/>. */ 21 22 #include "defs.h" 23 #include "frame.h" 24 #include "gdbcore.h" 25 #include "osabi.h" 26 #include "regcache.h" 27 #include "regset.h" 28 #include "value.h" 29 30 #include "alpha-tdep.h" 31 #include "alpha-bsd-tdep.h" 32 #include "netbsd-tdep.h" 33 #include "solib-svr4.h" 34 #include "target.h" 35 36 /* Core file support. */ 37 38 /* Sizeof `struct reg' in <machine/reg.h>. */ 39 #define ALPHANBSD_SIZEOF_GREGS (32 * 8) 40 41 /* Sizeof `struct fpreg' in <machine/reg.h. */ 42 #define ALPHANBSD_SIZEOF_FPREGS ((32 * 8) + 8) 43 44 /* Supply register REGNUM from the buffer specified by FPREGS and LEN 45 in the floating-point register set REGSET to register cache 46 REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */ 47 48 static void 49 alphanbsd_supply_fpregset (const struct regset *regset, 50 struct regcache *regcache, 51 int regnum, const void *fpregs, size_t len) 52 { 53 const gdb_byte *regs = (const gdb_byte *) fpregs; 54 int i; 55 56 gdb_assert (len >= ALPHANBSD_SIZEOF_FPREGS); 57 58 for (i = ALPHA_FP0_REGNUM; i < ALPHA_FP0_REGNUM + 31; i++) 59 { 60 if (regnum == i || regnum == -1) 61 regcache->raw_supply (i, regs + (i - ALPHA_FP0_REGNUM) * 8); 62 } 63 64 if (regnum == ALPHA_FPCR_REGNUM || regnum == -1) 65 regcache->raw_supply (ALPHA_FPCR_REGNUM, regs + 32 * 8); 66 } 67 68 /* Supply register REGNUM from the buffer specified by GREGS and LEN 69 in the general-purpose register set REGSET to register cache 70 REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */ 71 72 static void 73 alphanbsd_aout_supply_gregset (const struct regset *regset, 74 struct regcache *regcache, 75 int regnum, const void *gregs, size_t len) 76 { 77 const gdb_byte *regs = (const gdb_byte *) gregs; 78 int i; 79 80 /* Table to map a GDB register number to a trapframe register index. */ 81 static const int regmap[] = 82 { 83 0, 1, 2, 3, 84 4, 5, 6, 7, 85 8, 9, 10, 11, 86 12, 13, 14, 15, 87 30, 31, 32, 16, 88 17, 18, 19, 20, 89 21, 22, 23, 24, 90 25, 29, 26 91 }; 92 93 gdb_assert (len >= ALPHANBSD_SIZEOF_GREGS); 94 95 for (i = 0; i < ARRAY_SIZE(regmap); i++) 96 { 97 if (regnum == i || regnum == -1) 98 regcache->raw_supply (i, regs + regmap[i] * 8); 99 } 100 101 if (regnum == ALPHA_PC_REGNUM || regnum == -1) 102 regcache->raw_supply (ALPHA_PC_REGNUM, regs + 31 * 8); 103 104 if (len >= ALPHANBSD_SIZEOF_GREGS + ALPHANBSD_SIZEOF_FPREGS) 105 { 106 regs += ALPHANBSD_SIZEOF_GREGS; 107 len -= ALPHANBSD_SIZEOF_GREGS; 108 alphanbsd_supply_fpregset (regset, regcache, regnum, regs, len); 109 } 110 } 111 112 /* Supply register REGNUM from the buffer specified by GREGS and LEN 113 in the general-purpose register set REGSET to register cache 114 REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */ 115 116 static void 117 alphanbsd_supply_gregset (const struct regset *regset, 118 struct regcache *regcache, 119 int regnum, const void *gregs, size_t len) 120 { 121 const gdb_byte *regs = (const gdb_byte *) gregs; 122 int i; 123 124 if (len >= ALPHANBSD_SIZEOF_GREGS + ALPHANBSD_SIZEOF_FPREGS) 125 { 126 alphanbsd_aout_supply_gregset (regset, regcache, regnum, gregs, len); 127 return; 128 } 129 130 for (i = 0; i < ALPHA_ZERO_REGNUM; i++) 131 { 132 if (regnum == i || regnum == -1) 133 regcache->raw_supply (i, regs + i * 8); 134 } 135 136 if (regnum == ALPHA_PC_REGNUM || regnum == -1) 137 regcache->raw_supply (ALPHA_PC_REGNUM, regs + 31 * 8); 138 } 139 140 /* NetBSD/alpha register sets. */ 141 142 static const struct regset alphanbsd_gregset = 143 { 144 NULL, 145 alphanbsd_supply_gregset, 146 NULL, 147 REGSET_VARIABLE_SIZE 148 }; 149 150 static const struct regset alphanbsd_fpregset = 151 { 152 NULL, 153 alphanbsd_supply_fpregset 154 }; 155 156 /* Iterate over supported core file register note sections. */ 157 158 void 159 alphanbsd_iterate_over_regset_sections (struct gdbarch *gdbarch, 160 iterate_over_regset_sections_cb *cb, 161 void *cb_data, 162 const struct regcache *regcache) 163 { 164 cb (".reg", ALPHANBSD_SIZEOF_GREGS, ALPHANBSD_SIZEOF_GREGS, 165 &alphanbsd_gregset, NULL, cb_data); 166 cb (".reg2", ALPHANBSD_SIZEOF_FPREGS, ALPHANBSD_SIZEOF_FPREGS, 167 &alphanbsd_fpregset, NULL, cb_data); 168 } 169 170 171 /* Signal trampolines. */ 172 173 /* Under NetBSD/alpha, signal handler invocations can be identified by the 174 designated code sequence that is used to return from a signal handler. 175 In particular, the return address of a signal handler points to the 176 following code sequence: 177 178 ldq a0, 0(sp) 179 lda sp, 16(sp) 180 lda v0, 295(zero) # __sigreturn14 181 call_pal callsys 182 183 Each instruction has a unique encoding, so we simply attempt to match 184 the instruction the PC is pointing to with any of the above instructions. 185 If there is a hit, we know the offset to the start of the designated 186 sequence and can then check whether we really are executing in the 187 signal trampoline. If not, -1 is returned, otherwise the offset from the 188 start of the return sequence is returned. */ 189 static const gdb_byte sigtramp_retcode[] = 190 { 191 0x00, 0x00, 0x1e, 0xa6, /* ldq a0, 0(sp) */ 192 0x10, 0x00, 0xde, 0x23, /* lda sp, 16(sp) */ 193 0x27, 0x01, 0x1f, 0x20, /* lda v0, 295(zero) */ 194 0x83, 0x00, 0x00, 0x00, /* call_pal callsys */ 195 }; 196 #define RETCODE_NWORDS 4 197 #define RETCODE_SIZE (RETCODE_NWORDS * 4) 198 199 static LONGEST 200 alphanbsd_sigtramp_offset (struct gdbarch *gdbarch, CORE_ADDR pc) 201 { 202 gdb_byte ret[RETCODE_SIZE], w[4]; 203 LONGEST off; 204 int i; 205 206 if (target_read_memory (pc, w, 4) != 0) 207 return -1; 208 209 for (i = 0; i < RETCODE_NWORDS; i++) 210 { 211 if (memcmp (w, sigtramp_retcode + (i * 4), 4) == 0) 212 break; 213 } 214 if (i == RETCODE_NWORDS) 215 return (-1); 216 217 off = i * 4; 218 pc -= off; 219 220 if (target_read_memory (pc, ret, sizeof (ret)) != 0) 221 return -1; 222 223 if (memcmp (ret, sigtramp_retcode, RETCODE_SIZE) == 0) 224 return off; 225 226 return -1; 227 } 228 229 static int 230 alphanbsd_pc_in_sigtramp (struct gdbarch *gdbarch, 231 CORE_ADDR pc, const char *func_name) 232 { 233 return (nbsd_pc_in_sigtramp (pc, func_name) 234 || alphanbsd_sigtramp_offset (gdbarch, pc) >= 0); 235 } 236 237 static CORE_ADDR 238 alphanbsd_sigcontext_addr (frame_info_ptr frame) 239 { 240 /* FIXME: This is not correct for all versions of NetBSD/alpha. 241 We will probably need to disassemble the trampoline to figure 242 out which trampoline frame type we have. */ 243 if (!get_next_frame (frame)) 244 return 0; 245 return get_frame_base (get_next_frame (frame)); 246 } 247 248 249 static void 250 alphanbsd_init_abi (struct gdbarch_info info, 251 struct gdbarch *gdbarch) 252 { 253 alpha_gdbarch_tdep *tdep = gdbarch_tdep<alpha_gdbarch_tdep> (gdbarch); 254 255 /* Hook into the DWARF CFI frame unwinder. */ 256 alpha_dwarf2_init_abi (info, gdbarch); 257 258 /* Hook into the MDEBUG frame unwinder. */ 259 alpha_mdebug_init_abi (info, gdbarch); 260 261 nbsd_init_abi (info, gdbarch); 262 263 /* NetBSD/alpha does not provide single step support via ptrace(2); we 264 must use software single-stepping. */ 265 set_gdbarch_software_single_step (gdbarch, alpha_software_single_step); 266 267 /* NetBSD/alpha has SVR4-style shared libraries. */ 268 set_solib_svr4_fetch_link_map_offsets 269 (gdbarch, svr4_lp64_fetch_link_map_offsets); 270 271 tdep->dynamic_sigtramp_offset = alphanbsd_sigtramp_offset; 272 tdep->pc_in_sigtramp = alphanbsd_pc_in_sigtramp; 273 tdep->sigcontext_addr = alphanbsd_sigcontext_addr; 274 275 tdep->jb_pc = 2; 276 tdep->jb_elt_size = 8; 277 278 set_gdbarch_iterate_over_regset_sections 279 (gdbarch, alphanbsd_iterate_over_regset_sections); 280 } 281 282 283 void _initialize_alphanbsd_tdep (); 284 void 285 _initialize_alphanbsd_tdep () 286 { 287 /* Even though NetBSD/alpha used ELF since day one, it used the 288 traditional a.out-style core dump format before NetBSD 1.6, but 289 we don't support those. */ 290 gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_NETBSD, 291 alphanbsd_init_abi); 292 } 293