xref: /netbsd-src/external/gpl3/gdb/dist/gdb/alpha-netbsd-tdep.c (revision bfd24476eb99d97ebf0497e68104e1224ffcb2d1)
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