xref: /netbsd-src/external/gpl3/gdb.old/dist/gdb/mips-fbsd-tdep.c (revision cef8759bd76c1b621f8eab8faa6f208faabc2e15)
1 /* Target-dependent code for FreeBSD/mips.
2 
3    Copyright (C) 2017 Free Software Foundation, Inc.
4 
5    This file is part of GDB.
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 3 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
19 
20 #include "defs.h"
21 #include "osabi.h"
22 #include "regset.h"
23 #include "trad-frame.h"
24 #include "tramp-frame.h"
25 
26 #include "fbsd-tdep.h"
27 #include "mips-tdep.h"
28 #include "mips-fbsd-tdep.h"
29 
30 #include "solib-svr4.h"
31 
32 /* Shorthand for some register numbers used below.  */
33 #define MIPS_PC_REGNUM  MIPS_EMBED_PC_REGNUM
34 #define MIPS_FP0_REGNUM MIPS_EMBED_FP0_REGNUM
35 #define MIPS_FSR_REGNUM MIPS_EMBED_FP0_REGNUM + 32
36 
37 /* Core file support. */
38 
39 /* Number of registers in `struct reg' from <machine/reg.h>.  The
40    first 38 follow the standard MIPS layout.  The 39th holds
41    IC_INT_REG on RM7K and RM9K processors.  The 40th is a dummy for
42    padding.  */
43 #define MIPS_FBSD_NUM_GREGS	40
44 
45 /* Number of registers in `struct fpreg' from <machine/reg.h>.  The
46    first 32 hold floating point registers.  33 holds the FSR.  The
47    34th is a dummy for padding.  */
48 #define MIPS_FBSD_NUM_FPREGS	34
49 
50 /* Supply a single register.  If the source register size matches the
51    size the regcache expects, this can use regcache_raw_supply().  If
52    they are different, this copies the source register into a buffer
53    that can be passed to regcache_raw_supply().  */
54 
55 static void
56 mips_fbsd_supply_reg (struct regcache *regcache, int regnum, const void *addr,
57 		      size_t len)
58 {
59   struct gdbarch *gdbarch = get_regcache_arch (regcache);
60 
61   if (register_size (gdbarch, regnum) == len)
62     regcache_raw_supply (regcache, regnum, addr);
63   else
64     {
65       enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
66       gdb_byte buf[MAX_REGISTER_SIZE];
67       LONGEST val;
68 
69       val = extract_signed_integer ((const gdb_byte *) addr, len, byte_order);
70       store_signed_integer (buf, register_size (gdbarch, regnum), byte_order,
71 			    val);
72       regcache_raw_supply (regcache, regnum, buf);
73     }
74 }
75 
76 /* Collect a single register.  If the destination register size
77    matches the size the regcache expects, this can use
78    regcache_raw_supply().  If they are different, this fetches the
79    register via regcache_raw_supply() into a buffer and then copies it
80    into the final destination.  */
81 
82 static void
83 mips_fbsd_collect_reg (const struct regcache *regcache, int regnum, void *addr,
84 		       size_t len)
85 {
86   struct gdbarch *gdbarch = get_regcache_arch (regcache);
87 
88   if (register_size (gdbarch, regnum) == len)
89     regcache_raw_collect (regcache, regnum, addr);
90   else
91     {
92       enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
93       gdb_byte buf[MAX_REGISTER_SIZE];
94       LONGEST val;
95 
96       regcache_raw_collect (regcache, regnum, buf);
97       val = extract_signed_integer (buf, register_size (gdbarch, regnum),
98 				    byte_order);
99       store_signed_integer ((gdb_byte *) addr, len, byte_order, val);
100     }
101 }
102 
103 /* Supply the floating-point registers stored in FPREGS to REGCACHE.
104    Each floating-point register in FPREGS is REGSIZE bytes in
105    length.  */
106 
107 void
108 mips_fbsd_supply_fpregs (struct regcache *regcache, int regnum,
109 			 const void *fpregs, size_t regsize)
110 {
111   const gdb_byte *regs = (const gdb_byte *) fpregs;
112   int i;
113 
114   for (i = MIPS_FP0_REGNUM; i <= MIPS_FSR_REGNUM; i++)
115     if (regnum == i || regnum == -1)
116       mips_fbsd_supply_reg (regcache, i,
117 			    regs + (i - MIPS_FP0_REGNUM) * regsize, regsize);
118 }
119 
120 /* Supply the general-purpose registers stored in GREGS to REGCACHE.
121    Each general-purpose register in GREGS is REGSIZE bytes in
122    length.  */
123 
124 void
125 mips_fbsd_supply_gregs (struct regcache *regcache, int regnum,
126 			const void *gregs, size_t regsize)
127 {
128   const gdb_byte *regs = (const gdb_byte *) gregs;
129   int i;
130 
131   for (i = 0; i <= MIPS_PC_REGNUM; i++)
132     if (regnum == i || regnum == -1)
133       mips_fbsd_supply_reg (regcache, i, regs + i * regsize, regsize);
134 }
135 
136 /* Collect the floating-point registers from REGCACHE and store them
137    in FPREGS.  Each floating-point register in FPREGS is REGSIZE bytes
138    in length.  */
139 
140 void
141 mips_fbsd_collect_fpregs (const struct regcache *regcache, int regnum,
142 			  void *fpregs, size_t regsize)
143 {
144   gdb_byte *regs = (gdb_byte *) fpregs;
145   int i;
146 
147   for (i = MIPS_FP0_REGNUM; i <= MIPS_FSR_REGNUM; i++)
148     if (regnum == i || regnum == -1)
149       mips_fbsd_collect_reg (regcache, i,
150 			     regs + (i - MIPS_FP0_REGNUM) * regsize, regsize);
151 }
152 
153 /* Collect the general-purpose registers from REGCACHE and store them
154    in GREGS.  Each general-purpose register in GREGS is REGSIZE bytes
155    in length.  */
156 
157 void
158 mips_fbsd_collect_gregs (const struct regcache *regcache, int regnum,
159 			 void *gregs, size_t regsize)
160 {
161   gdb_byte *regs = (gdb_byte *) gregs;
162   int i;
163 
164   for (i = 0; i <= MIPS_PC_REGNUM; i++)
165     if (regnum == i || regnum == -1)
166       mips_fbsd_collect_reg (regcache, i, regs + i * regsize, regsize);
167 }
168 
169 /* Supply register REGNUM from the buffer specified by FPREGS and LEN
170    in the floating-point register set REGSET to register cache
171    REGCACHE.  If REGNUM is -1, do this for all registers in REGSET.  */
172 
173 static void
174 mips_fbsd_supply_fpregset (const struct regset *regset,
175 			   struct regcache *regcache,
176 			   int regnum, const void *fpregs, size_t len)
177 {
178   size_t regsize = mips_abi_regsize (get_regcache_arch (regcache));
179 
180   gdb_assert (len >= MIPS_FBSD_NUM_FPREGS * regsize);
181 
182   mips_fbsd_supply_fpregs (regcache, regnum, fpregs, regsize);
183 }
184 
185 /* Collect register REGNUM from the register cache REGCACHE and store
186    it in the buffer specified by FPREGS and LEN in the floating-point
187    register set REGSET.  If REGNUM is -1, do this for all registers in
188    REGSET.  */
189 
190 static void
191 mips_fbsd_collect_fpregset (const struct regset *regset,
192 			    const struct regcache *regcache,
193 			    int regnum, void *fpregs, size_t len)
194 {
195   size_t regsize = mips_abi_regsize (get_regcache_arch (regcache));
196 
197   gdb_assert (len >= MIPS_FBSD_NUM_FPREGS * regsize);
198 
199   mips_fbsd_collect_fpregs (regcache, regnum, fpregs, regsize);
200 }
201 
202 /* Supply register REGNUM from the buffer specified by GREGS and LEN
203    in the general-purpose register set REGSET to register cache
204    REGCACHE.  If REGNUM is -1, do this for all registers in REGSET.  */
205 
206 static void
207 mips_fbsd_supply_gregset (const struct regset *regset,
208 			  struct regcache *regcache, int regnum,
209 			  const void *gregs, size_t len)
210 {
211   size_t regsize = mips_abi_regsize (get_regcache_arch (regcache));
212 
213   gdb_assert (len >= MIPS_FBSD_NUM_GREGS * regsize);
214 
215   mips_fbsd_supply_gregs (regcache, regnum, gregs, regsize);
216 }
217 
218 /* Collect register REGNUM from the register cache REGCACHE and store
219    it in the buffer specified by GREGS and LEN in the general-purpose
220    register set REGSET.  If REGNUM is -1, do this for all registers in
221    REGSET.  */
222 
223 static void
224 mips_fbsd_collect_gregset (const struct regset *regset,
225 			   const struct regcache *regcache,
226 			   int regnum, void *gregs, size_t len)
227 {
228   size_t regsize = mips_abi_regsize (get_regcache_arch (regcache));
229 
230   gdb_assert (len >= MIPS_FBSD_NUM_GREGS * regsize);
231 
232   mips_fbsd_collect_gregs (regcache, regnum, gregs, regsize);
233 }
234 
235 /* FreeBSD/mips register sets.  */
236 
237 static const struct regset mips_fbsd_gregset =
238 {
239   NULL,
240   mips_fbsd_supply_gregset,
241   mips_fbsd_collect_gregset,
242 };
243 
244 static const struct regset mips_fbsd_fpregset =
245 {
246   NULL,
247   mips_fbsd_supply_fpregset,
248   mips_fbsd_collect_fpregset,
249 };
250 
251 /* Iterate over core file register note sections.  */
252 
253 static void
254 mips_fbsd_iterate_over_regset_sections (struct gdbarch *gdbarch,
255 					iterate_over_regset_sections_cb *cb,
256 					void *cb_data,
257 					const struct regcache *regcache)
258 {
259   size_t regsize = mips_abi_regsize (gdbarch);
260 
261   cb (".reg", MIPS_FBSD_NUM_GREGS * regsize, &mips_fbsd_gregset,
262       NULL, cb_data);
263   cb (".reg2", MIPS_FBSD_NUM_FPREGS * regsize, &mips_fbsd_fpregset,
264       NULL, cb_data);
265 }
266 
267 /* Signal trampoline support.  */
268 
269 #define FBSD_SYS_sigreturn	417
270 
271 #define MIPS_INST_LI_V0_SIGRETURN 0x24020000 + FBSD_SYS_sigreturn
272 #define MIPS_INST_SYSCALL	0x0000000c
273 #define MIPS_INST_BREAK		0x0000000d
274 
275 #define O32_SIGFRAME_UCONTEXT_OFFSET	(16)
276 #define O32_SIGSET_T_SIZE	(16)
277 
278 #define O32_UCONTEXT_ONSTACK	(O32_SIGSET_T_SIZE)
279 #define O32_UCONTEXT_PC		(O32_UCONTEXT_ONSTACK + 4)
280 #define O32_UCONTEXT_REGS	(O32_UCONTEXT_PC + 4)
281 #define O32_UCONTEXT_SR		(O32_UCONTEXT_REGS + 4 * 32)
282 #define O32_UCONTEXT_LO		(O32_UCONTEXT_SR + 4)
283 #define O32_UCONTEXT_HI		(O32_UCONTEXT_LO + 4)
284 #define O32_UCONTEXT_FPUSED	(O32_UCONTEXT_HI + 4)
285 #define O32_UCONTEXT_FPREGS	(O32_UCONTEXT_FPUSED + 4)
286 
287 #define O32_UCONTEXT_REG_SIZE	4
288 
289 static void
290 mips_fbsd_sigframe_init (const struct tramp_frame *self,
291 			 struct frame_info *this_frame,
292 			 struct trad_frame_cache *cache,
293 			 CORE_ADDR func)
294 {
295   struct gdbarch *gdbarch = get_frame_arch (this_frame);
296   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
297   CORE_ADDR sp, ucontext_addr, addr;
298   int regnum;
299   gdb_byte buf[4];
300 
301   /* We find the appropriate instance of `ucontext_t' at a
302      fixed offset in the signal frame.  */
303   sp = get_frame_register_signed (this_frame,
304 				  MIPS_SP_REGNUM + gdbarch_num_regs (gdbarch));
305   ucontext_addr = sp + O32_SIGFRAME_UCONTEXT_OFFSET;
306 
307   /* PC.  */
308   regnum = mips_regnum (gdbarch)->pc;
309   trad_frame_set_reg_addr (cache,
310 			   regnum + gdbarch_num_regs (gdbarch),
311 			   ucontext_addr + O32_UCONTEXT_PC);
312 
313   /* GPRs.  */
314   for (regnum = MIPS_ZERO_REGNUM, addr = ucontext_addr + O32_UCONTEXT_REGS;
315        regnum <= MIPS_RA_REGNUM; regnum++, addr += O32_UCONTEXT_REG_SIZE)
316     trad_frame_set_reg_addr (cache,
317 			     regnum + gdbarch_num_regs (gdbarch),
318 			     addr);
319 
320   regnum = MIPS_PS_REGNUM;
321   trad_frame_set_reg_addr (cache,
322 			   regnum + gdbarch_num_regs (gdbarch),
323 			   ucontext_addr + O32_UCONTEXT_SR);
324 
325   /* HI and LO.  */
326   regnum = mips_regnum (gdbarch)->lo;
327   trad_frame_set_reg_addr (cache,
328 			   regnum + gdbarch_num_regs (gdbarch),
329 			   ucontext_addr + O32_UCONTEXT_LO);
330   regnum = mips_regnum (gdbarch)->hi;
331   trad_frame_set_reg_addr (cache,
332 			   regnum + gdbarch_num_regs (gdbarch),
333 			   ucontext_addr + O32_UCONTEXT_HI);
334 
335   if (target_read_memory (ucontext_addr + O32_UCONTEXT_FPUSED, buf, 4) == 0
336       && extract_unsigned_integer (buf, 4, byte_order) != 0)
337     {
338       for (regnum = 0, addr = ucontext_addr + O32_UCONTEXT_FPREGS;
339 	   regnum < 32; regnum++, addr += O32_UCONTEXT_REG_SIZE)
340 	trad_frame_set_reg_addr (cache,
341 				 regnum + gdbarch_fp0_regnum (gdbarch),
342 				 addr);
343       trad_frame_set_reg_addr (cache, mips_regnum (gdbarch)->fp_control_status,
344 			       addr);
345     }
346 
347   trad_frame_set_id (cache, frame_id_build (sp, func));
348 }
349 
350 #define MIPS_INST_ADDIU_A0_SP_O32 (0x27a40000 \
351 				   + O32_SIGFRAME_UCONTEXT_OFFSET)
352 
353 static const struct tramp_frame mips_fbsd_sigframe =
354 {
355   SIGTRAMP_FRAME,
356   MIPS_INSN32_SIZE,
357   {
358     { MIPS_INST_ADDIU_A0_SP_O32, -1 },	/* addiu   a0, sp, SIGF_UC */
359     { MIPS_INST_LI_V0_SIGRETURN, -1 },	/* li      v0, SYS_sigreturn */
360     { MIPS_INST_SYSCALL, -1 },		/* syscall */
361     { MIPS_INST_BREAK, -1 },		/* break */
362     { TRAMP_SENTINEL_INSN, -1 }
363   },
364   mips_fbsd_sigframe_init
365 };
366 
367 #define N64_SIGFRAME_UCONTEXT_OFFSET	(32)
368 #define N64_SIGSET_T_SIZE	(16)
369 
370 #define N64_UCONTEXT_ONSTACK	(N64_SIGSET_T_SIZE)
371 #define N64_UCONTEXT_PC		(N64_UCONTEXT_ONSTACK + 8)
372 #define N64_UCONTEXT_REGS	(N64_UCONTEXT_PC + 8)
373 #define N64_UCONTEXT_SR		(N64_UCONTEXT_REGS + 8 * 32)
374 #define N64_UCONTEXT_LO		(N64_UCONTEXT_SR + 8)
375 #define N64_UCONTEXT_HI		(N64_UCONTEXT_LO + 8)
376 #define N64_UCONTEXT_FPUSED	(N64_UCONTEXT_HI + 8)
377 #define N64_UCONTEXT_FPREGS	(N64_UCONTEXT_FPUSED + 8)
378 
379 #define N64_UCONTEXT_REG_SIZE	8
380 
381 static void
382 mips64_fbsd_sigframe_init (const struct tramp_frame *self,
383 			   struct frame_info *this_frame,
384 			   struct trad_frame_cache *cache,
385 			   CORE_ADDR func)
386 {
387   struct gdbarch *gdbarch = get_frame_arch (this_frame);
388   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
389   CORE_ADDR sp, ucontext_addr, addr;
390   int regnum;
391   gdb_byte buf[4];
392 
393   /* We find the appropriate instance of `ucontext_t' at a
394      fixed offset in the signal frame.  */
395   sp = get_frame_register_signed (this_frame,
396 				  MIPS_SP_REGNUM + gdbarch_num_regs (gdbarch));
397   ucontext_addr = sp + N64_SIGFRAME_UCONTEXT_OFFSET;
398 
399   /* PC.  */
400   regnum = mips_regnum (gdbarch)->pc;
401   trad_frame_set_reg_addr (cache,
402 			   regnum + gdbarch_num_regs (gdbarch),
403 			   ucontext_addr + N64_UCONTEXT_PC);
404 
405   /* GPRs.  */
406   for (regnum = MIPS_ZERO_REGNUM, addr = ucontext_addr + N64_UCONTEXT_REGS;
407        regnum <= MIPS_RA_REGNUM; regnum++, addr += N64_UCONTEXT_REG_SIZE)
408     trad_frame_set_reg_addr (cache,
409 			     regnum + gdbarch_num_regs (gdbarch),
410 			     addr);
411 
412   regnum = MIPS_PS_REGNUM;
413   trad_frame_set_reg_addr (cache,
414 			   regnum + gdbarch_num_regs (gdbarch),
415 			   ucontext_addr + N64_UCONTEXT_SR);
416 
417   /* HI and LO.  */
418   regnum = mips_regnum (gdbarch)->lo;
419   trad_frame_set_reg_addr (cache,
420 			   regnum + gdbarch_num_regs (gdbarch),
421 			   ucontext_addr + N64_UCONTEXT_LO);
422   regnum = mips_regnum (gdbarch)->hi;
423   trad_frame_set_reg_addr (cache,
424 			   regnum + gdbarch_num_regs (gdbarch),
425 			   ucontext_addr + N64_UCONTEXT_HI);
426 
427   if (target_read_memory (ucontext_addr + N64_UCONTEXT_FPUSED, buf, 4) == 0
428       && extract_unsigned_integer (buf, 4, byte_order) != 0)
429     {
430       for (regnum = 0, addr = ucontext_addr + N64_UCONTEXT_FPREGS;
431 	   regnum < 32; regnum++, addr += N64_UCONTEXT_REG_SIZE)
432 	trad_frame_set_reg_addr (cache,
433 				 regnum + gdbarch_fp0_regnum (gdbarch),
434 				 addr);
435       trad_frame_set_reg_addr (cache, mips_regnum (gdbarch)->fp_control_status,
436 			       addr);
437     }
438 
439   trad_frame_set_id (cache, frame_id_build (sp, func));
440 }
441 
442 #define MIPS_INST_DADDIU_A0_SP_N64 (0x67a40000 \
443 				    + N64_SIGFRAME_UCONTEXT_OFFSET)
444 
445 static const struct tramp_frame mips64_fbsd_sigframe =
446 {
447   SIGTRAMP_FRAME,
448   MIPS_INSN32_SIZE,
449   {
450     { MIPS_INST_DADDIU_A0_SP_N64, -1 },	/* daddiu  a0, sp, SIGF_UC */
451     { MIPS_INST_LI_V0_SIGRETURN, -1 },	/* li      v0, SYS_sigreturn */
452     { MIPS_INST_SYSCALL, -1 },		/* syscall */
453     { MIPS_INST_BREAK, -1 },		/* break */
454     { TRAMP_SENTINEL_INSN, -1 }
455   },
456   mips64_fbsd_sigframe_init
457 };
458 
459 /* Shared library support.  */
460 
461 /* FreeBSD/mips uses a slightly different `struct link_map' than the
462    other FreeBSD platforms as it includes an additional `l_off'
463    member.  */
464 
465 static struct link_map_offsets *
466 mips_fbsd_ilp32_fetch_link_map_offsets (void)
467 {
468   static struct link_map_offsets lmo;
469   static struct link_map_offsets *lmp = NULL;
470 
471   if (lmp == NULL)
472     {
473       lmp = &lmo;
474 
475       lmo.r_version_offset = 0;
476       lmo.r_version_size = 4;
477       lmo.r_map_offset = 4;
478       lmo.r_brk_offset = 8;
479       lmo.r_ldsomap_offset = -1;
480 
481       lmo.link_map_size = 24;
482       lmo.l_addr_offset = 0;
483       lmo.l_name_offset = 8;
484       lmo.l_ld_offset = 12;
485       lmo.l_next_offset = 16;
486       lmo.l_prev_offset = 20;
487     }
488 
489   return lmp;
490 }
491 
492 static struct link_map_offsets *
493 mips_fbsd_lp64_fetch_link_map_offsets (void)
494 {
495   static struct link_map_offsets lmo;
496   static struct link_map_offsets *lmp = NULL;
497 
498   if (lmp == NULL)
499     {
500       lmp = &lmo;
501 
502       lmo.r_version_offset = 0;
503       lmo.r_version_size = 4;
504       lmo.r_map_offset = 8;
505       lmo.r_brk_offset = 16;
506       lmo.r_ldsomap_offset = -1;
507 
508       lmo.link_map_size = 48;
509       lmo.l_addr_offset = 0;
510       lmo.l_name_offset = 16;
511       lmo.l_ld_offset = 24;
512       lmo.l_next_offset = 32;
513       lmo.l_prev_offset = 40;
514     }
515 
516   return lmp;
517 }
518 
519 static void
520 mips_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
521 {
522   enum mips_abi abi = mips_abi (gdbarch);
523 
524   /* Generic FreeBSD support.  */
525   fbsd_init_abi (info, gdbarch);
526 
527   set_gdbarch_software_single_step (gdbarch, mips_software_single_step);
528 
529   switch (abi)
530     {
531       case MIPS_ABI_O32:
532 	tramp_frame_prepend_unwinder (gdbarch, &mips_fbsd_sigframe);
533 	break;
534       case MIPS_ABI_N32:
535 	break;
536       case MIPS_ABI_N64:
537 	tramp_frame_prepend_unwinder (gdbarch, &mips64_fbsd_sigframe);
538 	break;
539     }
540 
541   set_gdbarch_iterate_over_regset_sections
542     (gdbarch, mips_fbsd_iterate_over_regset_sections);
543 
544   /* FreeBSD/mips has SVR4-style shared libraries.  */
545   set_solib_svr4_fetch_link_map_offsets
546     (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32 ?
547 	       mips_fbsd_ilp32_fetch_link_map_offsets :
548 	       mips_fbsd_lp64_fetch_link_map_offsets));
549 }
550 
551 
552 /* Provide a prototype to silence -Wmissing-prototypes.  */
553 void _initialize_mips_fbsd_tdep (void);
554 
555 void
556 _initialize_mips_fbsd_tdep (void)
557 {
558   gdbarch_register_osabi (bfd_arch_mips, 0, GDB_OSABI_FREEBSD,
559 			  mips_fbsd_init_abi);
560 }
561