Home | History | Annotate | Line # | Download | only in gdb
      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 
    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 (const 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 
    250 static void
    251 alphanbsd_init_abi (struct gdbarch_info info,
    252 		    struct gdbarch *gdbarch)
    253 {
    254   alpha_gdbarch_tdep *tdep = gdbarch_tdep<alpha_gdbarch_tdep> (gdbarch);
    255 
    256   /* Hook into the DWARF CFI frame unwinder.  */
    257   alpha_dwarf2_init_abi (info, gdbarch);
    258 
    259   /* Hook into the MDEBUG frame unwinder.  */
    260   alpha_mdebug_init_abi (info, gdbarch);
    261 
    262   nbsd_init_abi (info, gdbarch);
    263 
    264   /* NetBSD/alpha does not provide single step support via ptrace(2); we
    265      must use software single-stepping.  */
    266   set_gdbarch_software_single_step (gdbarch, alpha_software_single_step);
    267 
    268   /* NetBSD/alpha has SVR4-style shared libraries.  */
    269   set_solib_svr4_fetch_link_map_offsets
    270     (gdbarch, svr4_lp64_fetch_link_map_offsets);
    271 
    272   tdep->dynamic_sigtramp_offset = alphanbsd_sigtramp_offset;
    273   tdep->pc_in_sigtramp = alphanbsd_pc_in_sigtramp;
    274   tdep->sigcontext_addr = alphanbsd_sigcontext_addr;
    275 
    276   tdep->jb_pc = 2;
    277   tdep->jb_elt_size = 8;
    278 
    279   set_gdbarch_iterate_over_regset_sections
    280     (gdbarch, alphanbsd_iterate_over_regset_sections);
    281 }
    282 
    283 
    285 void _initialize_alphanbsd_tdep ();
    286 void
    287 _initialize_alphanbsd_tdep ()
    288 {
    289   /* Even though NetBSD/alpha used ELF since day one, it used the
    290      traditional a.out-style core dump format before NetBSD 1.6, but
    291      we don't support those.  */
    292   gdbarch_register_osabi (bfd_arch_alpha, 0, GDB_OSABI_NETBSD,
    293 			  alphanbsd_init_abi);
    294 }
    295