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