binutils-gdb/gdb/riscv-fbsd-tdep.c
Andrew Burgess 6a9ad81c44 gdb/riscv: use a single regset supply function for riscv fbsd & linux
The RISC-V x0 register is hard-coded to zero.  As such neither Linux
or FreeBSD supply the value of the register x0 in their core dump
files.

For FreeBSD we take care of this by manually supplying the value of x0
in riscv_fbsd_supply_gregset, however we don't do this for Linux.  As
a result after loading a core file on Linux we see this behaviour:

  (gdb) p $x0
  $1 = <unavailable>

In this commit I make riscv_fbsd_supply_gregset a common function that
can be shared between RISC-V for FreeBSD and Linux, this resolves the
above issue.

There is a similar problem for the two registers `fflags` and `frm`.
These two floating point related CSRs are a little weird.  They are
separate CSRs in the RISC-V specification, but are actually sub-fields
of the `fcsr` CSR.

As a result neither Linux or FreeBSD supply the `fflags` or `frm`
registers as separate fields in their core dumps, and so, after
restoring a core dump these register are similarly unavailable.

In this commit I supply `fflags` and `frm` by first asking for the
value of `fcsr`, extracting the two fields, and using these to supply
the values for `fflags` and `frm`.

gdb/ChangeLog:

	* riscv-fbsd-tdep.c (riscv_fbsd_supply_gregset): Delete.
	(riscv_fbsd_gregset): Use riscv_supply_regset.
	(riscv_fbsd_fpregset): Likewise.
	* riscv-linux-tdep.c (riscv_linux_gregset): Likewise.
	(riscv_linux_fregset): Likewise.
	* riscv-tdep.c (riscv_supply_regset): Define new function.
	* riscv-tdep.h (riscv_supply_regset): Declare new function.
2021-01-18 14:14:11 +00:00

219 lines
6.7 KiB
C

/* Target-dependent code for FreeBSD on RISC-V processors.
Copyright (C) 2018-2021 Free Software Foundation, 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 "fbsd-tdep.h"
#include "osabi.h"
#include "riscv-tdep.h"
#include "riscv-fbsd-tdep.h"
#include "solib-svr4.h"
#include "target.h"
#include "trad-frame.h"
#include "tramp-frame.h"
#include "gdbarch.h"
#include "inferior.h"
/* Register maps. */
static const struct regcache_map_entry riscv_fbsd_gregmap[] =
{
{ 1, RISCV_RA_REGNUM, 0 },
{ 1, RISCV_SP_REGNUM, 0 },
{ 1, RISCV_GP_REGNUM, 0 },
{ 1, RISCV_TP_REGNUM, 0 },
{ 3, 5, 0 }, /* t0 - t2 */
{ 4, 28, 0 }, /* t3 - t6 */
{ 2, RISCV_FP_REGNUM, 0 }, /* s0 - s1 */
{ 10, 18, 0 }, /* s2 - s11 */
{ 8, RISCV_A0_REGNUM, 0 }, /* a0 - a7 */
{ 1, RISCV_PC_REGNUM, 0 },
{ 1, RISCV_CSR_SSTATUS_REGNUM, 0 },
{ 0 }
};
static const struct regcache_map_entry riscv_fbsd_fpregmap[] =
{
{ 32, RISCV_FIRST_FP_REGNUM, 16 },
{ 1, RISCV_CSR_FCSR_REGNUM, 8 },
{ 0 }
};
/* Register set definitions. */
const struct regset riscv_fbsd_gregset =
{
riscv_fbsd_gregmap, riscv_supply_regset, regcache_collect_regset
};
const struct regset riscv_fbsd_fpregset =
{
riscv_fbsd_fpregmap, riscv_supply_regset, regcache_collect_regset
};
/* Implement the "iterate_over_regset_sections" gdbarch method. */
static void
riscv_fbsd_iterate_over_regset_sections (struct gdbarch *gdbarch,
iterate_over_regset_sections_cb *cb,
void *cb_data,
const struct regcache *regcache)
{
cb (".reg", RISCV_FBSD_NUM_GREGS * riscv_isa_xlen (gdbarch),
RISCV_FBSD_NUM_GREGS * riscv_isa_xlen (gdbarch),
&riscv_fbsd_gregset, NULL, cb_data);
cb (".reg2", RISCV_FBSD_SIZEOF_FPREGSET, RISCV_FBSD_SIZEOF_FPREGSET,
&riscv_fbsd_fpregset, NULL, cb_data);
}
/* In a signal frame, sp points to a 'struct sigframe' which is
defined as:
struct sigframe {
siginfo_t sf_si;
ucontext_t sf_uc;
};
ucontext_t is defined as:
struct __ucontext {
sigset_t uc_sigmask;
mcontext_t uc_mcontext;
...
};
The mcontext_t contains the general purpose register set followed
by the floating point register set. The floating point register
set is only valid if the _MC_FP_VALID flag is set in mc_flags. */
#define RISCV_SIGFRAME_UCONTEXT_OFFSET 80
#define RISCV_UCONTEXT_MCONTEXT_OFFSET 16
#define RISCV_MCONTEXT_FLAG_FP_VALID 0x1
/* Implement the "init" method of struct tramp_frame. */
static void
riscv_fbsd_sigframe_init (const struct tramp_frame *self,
struct frame_info *this_frame,
struct trad_frame_cache *this_cache,
CORE_ADDR func)
{
struct gdbarch *gdbarch = get_frame_arch (this_frame);
enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
CORE_ADDR sp = get_frame_register_unsigned (this_frame, RISCV_SP_REGNUM);
CORE_ADDR mcontext_addr
= (sp
+ RISCV_SIGFRAME_UCONTEXT_OFFSET
+ RISCV_UCONTEXT_MCONTEXT_OFFSET);
gdb_byte buf[4];
trad_frame_set_reg_regmap (this_cache, riscv_fbsd_gregmap, mcontext_addr,
RISCV_FBSD_NUM_GREGS * riscv_isa_xlen (gdbarch));
CORE_ADDR fpregs_addr
= mcontext_addr + RISCV_FBSD_NUM_GREGS * riscv_isa_xlen (gdbarch);
CORE_ADDR fp_flags_addr
= fpregs_addr + RISCV_FBSD_SIZEOF_FPREGSET;
if (target_read_memory (fp_flags_addr, buf, 4) == 0
&& (extract_unsigned_integer (buf, 4, byte_order)
& RISCV_MCONTEXT_FLAG_FP_VALID))
trad_frame_set_reg_regmap (this_cache, riscv_fbsd_fpregmap, fpregs_addr,
RISCV_FBSD_SIZEOF_FPREGSET);
trad_frame_set_id (this_cache, frame_id_build (sp, func));
}
/* RISC-V supports 16-bit instructions ("C") as well as 32-bit
instructions. The signal trampoline on FreeBSD uses a mix of
these, but tramp_frame assumes a fixed instruction size. To cope,
claim that all instructions are 16 bits and use two "slots" for
32-bit instructions. */
static const struct tramp_frame riscv_fbsd_sigframe =
{
SIGTRAMP_FRAME,
2,
{
{0x850a, ULONGEST_MAX}, /* mov a0, sp */
{0x0513, ULONGEST_MAX}, /* addi a0, a0, #SF_UC */
{0x0505, ULONGEST_MAX},
{0x0293, ULONGEST_MAX}, /* li t0, #SYS_sigreturn */
{0x1a10, ULONGEST_MAX},
{0x0073, ULONGEST_MAX}, /* ecall */
{0x0000, ULONGEST_MAX},
{TRAMP_SENTINEL_INSN, ULONGEST_MAX}
},
riscv_fbsd_sigframe_init
};
/* Implement the "get_thread_local_address" gdbarch method. */
static CORE_ADDR
riscv_fbsd_get_thread_local_address (struct gdbarch *gdbarch, ptid_t ptid,
CORE_ADDR lm_addr, CORE_ADDR offset)
{
struct regcache *regcache;
regcache = get_thread_arch_regcache (current_inferior ()->process_target (),
ptid, gdbarch);
target_fetch_registers (regcache, RISCV_TP_REGNUM);
ULONGEST tp;
if (regcache->cooked_read (RISCV_TP_REGNUM, &tp) != REG_VALID)
error (_("Unable to fetch %%tp"));
/* %tp points to the end of the TCB which contains two pointers.
The first pointer in the TCB points to the DTV array. */
CORE_ADDR dtv_addr = tp - (gdbarch_ptr_bit (gdbarch) / 8) * 2;
return fbsd_get_thread_local_address (gdbarch, dtv_addr, lm_addr, offset);
}
/* Implement the 'init_osabi' method of struct gdb_osabi_handler. */
static void
riscv_fbsd_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch)
{
/* Generic FreeBSD support. */
fbsd_init_abi (info, gdbarch);
set_gdbarch_software_single_step (gdbarch, riscv_software_single_step);
set_solib_svr4_fetch_link_map_offsets (gdbarch,
(riscv_isa_xlen (gdbarch) == 4
? svr4_ilp32_fetch_link_map_offsets
: svr4_lp64_fetch_link_map_offsets));
tramp_frame_prepend_unwinder (gdbarch, &riscv_fbsd_sigframe);
set_gdbarch_iterate_over_regset_sections
(gdbarch, riscv_fbsd_iterate_over_regset_sections);
set_gdbarch_fetch_tls_load_module_address (gdbarch,
svr4_fetch_objfile_link_map);
set_gdbarch_get_thread_local_address (gdbarch,
riscv_fbsd_get_thread_local_address);
}
void _initialize_riscv_fbsd_tdep ();
void
_initialize_riscv_fbsd_tdep ()
{
gdbarch_register_osabi (bfd_arch_riscv, 0, GDB_OSABI_FREEBSD,
riscv_fbsd_init_abi);
}