#include "defs.h"
#include "gdbcore.h"
#include "regcache.h"
#include "regset.h"
#include "target.h"
#include "value.h"
#include "osabi.h"
#include "gdb_assert.h"
#include "gdb_string.h"
#include "nbsd-tdep.h"
#include "mipsnbsd-tdep.h"
#include "mips-tdep.h"
#include "solib-svr4.h"
#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
#define MIPSNBSD_NUM_GREGS 38
#define MIPSNBSD_NUM_FPREGS 33
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 = 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);
}
}
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 = 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);
}
}
static struct regset mipsnbsd_gregset =
{
NULL,
mipsnbsd_supply_gregset
};
static struct regset mipsnbsd_fpregset =
{
NULL,
mipsnbsd_supply_fpregset
};
static const struct regset *
mipsnbsd_regset_from_core_section (struct gdbarch *gdbarch,
const char *sect_name, size_t sect_size)
{
size_t regsize = mips_isa_regsize (gdbarch);
if (strcmp (sect_name, ".reg") == 0
&& sect_size >= MIPSNBSD_NUM_GREGS * regsize)
return &mipsnbsd_gregset;
if (strcmp (sect_name, ".reg2") == 0
&& sect_size >= MIPSNBSD_NUM_FPREGS * regsize)
return &mipsnbsd_fpregset;
return NULL;
}
void
mipsnbsd_supply_reg (char *regs, int regno)
{
int i;
for (i = 0; i <= PC_REGNUM; i++)
{
if (regno == i || regno == -1)
{
if (CANNOT_FETCH_REGISTER (i))
regcache_raw_supply (current_regcache, i, NULL);
else
regcache_raw_supply (current_regcache, i,
regs + (i * mips_isa_regsize (current_gdbarch)));
}
}
}
void
mipsnbsd_fill_reg (char *regs, int regno)
{
int i;
for (i = 0; i <= PC_REGNUM; i++)
if ((regno == i || regno == -1) && ! CANNOT_STORE_REGISTER (i))
regcache_raw_collect (current_regcache, i,
regs + (i * mips_isa_regsize (current_gdbarch)));
}
void
mipsnbsd_supply_fpreg (char *fpregs, int regno)
{
int i;
for (i = FP0_REGNUM;
i <= mips_regnum (current_gdbarch)->fp_implementation_revision;
i++)
{
if (regno == i || regno == -1)
{
if (CANNOT_FETCH_REGISTER (i))
regcache_raw_supply (current_regcache, i, NULL);
else
regcache_raw_supply (current_regcache, i,
fpregs + ((i - FP0_REGNUM) * mips_isa_regsize (current_gdbarch)));
}
}
}
void
mipsnbsd_fill_fpreg (char *fpregs, int regno)
{
int i;
for (i = FP0_REGNUM; i <= mips_regnum (current_gdbarch)->fp_control_status;
i++)
if ((regno == i || regno == -1) && ! CANNOT_STORE_REGISTER (i))
regcache_raw_collect (current_regcache, i,
fpregs + ((i - FP0_REGNUM) * mips_isa_regsize (current_gdbarch)));
}
#define RETCODE_NWORDS 3
#define RETCODE_SIZE (RETCODE_NWORDS * 4)
static const unsigned char sigtramp_retcode_mipsel[RETCODE_SIZE] =
{
0x10, 0x00, 0xa4, 0x27,
0x27, 0x01, 0x02, 0x24,
0x0c, 0x00, 0x00, 0x00,
};
static const unsigned char sigtramp_retcode_mipseb[RETCODE_SIZE] =
{
0x27, 0xa4, 0x00, 0x10,
0x24, 0x02, 0x01, 0x27,
0x00, 0x00, 0x00, 0x0c,
};
static LONGEST
mipsnbsd_sigtramp_offset (struct frame_info *next_frame)
{
CORE_ADDR pc = frame_pc_unwind (next_frame);
const char *retcode = TARGET_BYTE_ORDER == BFD_ENDIAN_BIG
? sigtramp_retcode_mipseb : sigtramp_retcode_mipsel;
unsigned char ret[RETCODE_SIZE], w[4];
LONGEST off;
int i;
if (!safe_frame_unwind_memory (next_frame, pc, w, sizeof (w)))
return -1;
for (i = 0; i < RETCODE_NWORDS; i++)
{
if (memcmp (w, retcode + (i * 4), 4) == 0)
break;
}
if (i == RETCODE_NWORDS)
return -1;
off = i * 4;
pc -= off;
if (!safe_frame_unwind_memory (next_frame, pc, ret, sizeof (ret)))
return -1;
if (memcmp (ret, retcode, RETCODE_SIZE) == 0)
return off;
return -1;
}
#define NBSD_MIPS_JB_PC (2 * 4)
#define NBSD_MIPS_JB_ELEMENT_SIZE mips_isa_regsize (current_gdbarch)
#define NBSD_MIPS_JB_OFFSET (NBSD_MIPS_JB_PC * \
NBSD_MIPS_JB_ELEMENT_SIZE)
static int
mipsnbsd_get_longjmp_target (CORE_ADDR *pc)
{
CORE_ADDR jb_addr;
char *buf;
buf = alloca (NBSD_MIPS_JB_ELEMENT_SIZE);
jb_addr = read_register (MIPS_A0_REGNUM);
if (target_read_memory (jb_addr + NBSD_MIPS_JB_OFFSET, buf,
NBSD_MIPS_JB_ELEMENT_SIZE))
return 0;
*pc = extract_unsigned_integer (buf, NBSD_MIPS_JB_ELEMENT_SIZE);
return 1;
}
static int
mipsnbsd_cannot_fetch_register (int regno)
{
return (regno == MIPS_ZERO_REGNUM
|| regno == mips_regnum (current_gdbarch)->fp_implementation_revision);
}
static int
mipsnbsd_cannot_store_register (int regno)
{
return (regno == MIPS_ZERO_REGNUM
|| regno == mips_regnum (current_gdbarch)->fp_implementation_revision);
}
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_debug_size = 8;
lmo.r_map_offset = 4;
lmo.r_map_size = 4;
lmo.link_map_size = 24;
lmo.l_addr_offset = 4;
lmo.l_addr_size = 4;
lmo.l_name_offset = 8;
lmo.l_name_size = 4;
lmo.l_next_offset = 16;
lmo.l_next_size = 4;
lmo.l_prev_offset = 20;
lmo.l_prev_size = 4;
}
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_debug_size = 16;
lmo.r_map_offset = 8;
lmo.r_map_size = 8;
lmo.link_map_size = 48;
lmo.l_addr_offset = 0;
lmo.l_addr_size = 8;
lmo.l_name_offset = 16;
lmo.l_name_size = 8;
lmo.l_next_offset = 32;
lmo.l_next_size = 8;
lmo.l_prev_offset = 40;
lmo.l_prev_size = 8;
}
return lmp;
}
static void
mipsnbsd_init_abi (struct gdbarch_info info,
struct gdbarch *gdbarch)
{
set_gdbarch_regset_from_core_section
(gdbarch, mipsnbsd_regset_from_core_section);
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);
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));
}
static enum gdb_osabi
mipsnbsd_core_osabi_sniffer (bfd *abfd)
{
if (strcmp (bfd_get_target (abfd), "netbsd-core") == 0)
return GDB_OSABI_NETBSD_ELF;
return GDB_OSABI_UNKNOWN;
}
void
_initialize_mipsnbsd_tdep (void)
{
gdbarch_register_osabi (bfd_arch_mips, 0, GDB_OSABI_NETBSD_ELF,
mipsnbsd_init_abi);
}