aboutsummaryrefslogtreecommitdiff
path: root/gdb/mips-nbsd-tdep.c
diff options
context:
space:
mode:
Diffstat (limited to 'gdb/mips-nbsd-tdep.c')
-rw-r--r--gdb/mips-nbsd-tdep.c385
1 files changed, 385 insertions, 0 deletions
diff --git a/gdb/mips-nbsd-tdep.c b/gdb/mips-nbsd-tdep.c
new file mode 100644
index 0000000..2d7cd82
--- /dev/null
+++ b/gdb/mips-nbsd-tdep.c
@@ -0,0 +1,385 @@
+/* Target-dependent code for NetBSD/mips.
+
+ Copyright (C) 2002-2016 Free Software Foundation, Inc.
+
+ Contributed by Wasabi Systems, Inc.
+
+ This file is part of GDB.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
+
+#include "defs.h"
+#include "gdbcore.h"
+#include "regcache.h"
+#include "regset.h"
+#include "target.h"
+#include "value.h"
+#include "osabi.h"
+
+#include "nbsd-tdep.h"
+#include "mips-nbsd-tdep.h"
+#include "mips-tdep.h"
+
+#include "solib-svr4.h"
+
+/* Shorthand for some register numbers used below. */
+#define MIPS_PC_REGNUM MIPS_EMBED_PC_REGNUM
+#define MIPS_FP0_REGNUM MIPS_EMBED_FP0_REGNUM
+#define MIPS_FSR_REGNUM MIPS_EMBED_FP0_REGNUM + 32
+
+/* Core file support. */
+
+/* Number of registers in `struct reg' from <machine/reg.h>. */
+#define MIPSNBSD_NUM_GREGS 38
+
+/* Number of registers in `struct fpreg' from <machine/reg.h>. */
+#define MIPSNBSD_NUM_FPREGS 33
+
+/* Supply register REGNUM from the buffer specified by FPREGS and LEN
+ in the floating-point register set REGSET to register cache
+ REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */
+
+static void
+mipsnbsd_supply_fpregset (const struct regset *regset,
+ struct regcache *regcache,
+ int regnum, const void *fpregs, size_t len)
+{
+ size_t regsize = mips_isa_regsize (get_regcache_arch (regcache));
+ const char *regs = (const char *) fpregs;
+ int i;
+
+ gdb_assert (len >= MIPSNBSD_NUM_FPREGS * regsize);
+
+ for (i = MIPS_FP0_REGNUM; i <= MIPS_FSR_REGNUM; i++)
+ {
+ if (regnum == i || regnum == -1)
+ regcache_raw_supply (regcache, i,
+ regs + (i - MIPS_FP0_REGNUM) * regsize);
+ }
+}
+
+/* Supply register REGNUM from the buffer specified by GREGS and LEN
+ in the general-purpose register set REGSET to register cache
+ REGCACHE. If REGNUM is -1, do this for all registers in REGSET. */
+
+static void
+mipsnbsd_supply_gregset (const struct regset *regset,
+ struct regcache *regcache, int regnum,
+ const void *gregs, size_t len)
+{
+ size_t regsize = mips_isa_regsize (get_regcache_arch (regcache));
+ const char *regs = (const char *) gregs;
+ int i;
+
+ gdb_assert (len >= MIPSNBSD_NUM_GREGS * regsize);
+
+ for (i = 0; i <= MIPS_PC_REGNUM; i++)
+ {
+ if (regnum == i || regnum == -1)
+ regcache_raw_supply (regcache, i, regs + i * regsize);
+ }
+
+ if (len >= (MIPSNBSD_NUM_GREGS + MIPSNBSD_NUM_FPREGS) * regsize)
+ {
+ regs += MIPSNBSD_NUM_GREGS * regsize;
+ len -= MIPSNBSD_NUM_GREGS * regsize;
+ mipsnbsd_supply_fpregset (regset, regcache, regnum, regs, len);
+ }
+}
+
+/* NetBSD/mips register sets. */
+
+static const struct regset mipsnbsd_gregset =
+{
+ NULL,
+ mipsnbsd_supply_gregset,
+ NULL,
+ REGSET_VARIABLE_SIZE
+};
+
+static const struct regset mipsnbsd_fpregset =
+{
+ NULL,
+ mipsnbsd_supply_fpregset
+};
+
+/* Iterate over core file register note sections. */
+
+static void
+mipsnbsd_iterate_over_regset_sections (struct gdbarch *gdbarch,
+ iterate_over_regset_sections_cb *cb,
+ void *cb_data,
+ const struct regcache *regcache)
+{
+ size_t regsize = mips_isa_regsize (gdbarch);
+
+ cb (".reg", MIPSNBSD_NUM_GREGS * regsize, &mipsnbsd_gregset,
+ NULL, cb_data);
+ cb (".reg2", MIPSNBSD_NUM_FPREGS * regsize, &mipsnbsd_fpregset,
+ NULL, cb_data);
+}
+
+
+/* Conveniently, GDB uses the same register numbering as the
+ ptrace register structure used by NetBSD/mips. */
+
+void
+mipsnbsd_supply_reg (struct regcache *regcache, const char *regs, int regno)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int i;
+
+ for (i = 0; i <= gdbarch_pc_regnum (gdbarch); i++)
+ {
+ if (regno == i || regno == -1)
+ {
+ if (gdbarch_cannot_fetch_register (gdbarch, i))
+ regcache_raw_supply (regcache, i, NULL);
+ else
+ regcache_raw_supply (regcache, i,
+ regs + (i * mips_isa_regsize (gdbarch)));
+ }
+ }
+}
+
+void
+mipsnbsd_fill_reg (const struct regcache *regcache, char *regs, int regno)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int i;
+
+ for (i = 0; i <= gdbarch_pc_regnum (gdbarch); i++)
+ if ((regno == i || regno == -1)
+ && ! gdbarch_cannot_store_register (gdbarch, i))
+ regcache_raw_collect (regcache, i,
+ regs + (i * mips_isa_regsize (gdbarch)));
+}
+
+void
+mipsnbsd_supply_fpreg (struct regcache *regcache,
+ const char *fpregs, int regno)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int i;
+
+ for (i = gdbarch_fp0_regnum (gdbarch);
+ i <= mips_regnum (gdbarch)->fp_implementation_revision;
+ i++)
+ {
+ if (regno == i || regno == -1)
+ {
+ if (gdbarch_cannot_fetch_register (gdbarch, i))
+ regcache_raw_supply (regcache, i, NULL);
+ else
+ regcache_raw_supply (regcache, i,
+ fpregs
+ + ((i - gdbarch_fp0_regnum (gdbarch))
+ * mips_isa_regsize (gdbarch)));
+ }
+ }
+}
+
+void
+mipsnbsd_fill_fpreg (const struct regcache *regcache, char *fpregs, int regno)
+{
+ struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ int i;
+
+ for (i = gdbarch_fp0_regnum (gdbarch);
+ i <= mips_regnum (gdbarch)->fp_control_status;
+ i++)
+ if ((regno == i || regno == -1)
+ && ! gdbarch_cannot_store_register (gdbarch, i))
+ regcache_raw_collect (regcache, i,
+ fpregs + ((i - gdbarch_fp0_regnum (gdbarch))
+ * mips_isa_regsize (gdbarch)));
+}
+
+#if 0
+
+/* Under NetBSD/mips, signal handler invocations can be identified by the
+ designated code sequence that is used to return from a signal handler.
+ In particular, the return address of a signal handler points to the
+ following code sequence:
+
+ addu a0, sp, 16
+ li v0, 295 # __sigreturn14
+ syscall
+
+ Each instruction has a unique encoding, so we simply attempt to match
+ the instruction the PC is pointing to with any of the above instructions.
+ If there is a hit, we know the offset to the start of the designated
+ sequence and can then check whether we really are executing in the
+ signal trampoline. If not, -1 is returned, otherwise the offset from the
+ start of the return sequence is returned. */
+
+#define RETCODE_NWORDS 3
+#define RETCODE_SIZE (RETCODE_NWORDS * 4)
+
+static const unsigned char sigtramp_retcode_mipsel[RETCODE_SIZE] =
+{
+ 0x10, 0x00, 0xa4, 0x27, /* addu a0, sp, 16 */
+ 0x27, 0x01, 0x02, 0x24, /* li v0, 295 */
+ 0x0c, 0x00, 0x00, 0x00, /* syscall */
+};
+
+static const unsigned char sigtramp_retcode_mipseb[RETCODE_SIZE] =
+{
+ 0x27, 0xa4, 0x00, 0x10, /* addu a0, sp, 16 */
+ 0x24, 0x02, 0x01, 0x27, /* li v0, 295 */
+ 0x00, 0x00, 0x00, 0x0c, /* syscall */
+};
+
+#endif
+
+/* Figure out where the longjmp will land. We expect that we have
+ just entered longjmp and haven't yet setup the stack frame, so the
+ args are still in the argument regs. MIPS_A0_REGNUM points at the
+ jmp_buf structure from which we extract the PC that we will land
+ at. The PC is copied into *pc. This routine returns true on
+ success. */
+
+#define NBSD_MIPS_JB_PC (2 * 4)
+#define NBSD_MIPS_JB_ELEMENT_SIZE(gdbarch) mips_isa_regsize (gdbarch)
+#define NBSD_MIPS_JB_OFFSET(gdbarch) (NBSD_MIPS_JB_PC * \
+ NBSD_MIPS_JB_ELEMENT_SIZE (gdbarch))
+
+static int
+mipsnbsd_get_longjmp_target (struct frame_info *frame, CORE_ADDR *pc)
+{
+ struct gdbarch *gdbarch = get_frame_arch (frame);
+ enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
+ CORE_ADDR jb_addr;
+ gdb_byte *buf;
+
+ buf = (gdb_byte *) alloca (NBSD_MIPS_JB_ELEMENT_SIZE (gdbarch));
+
+ jb_addr = get_frame_register_unsigned (frame, MIPS_A0_REGNUM);
+
+ if (target_read_memory (jb_addr + NBSD_MIPS_JB_OFFSET (gdbarch), buf,
+ NBSD_MIPS_JB_ELEMENT_SIZE (gdbarch)))
+ return 0;
+
+ *pc = extract_unsigned_integer (buf, NBSD_MIPS_JB_ELEMENT_SIZE (gdbarch),
+ byte_order);
+ return 1;
+}
+
+static int
+mipsnbsd_cannot_fetch_register (struct gdbarch *gdbarch, int regno)
+{
+ return (regno == MIPS_ZERO_REGNUM
+ || regno == mips_regnum (gdbarch)->fp_implementation_revision);
+}
+
+static int
+mipsnbsd_cannot_store_register (struct gdbarch *gdbarch, int regno)
+{
+ return (regno == MIPS_ZERO_REGNUM
+ || regno == mips_regnum (gdbarch)->fp_implementation_revision);
+}
+
+/* Shared library support. */
+
+/* NetBSD/mips uses a slightly different `struct link_map' than the
+ other NetBSD platforms. */
+
+static struct link_map_offsets *
+mipsnbsd_ilp32_fetch_link_map_offsets (void)
+{
+ static struct link_map_offsets lmo;
+ static struct link_map_offsets *lmp = NULL;
+
+ if (lmp == NULL)
+ {
+ lmp = &lmo;
+
+ lmo.r_version_offset = 0;
+ lmo.r_version_size = 4;
+ lmo.r_map_offset = 4;
+ lmo.r_brk_offset = 8;
+ lmo.r_ldsomap_offset = -1;
+
+ /* Everything we need is in the first 24 bytes. */
+ lmo.link_map_size = 24;
+ lmo.l_addr_offset = 4;
+ lmo.l_name_offset = 8;
+ lmo.l_ld_offset = 12;
+ lmo.l_next_offset = 16;
+ lmo.l_prev_offset = 20;
+ }
+
+ return lmp;
+}
+
+static struct link_map_offsets *
+mipsnbsd_lp64_fetch_link_map_offsets (void)
+{
+ static struct link_map_offsets lmo;
+ static struct link_map_offsets *lmp = NULL;
+
+ if (lmp == NULL)
+ {
+ lmp = &lmo;
+
+ lmo.r_version_offset = 0;
+ lmo.r_version_size = 4;
+ lmo.r_map_offset = 8;
+ lmo.r_brk_offset = 16;
+ lmo.r_ldsomap_offset = -1;
+
+ /* Everything we need is in the first 40 bytes. */
+ lmo.link_map_size = 48;
+ lmo.l_addr_offset = 0;
+ lmo.l_name_offset = 16;
+ lmo.l_ld_offset = 24;
+ lmo.l_next_offset = 32;
+ lmo.l_prev_offset = 40;
+ }
+
+ return lmp;
+}
+
+
+static void
+mipsnbsd_init_abi (struct gdbarch_info info,
+ struct gdbarch *gdbarch)
+{
+ set_gdbarch_iterate_over_regset_sections
+ (gdbarch, mipsnbsd_iterate_over_regset_sections);
+
+ set_gdbarch_get_longjmp_target (gdbarch, mipsnbsd_get_longjmp_target);
+
+ set_gdbarch_cannot_fetch_register (gdbarch, mipsnbsd_cannot_fetch_register);
+ set_gdbarch_cannot_store_register (gdbarch, mipsnbsd_cannot_store_register);
+
+ set_gdbarch_software_single_step (gdbarch, mips_software_single_step);
+
+ /* NetBSD/mips has SVR4-style shared libraries. */
+ set_solib_svr4_fetch_link_map_offsets
+ (gdbarch, (gdbarch_ptr_bit (gdbarch) == 32 ?
+ mipsnbsd_ilp32_fetch_link_map_offsets :
+ mipsnbsd_lp64_fetch_link_map_offsets));
+}
+
+
+/* Provide a prototype to silence -Wmissing-prototypes. */
+extern initialize_file_ftype _initialize_mipsnbsd_tdep;
+
+void
+_initialize_mipsnbsd_tdep (void)
+{
+ gdbarch_register_osabi (bfd_arch_mips, 0, GDB_OSABI_NETBSD_ELF,
+ mipsnbsd_init_abi);
+}