/* Target-dependent code for the RISC-V architecture, for GDB.
- Copyright (C) 2018 Free Software Foundation, Inc.
+ Copyright (C) 2018-2020 Free Software Foundation, Inc.
This file is part of GDB.
#include "dwarf2-frame.h"
#include "user-regs.h"
#include "valprint.h"
-#include "common-defs.h"
+#include "gdbsupport/common-defs.h"
#include "opcode/riscv-opc.h"
#include "cli/cli-decode.h"
#include "observable.h"
#include "prologue-value.h"
#include "arch/riscv.h"
+#include "riscv-ravenscar-thread.h"
/* The stack must be 16-byte aligned. */
#define SP_ALIGNMENT 16
{ RISCV_FIRST_FP_REGNUM + 5, { "ft5", "f5" }, true },
{ RISCV_FIRST_FP_REGNUM + 6, { "ft6", "f6" }, true },
{ RISCV_FIRST_FP_REGNUM + 7, { "ft7", "f7" }, true },
- { RISCV_FIRST_FP_REGNUM + 8, { "fs0", "f8", "s0" }, true },
+ { RISCV_FIRST_FP_REGNUM + 8, { "fs0", "f8" }, true },
{ RISCV_FIRST_FP_REGNUM + 9, { "fs1", "f9" }, true },
{ RISCV_FIRST_FP_REGNUM + 10, { "fa0", "f10" }, true },
{ RISCV_FIRST_FP_REGNUM + 11, { "fa1", "f11" }, true },
int
riscv_isa_xlen (struct gdbarch *gdbarch)
{
- return gdbarch_tdep (gdbarch)->features.xlen;
+ return gdbarch_tdep (gdbarch)->isa_features.xlen;
+}
+
+/* See riscv-tdep.h. */
+
+int
+riscv_abi_xlen (struct gdbarch *gdbarch)
+{
+ return gdbarch_tdep (gdbarch)->abi_features.xlen;
}
/* See riscv-tdep.h. */
int
riscv_isa_flen (struct gdbarch *gdbarch)
{
- return gdbarch_tdep (gdbarch)->features.flen;
+ return gdbarch_tdep (gdbarch)->isa_features.flen;
+}
+
+/* See riscv-tdep.h. */
+
+int
+riscv_abi_flen (struct gdbarch *gdbarch)
+{
+ return gdbarch_tdep (gdbarch)->abi_features.flen;
}
/* Return true if the target for GDBARCH has floating point hardware. */
static bool
riscv_has_fp_abi (struct gdbarch *gdbarch)
{
- return gdbarch_tdep (gdbarch)->features.hw_float_abi;
+ return gdbarch_tdep (gdbarch)->abi_features.flen > 0;
}
/* Return true if REGNO is a floating pointer register. */
unaligned_p = true;
else
{
- /* Read the opcode byte to determine the instruction length. */
+ /* Read the opcode byte to determine the instruction length. If
+ the read fails this may be because we tried to set the
+ breakpoint at an invalid address, in this case we provide a
+ fake result which will give a breakpoint length of 4.
+ Hopefully when we try to actually insert the breakpoint we
+ will see a failure then too which will be reported to the
+ user. */
+ if (target_read_code (*pcptr, buf, 1) == -1)
+ buf[0] = 0;
read_code (*pcptr, buf, 1);
}
switch (regnum)
{
- #include "opcode/riscv-opc.h"
+#include "opcode/riscv-opc.h"
}
#undef DECLARE_CSR
}
fputs_filtered (name, file);
print_spaces_filtered (value_column_1 - strlen (name), file);
- TRY
+ try
{
val = value_of_register (regnum, frame);
regtype = value_type (val);
}
- CATCH (ex, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &ex)
{
/* Handle failure to read a register without interrupting the entire
'info registers' flow. */
- fprintf_filtered (file, "%s\n", ex.message);
+ fprintf_filtered (file, "%s\n", ex.what ());
return;
}
- END_CATCH
print_raw_format = (value_entirely_available (val)
&& !value_optimized_out (val));
{
struct value_print_options opts;
const gdb_byte *valaddr = value_contents_for_printing (val);
- enum bfd_endian byte_order = gdbarch_byte_order (get_type_arch (regtype));
+ enum bfd_endian byte_order = type_byte_order (regtype);
get_user_print_options (&opts);
opts.deref_ref = 1;
int size = register_size (gdbarch, regnum);
unsigned xlen;
+ /* The SD field is always in the upper bit of MSTATUS, regardless
+ of the number of bits in MSTATUS. */
d = value_as_long (val);
- xlen = size * 4;
+ xlen = size * 8;
fprintf_filtered (file,
"\tSD:%X VM:%02X MXR:%X PUM:%X MPRV:%X XS:%X "
"FS:%X MPP:%x HPP:%X SPP:%X MPIE:%X HPIE:%X "
int base;
unsigned xlen, i;
LONGEST d;
+ int size = register_size (gdbarch, regnum);
+ /* The MXL field is always in the upper two bits of MISA,
+ regardless of the number of bits in MISA. Mask out other
+ bits to ensure we have a positive value. */
d = value_as_long (val);
- base = d >> 30;
+ base = (d >> ((size * 8) - 2)) & 0x3;
xlen = 16;
for (; base > 0; base--)
else if (reggroup == restore_reggroup || reggroup == save_reggroup)
{
if (riscv_has_fp_regs (gdbarch))
- return regnum <= RISCV_LAST_FP_REGNUM;
+ return (regnum <= RISCV_LAST_FP_REGNUM
+ || regnum == RISCV_CSR_FCSR_REGNUM
+ || regnum == RISCV_CSR_FFLAGS_REGNUM
+ || regnum == RISCV_CSR_FRM_REGNUM);
else
return regnum < RISCV_FIRST_FP_REGNUM;
}
LUI,
SD,
SW,
- /* These are needed for software breakopint support. */
+ /* These are needed for software breakpoint support. */
JAL,
JALR,
BEQ,
m_opcode = OTHER;
}
else
- internal_error (__FILE__, __LINE__,
- _("unable to decode %d byte instructions in "
- "prologue at %s"), m_length,
- core_addr_to_string (pc));
+ {
+ /* This must be a 6 or 8 byte instruction, we don't currently decode
+ any of these, so just ignore it. */
+ gdb_assert (m_length == 6 || m_length == 8);
+ m_opcode = OTHER;
+ }
}
/* The prologue scanner. This is currently only used for skipping the
if (stack.find_reg (gdbarch, i, &offset))
{
if (riscv_debug_unwinder)
- fprintf_unfiltered (gdb_stdlog,
- "Register $%s at stack offset %ld\n",
- gdbarch_register_name (gdbarch, i),
- offset);
+ {
+ /* Display OFFSET as a signed value, the offsets are from
+ the frame base address to the registers location on
+ the stack, with a descending stack this means the
+ offsets are always negative. */
+ fprintf_unfiltered (gdb_stdlog,
+ "Register $%s at stack offset %s\n",
+ gdbarch_register_name (gdbarch, i),
+ plongest ((LONGEST) offset));
+ }
trad_frame_set_addr (cache->regs, i, offset);
}
}
struct type *value_type, CORE_ADDR *real_pc,
CORE_ADDR *bp_addr, struct regcache *regcache)
{
+ /* A nop instruction is 'add x0, x0, 0'. */
+ static const gdb_byte nop_insn[] = { 0x13, 0x00, 0x00, 0x00 };
+
/* Allocate space for a breakpoint, and keep the stack correctly
- aligned. */
+ aligned. The space allocated here must be at least big enough to
+ accommodate the NOP_INSN defined above. */
sp -= 16;
*bp_addr = sp;
*real_pc = funaddr;
+
+ /* When we insert a breakpoint we select whether to use a compressed
+ breakpoint or not based on the existing contents of the memory.
+
+ If the breakpoint is being placed onto the stack as part of setting up
+ for an inferior call from GDB, then the existing stack contents may
+ randomly appear to be a compressed instruction, causing GDB to insert
+ a compressed breakpoint. If this happens on a target that does not
+ support compressed instructions then this could cause problems.
+
+ To prevent this issue we write an uncompressed nop onto the stack at
+ the location where the breakpoint will be inserted. In this way we
+ ensure that we always use an uncompressed breakpoint, which should
+ work on all targets.
+
+ We call TARGET_WRITE_MEMORY here so that if the write fails we don't
+ throw an exception. Instead we ignore the error and move on. The
+ assumption is that either GDB will error later when actually trying to
+ insert a software breakpoint, or GDB will use hardware breakpoints and
+ there will be no need to write to memory later. */
+ int status = target_write_memory (*bp_addr, nop_insn, sizeof (nop_insn));
+
+ if (riscv_debug_breakpoints || riscv_debug_infcall)
+ fprintf_unfiltered (gdb_stdlog,
+ "Writing %s-byte nop instruction to %s: %s\n",
+ plongest (sizeof (nop_insn)),
+ paddress (gdbarch, *bp_addr),
+ (status == 0 ? "success" : "failed"));
+
return sp;
}
-/* Compute the alignment of the type T. Used while setting up the
- arguments for a dummy call. */
+/* Implement the gdbarch type alignment method, overrides the generic
+ alignment algorithm for anything that is RISC-V specific. */
-static int
-riscv_type_alignment (struct type *t)
+static ULONGEST
+riscv_type_align (gdbarch *gdbarch, type *type)
{
- t = check_typedef (t);
- switch (TYPE_CODE (t))
- {
- default:
- error (_("Could not compute alignment of type"));
+ type = check_typedef (type);
+ if (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type))
+ return std::min (TYPE_LENGTH (type), (ULONGEST) BIGGEST_ALIGNMENT);
- case TYPE_CODE_RVALUE_REF:
- case TYPE_CODE_PTR:
- case TYPE_CODE_ENUM:
- case TYPE_CODE_INT:
- case TYPE_CODE_FLT:
- case TYPE_CODE_REF:
- case TYPE_CODE_CHAR:
- case TYPE_CODE_BOOL:
- return TYPE_LENGTH (t);
-
- case TYPE_CODE_ARRAY:
- if (TYPE_VECTOR (t))
- return std::min (TYPE_LENGTH (t), (unsigned) BIGGEST_ALIGNMENT);
- /* FALLTHROUGH */
-
- case TYPE_CODE_COMPLEX:
- return riscv_type_alignment (TYPE_TARGET_TYPE (t));
-
- case TYPE_CODE_STRUCT:
- case TYPE_CODE_UNION:
- {
- int i;
- int align = 1;
-
- for (i = 0; i < TYPE_NFIELDS (t); ++i)
- {
- if (TYPE_FIELD_LOC_KIND (t, i) == FIELD_LOC_KIND_BITPOS)
- {
- int a = riscv_type_alignment (TYPE_FIELD_TYPE (t, i));
- if (a > align)
- align = a;
- }
- }
- return align;
- }
- }
+ /* Anything else will be aligned by the generic code. */
+ return 0;
}
/* Holds information about a single argument either being passed to an
: int_regs (RISCV_A0_REGNUM, RISCV_A0_REGNUM + 7),
float_regs (RISCV_FA0_REGNUM, RISCV_FA0_REGNUM + 7)
{
- xlen = riscv_isa_xlen (gdbarch);
- flen = riscv_isa_flen (gdbarch);
+ xlen = riscv_abi_xlen (gdbarch);
+ flen = riscv_abi_flen (gdbarch);
/* Disable use of floating point registers if needed. */
if (!riscv_has_fp_abi (gdbarch))
struct riscv_arg_reg float_regs;
/* The XLEN and FLEN are copied in to this structure for convenience, and
- are just the results of calling RISCV_ISA_XLEN and RISCV_ISA_FLEN. */
+ are just the results of calling RISCV_ABI_XLEN and RISCV_ABI_FLEN. */
int xlen;
int flen;
};
riscv_call_arg_scalar_float (struct riscv_arg_info *ainfo,
struct riscv_call_info *cinfo)
{
- if (ainfo->length > cinfo->flen)
+ if (ainfo->length > cinfo->flen || ainfo->is_unnamed)
return riscv_call_arg_scalar_int (ainfo, cinfo);
else
{
struct riscv_call_info *cinfo)
{
if (ainfo->length <= (2 * cinfo->flen)
- && riscv_arg_regs_available (&cinfo->float_regs) >= 2)
+ && riscv_arg_regs_available (&cinfo->float_regs) >= 2
+ && !ainfo->is_unnamed)
{
bool result;
int len = ainfo->length / 2;
result = riscv_assign_reg_location (&ainfo->argloc[0],
- &cinfo->float_regs, len, len);
+ &cinfo->float_regs, len, 0);
gdb_assert (result);
result = riscv_assign_reg_location (&ainfo->argloc[1],
public:
riscv_struct_info ()
: m_number_of_fields (0),
- m_types { nullptr, nullptr }
+ m_types { nullptr, nullptr },
+ m_offsets { 0, 0 }
{
/* Nothing. */
}
/* Analyse TYPE descending into nested structures, count the number of
scalar fields and record the types of the first two fields found. */
- void analyse (struct type *type);
+ void analyse (struct type *type)
+ {
+ analyse_inner (type, 0);
+ }
/* The number of scalar fields found in the analysed type. This is
currently only accurate if the value returned is 0, 1, or 2 as the
return m_types[index];
}
+ /* Return the offset of scalar field INDEX within the analysed type. Will
+ return 0 if there is no field at that index. Only INDEX values 0 and
+ 1 can be requested as the RiscV ABI only has special cases for
+ structures with 1 or 2 fields. */
+ int field_offset (int index) const
+ {
+ gdb_assert (index < (sizeof (m_offsets) / sizeof (m_offsets[0])));
+ return m_offsets[index];
+ }
+
private:
/* The number of scalar fields found within the structure after recursing
into nested structures. */
/* The types of the first two scalar fields found within the structure
after recursing into nested structures. */
struct type *m_types[2];
+
+ /* The offsets of the first two scalar fields found within the structure
+ after recursing into nested structures. */
+ int m_offsets[2];
+
+ /* Recursive core for ANALYSE, the OFFSET parameter tracks the byte
+ offset from the start of the top level structure being analysed. */
+ void analyse_inner (struct type *type, int offset);
};
-/* Analyse TYPE descending into nested structures, count the number of
- scalar fields and record the types of the first two fields found. */
+/* See description in class declaration. */
void
-riscv_struct_info::analyse (struct type *type)
+riscv_struct_info::analyse_inner (struct type *type, int offset)
{
unsigned int count = TYPE_NFIELDS (type);
unsigned int i;
struct type *field_type = TYPE_FIELD_TYPE (type, i);
field_type = check_typedef (field_type);
+ int field_offset
+ = offset + TYPE_FIELD_BITPOS (type, i) / TARGET_CHAR_BIT;
switch (TYPE_CODE (field_type))
{
case TYPE_CODE_STRUCT:
- analyse (field_type);
+ analyse_inner (field_type, field_offset);
break;
default:
structure we can special case, and pass the structure in
memory. */
if (m_number_of_fields < 2)
- m_types[m_number_of_fields] = field_type;
+ {
+ m_types[m_number_of_fields] = field_type;
+ m_offsets[m_number_of_fields] = field_offset;
+ }
m_number_of_fields++;
break;
}
if (sinfo.number_of_fields () == 1
&& TYPE_CODE (sinfo.field_type (0)) == TYPE_CODE_COMPLEX)
{
- gdb_assert (TYPE_LENGTH (ainfo->type)
- == TYPE_LENGTH (sinfo.field_type (0)));
- return riscv_call_arg_complex_float (ainfo, cinfo);
+ /* The following is similar to RISCV_CALL_ARG_COMPLEX_FLOAT,
+ except we use the type of the complex field instead of the
+ type from AINFO, and the first location might be at a non-zero
+ offset. */
+ if (TYPE_LENGTH (sinfo.field_type (0)) <= (2 * cinfo->flen)
+ && riscv_arg_regs_available (&cinfo->float_regs) >= 2
+ && !ainfo->is_unnamed)
+ {
+ bool result;
+ int len = TYPE_LENGTH (sinfo.field_type (0)) / 2;
+ int offset = sinfo.field_offset (0);
+
+ result = riscv_assign_reg_location (&ainfo->argloc[0],
+ &cinfo->float_regs, len,
+ offset);
+ gdb_assert (result);
+
+ result = riscv_assign_reg_location (&ainfo->argloc[1],
+ &cinfo->float_regs, len,
+ (offset + len));
+ gdb_assert (result);
+ }
+ else
+ riscv_call_arg_scalar_int (ainfo, cinfo);
+ return;
}
if (sinfo.number_of_fields () == 1
&& TYPE_CODE (sinfo.field_type (0)) == TYPE_CODE_FLT)
{
- gdb_assert (TYPE_LENGTH (ainfo->type)
- == TYPE_LENGTH (sinfo.field_type (0)));
- return riscv_call_arg_scalar_float (ainfo, cinfo);
+ /* The following is similar to RISCV_CALL_ARG_SCALAR_FLOAT,
+ except we use the type of the first scalar field instead of
+ the type from AINFO. Also the location might be at a non-zero
+ offset. */
+ if (TYPE_LENGTH (sinfo.field_type (0)) > cinfo->flen
+ || ainfo->is_unnamed)
+ riscv_call_arg_scalar_int (ainfo, cinfo);
+ else
+ {
+ int offset = sinfo.field_offset (0);
+ int len = TYPE_LENGTH (sinfo.field_type (0));
+
+ if (!riscv_assign_reg_location (&ainfo->argloc[0],
+ &cinfo->float_regs,
+ len, offset))
+ riscv_call_arg_scalar_int (ainfo, cinfo);
+ }
+ return;
}
if (sinfo.number_of_fields () == 2
&& TYPE_LENGTH (sinfo.field_type (1)) <= cinfo->flen
&& riscv_arg_regs_available (&cinfo->float_regs) >= 2)
{
- int len0, len1, offset;
-
- gdb_assert (TYPE_LENGTH (ainfo->type) <= (2 * cinfo->flen));
-
- len0 = TYPE_LENGTH (sinfo.field_type (0));
+ int len0 = TYPE_LENGTH (sinfo.field_type (0));
+ int offset = sinfo.field_offset (0);
if (!riscv_assign_reg_location (&ainfo->argloc[0],
- &cinfo->float_regs, len0, 0))
+ &cinfo->float_regs, len0, offset))
error (_("failed during argument setup"));
- len1 = TYPE_LENGTH (sinfo.field_type (1));
- offset = align_up (len0, riscv_type_alignment (sinfo.field_type (1)));
+ int len1 = TYPE_LENGTH (sinfo.field_type (1));
+ offset = sinfo.field_offset (1);
gdb_assert (len1 <= (TYPE_LENGTH (ainfo->type)
- TYPE_LENGTH (sinfo.field_type (0))));
&& is_integral_type (sinfo.field_type (1))
&& TYPE_LENGTH (sinfo.field_type (1)) <= cinfo->xlen))
{
- int len0, len1, offset;
-
- gdb_assert (TYPE_LENGTH (ainfo->type)
- <= (cinfo->flen + cinfo->xlen));
-
- len0 = TYPE_LENGTH (sinfo.field_type (0));
+ int len0 = TYPE_LENGTH (sinfo.field_type (0));
+ int offset = sinfo.field_offset (0);
if (!riscv_assign_reg_location (&ainfo->argloc[0],
- &cinfo->float_regs, len0, 0))
+ &cinfo->float_regs, len0, offset))
error (_("failed during argument setup"));
- len1 = TYPE_LENGTH (sinfo.field_type (1));
- offset = align_up (len0, riscv_type_alignment (sinfo.field_type (1)));
+ int len1 = TYPE_LENGTH (sinfo.field_type (1));
+ offset = sinfo.field_offset (1);
gdb_assert (len1 <= cinfo->xlen);
if (!riscv_assign_reg_location (&ainfo->argloc[1],
&cinfo->int_regs, len1, offset))
&& TYPE_CODE (sinfo.field_type (1)) == TYPE_CODE_FLT
&& TYPE_LENGTH (sinfo.field_type (1)) <= cinfo->flen))
{
- int len0, len1, offset;
-
- gdb_assert (TYPE_LENGTH (ainfo->type)
- <= (cinfo->flen + cinfo->xlen));
-
- len0 = TYPE_LENGTH (sinfo.field_type (0));
- len1 = TYPE_LENGTH (sinfo.field_type (1));
- offset = align_up (len0, riscv_type_alignment (sinfo.field_type (1)));
+ int len0 = TYPE_LENGTH (sinfo.field_type (0));
+ int len1 = TYPE_LENGTH (sinfo.field_type (1));
gdb_assert (len0 <= cinfo->xlen);
gdb_assert (len1 <= cinfo->flen);
+ int offset = sinfo.field_offset (0);
if (!riscv_assign_reg_location (&ainfo->argloc[0],
- &cinfo->int_regs, len0, 0))
+ &cinfo->int_regs, len0, offset))
error (_("failed during argument setup"));
+ offset = sinfo.field_offset (1);
if (!riscv_assign_reg_location (&ainfo->argloc[1],
&cinfo->float_regs,
len1, offset))
/* Non of the structure flattening cases apply, so we just pass using
the integer ABI. */
- ainfo->length = align_up (ainfo->length, cinfo->xlen);
riscv_call_arg_scalar_int (ainfo, cinfo);
}
{
ainfo->type = type;
ainfo->length = TYPE_LENGTH (ainfo->type);
- ainfo->align = riscv_type_alignment (ainfo->type);
+ ainfo->align = type_align (ainfo->type);
ainfo->is_unnamed = is_unnamed;
ainfo->contents = nullptr;
+ ainfo->argloc[0].c_length = 0;
+ ainfo->argloc[1].c_length = 0;
switch (TYPE_CODE (ainfo->type))
{
}
/* Recalculate the alignment requirement. */
- ainfo->align = riscv_type_alignment (ainfo->type);
+ ainfo->align = type_align (ainfo->type);
riscv_call_arg_scalar_int (ainfo, cinfo);
break;
memset (tmp, -1, sizeof (tmp));
else
memset (tmp, 0, sizeof (tmp));
- memcpy (tmp, info->contents, info->argloc[0].c_length);
+ memcpy (tmp, (info->contents + info->argloc[0].c_offset),
+ info->argloc[0].c_length);
regcache->cooked_write (info->argloc[0].loc_data.regno, tmp);
second_arg_length =
- ((info->argloc[0].c_length < info->length)
+ (((info->argloc[0].c_length + info->argloc[0].c_offset) < info->length)
? info->argloc[1].c_length : 0);
second_arg_data = info->contents + info->argloc[1].c_offset;
}
if (readbuf != nullptr || writebuf != nullptr)
{
- int regnum;
+ unsigned int arg_len;
+ struct value *abi_val;
+ gdb_byte *old_readbuf = nullptr;
+ int regnum;
+
+ /* We only do one thing at a time. */
+ gdb_assert (readbuf == nullptr || writebuf == nullptr);
+
+ /* In some cases the argument is not returned as the declared type,
+ and we need to cast to or from the ABI type in order to
+ correctly access the argument. When writing to the machine we
+ do the cast here, when reading from the machine the cast occurs
+ later, after extracting the value. As the ABI type can be
+ larger than the declared type, then the read or write buffers
+ passed in might be too small. Here we ensure that we are using
+ buffers of sufficient size. */
+ if (writebuf != nullptr)
+ {
+ struct value *arg_val = value_from_contents (arg_type, writebuf);
+ abi_val = value_cast (info.type, arg_val);
+ writebuf = value_contents_raw (abi_val);
+ }
+ else
+ {
+ abi_val = allocate_value (info.type);
+ old_readbuf = readbuf;
+ readbuf = value_contents_raw (abi_val);
+ }
+ arg_len = TYPE_LENGTH (info.type);
switch (info.argloc[0].loc_type)
{
case riscv_arg_info::location::in_reg:
{
regnum = info.argloc[0].loc_data.regno;
+ gdb_assert (info.argloc[0].c_length <= arg_len);
+ gdb_assert (info.argloc[0].c_length
+ <= register_size (gdbarch, regnum));
if (readbuf)
- regcache->cooked_read (regnum, readbuf);
+ {
+ gdb_byte *ptr = readbuf + info.argloc[0].c_offset;
+ regcache->cooked_read_part (regnum, 0,
+ info.argloc[0].c_length,
+ ptr);
+ }
if (writebuf)
- regcache->cooked_write (regnum, writebuf);
+ {
+ const gdb_byte *ptr = writebuf + info.argloc[0].c_offset;
+ regcache->cooked_write_part (regnum, 0,
+ info.argloc[0].c_length,
+ ptr);
+ }
/* A return value in register can have a second part in a
second register. */
- if (info.argloc[0].c_length < info.length)
+ if (info.argloc[1].c_length > 0)
{
switch (info.argloc[1].loc_type)
{
case riscv_arg_info::location::in_reg:
regnum = info.argloc[1].loc_data.regno;
+ gdb_assert ((info.argloc[0].c_length
+ + info.argloc[1].c_length) <= arg_len);
+ gdb_assert (info.argloc[1].c_length
+ <= register_size (gdbarch, regnum));
+
if (readbuf)
{
readbuf += info.argloc[1].c_offset;
- regcache->cooked_read (regnum, readbuf);
+ regcache->cooked_read_part (regnum, 0,
+ info.argloc[1].c_length,
+ readbuf);
}
if (writebuf)
{
writebuf += info.argloc[1].c_offset;
- regcache->cooked_write (regnum, writebuf);
+ regcache->cooked_write_part (regnum, 0,
+ info.argloc[1].c_length,
+ writebuf);
}
break;
error (_("invalid argument location"));
break;
}
+
+ /* This completes the cast from abi type back to the declared type
+ in the case that we are reading from the machine. See the
+ comment at the head of this block for more details. */
+ if (readbuf != nullptr)
+ {
+ struct value *arg_val = value_cast (arg_type, abi_val);
+ memcpy (old_readbuf, value_contents_raw (arg_val),
+ TYPE_LENGTH (arg_type));
+ }
}
switch (info.argloc[0].loc_type)
return align_down (addr, 16);
}
-/* Implement the unwind_pc gdbarch method. */
-
-static CORE_ADDR
-riscv_unwind_pc (struct gdbarch *gdbarch, struct frame_info *next_frame)
-{
- return frame_unwind_register_unsigned (next_frame, RISCV_PC_REGNUM);
-}
-
-/* Implement the unwind_sp gdbarch method. */
-
-static CORE_ADDR
-riscv_unwind_sp (struct gdbarch *gdbarch, struct frame_info *next_frame)
-{
- return frame_unwind_register_unsigned (next_frame, RISCV_SP_REGNUM);
-}
-
-/* Implement the dummy_id gdbarch method. */
-
-static struct frame_id
-riscv_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
-{
- return frame_id_build (get_frame_register_signed (this_frame, RISCV_SP_REGNUM),
- get_frame_pc (this_frame));
-}
-
/* Generate, or return the cached frame cache for the RiscV frame
unwinder. */
{
struct riscv_unwind_cache *cache;
- TRY
+ try
{
cache = riscv_frame_cache (this_frame, prologue_cache);
*this_id = cache->this_id;
}
- CATCH (ex, RETURN_MASK_ERROR)
+ catch (const gdb_exception_error &ex)
{
/* Ignore errors, this leaves the frame id as the predefined outer
frame id which terminates the backtrace at this point. */
}
- END_CATCH
}
/* Implement the prev_register callback for RiscV frame unwinder. */
/*.prev_arch =*/ NULL,
};
-/* Find a suitable default target description. Use the contents of INFO,
- specifically the bfd object being executed, to guide the selection of a
- suitable default target description. */
+/* Extract a set of required target features out of INFO, specifically the
+ bfd being executed is examined to see what target features it requires.
+ IF there is no current bfd, or the bfd doesn't indicate any useful
+ features then a RISCV_GDBARCH_FEATURES is returned in its default state. */
-static const struct target_desc *
-riscv_find_default_target_description (const struct gdbarch_info info)
+static struct riscv_gdbarch_features
+riscv_features_from_gdbarch_info (const struct gdbarch_info info)
{
struct riscv_gdbarch_features features;
- /* Setup some arbitrary defaults. */
- features.xlen = 8;
- features.flen = 0;
- features.hw_float_abi = false;
-
/* Now try to improve on the defaults by looking at the binary we are
going to execute. We assume the user knows what they are doing and
that the target will match the binary. Remember, this code path is
_("unknown ELF header class %d"), eclass);
if (e_flags & EF_RISCV_FLOAT_ABI_DOUBLE)
- {
- features.flen = 8;
- features.hw_float_abi = true;
- }
+ features.flen = 8;
else if (e_flags & EF_RISCV_FLOAT_ABI_SINGLE)
- {
- features.flen = 4;
- features.hw_float_abi = true;
- }
+ features.flen = 4;
}
- else
- {
- const struct bfd_arch_info *binfo = info.bfd_arch_info;
- if (binfo->bits_per_word == 32)
- features.xlen = 4;
- else if (binfo->bits_per_word == 64)
- features.xlen = 8;
- else
- internal_error (__FILE__, __LINE__, _("unknown bits_per_word %d"),
- binfo->bits_per_word);
- }
+ return features;
+}
+
+/* Find a suitable default target description. Use the contents of INFO,
+ specifically the bfd object being executed, to guide the selection of a
+ suitable default target description. */
+
+static const struct target_desc *
+riscv_find_default_target_description (const struct gdbarch_info info)
+{
+ /* Extract desired feature set from INFO. */
+ struct riscv_gdbarch_features features
+ = riscv_features_from_gdbarch_info (info);
+
+ /* If the XLEN field is still 0 then we got nothing useful from INFO. In
+ this case we fall back to a minimal useful target, 8-byte x-registers,
+ with no floating point. */
+ if (features.xlen == 0)
+ features.xlen = 8;
/* Now build a target description based on the feature set. */
return riscv_create_target_description (features);
}
}
+/* Implement the "dwarf2_reg_to_regnum" gdbarch method. */
+
+static int
+riscv_dwarf_reg_to_regnum (struct gdbarch *gdbarch, int reg)
+{
+ if (reg < RISCV_DWARF_REGNUM_X31)
+ return RISCV_ZERO_REGNUM + (reg - RISCV_DWARF_REGNUM_X0);
+
+ else if (reg < RISCV_DWARF_REGNUM_F31)
+ return RISCV_FIRST_FP_REGNUM + (reg - RISCV_DWARF_REGNUM_F0);
+
+ return -1;
+}
+
+/* Implement the gcc_target_options method. We have to select the arch and abi
+ from the feature info. We have enough feature info to select the abi, but
+ not enough info for the arch given all of the possible architecture
+ extensions. So choose reasonable defaults for now. */
+
+static std::string
+riscv_gcc_target_options (struct gdbarch *gdbarch)
+{
+ int isa_xlen = riscv_isa_xlen (gdbarch);
+ int isa_flen = riscv_isa_flen (gdbarch);
+ int abi_xlen = riscv_abi_xlen (gdbarch);
+ int abi_flen = riscv_abi_flen (gdbarch);
+ std::string target_options;
+
+ target_options = "-march=rv";
+ if (isa_xlen == 8)
+ target_options += "64";
+ else
+ target_options += "32";
+ if (isa_flen == 8)
+ target_options += "gc";
+ else if (isa_flen == 4)
+ target_options += "imafc";
+ else
+ target_options += "imac";
+
+ target_options += " -mabi=";
+ if (abi_xlen == 8)
+ target_options += "lp64";
+ else
+ target_options += "ilp32";
+ if (abi_flen == 8)
+ target_options += "d";
+ else if (abi_flen == 4)
+ target_options += "f";
+
+ /* The gdb loader doesn't handle link-time relaxation relocations. */
+ target_options += " -mno-relax";
+
+ return target_options;
+}
+
+/* Implement the gnu_triplet_regexp method. A single compiler supports both
+ 32-bit and 64-bit code, and may be named riscv32 or riscv64 or (not
+ recommended) riscv. */
+
+static const char *
+riscv_gnu_triplet_regexp (struct gdbarch *gdbarch)
+{
+ return "riscv(32|64)?";
+}
+
/* Initialize the current architecture based on INFO. If possible,
re-use an architecture from ARCHES, which is a list of
architectures already created during this debugging session.
valid_p &= riscv_check_tdesc_feature (tdesc_data, feature_fpu,
&riscv_freg_feature);
- int bitsize = tdesc_register_bitsize (feature_fpu, "ft0");
+ /* Search for the first floating point register (by any alias), to
+ determine the bitsize. */
+ int bitsize = -1;
+ const auto &fp0 = riscv_freg_feature.registers[0];
+
+ for (const char *name : fp0.names)
+ {
+ if (tdesc_unnumbered_register (feature_fpu, name))
+ {
+ bitsize = tdesc_register_bitsize (feature_fpu, name);
+ break;
+ }
+ }
+
+ gdb_assert (bitsize != -1);
features.flen = (bitsize / 8);
- features.hw_float_abi = true;
if (riscv_debug_gdbarch)
fprintf_filtered
else
{
features.flen = 0;
- features.hw_float_abi = false;
if (riscv_debug_gdbarch)
fprintf_filtered
return NULL;
}
+ /* Have a look at what the supplied (if any) bfd object requires of the
+ target, then check that this matches with what the target is
+ providing. */
+ struct riscv_gdbarch_features abi_features
+ = riscv_features_from_gdbarch_info (info);
+ /* In theory a binary compiled for RV32 could run on an RV64 target,
+ however, this has not been tested in GDB yet, so for now we require
+ that the requested xlen match the targets xlen. */
+ if (abi_features.xlen != 0 && abi_features.xlen != features.xlen)
+ error (_("bfd requires xlen %d, but target has xlen %d"),
+ abi_features.xlen, features.xlen);
+ /* We do support running binaries compiled for 32-bit float on targets
+ with 64-bit float, so we only complain if the binary requires more
+ than the target has available. */
+ if (abi_features.flen > features.flen)
+ error (_("bfd requires flen %d, but target has flen %d"),
+ abi_features.flen, features.flen);
+
+ /* If the ABI_FEATURES xlen is 0 then this indicates we got no useful abi
+ features from the INFO object. In this case we assume that the xlen
+ abi matches the hardware. */
+ if (abi_features.xlen == 0)
+ abi_features.xlen = features.xlen;
+
/* Find a candidate among the list of pre-declared architectures. */
for (arches = gdbarch_list_lookup_by_info (arches, &info);
arches != NULL;
gdbarch. */
struct gdbarch_tdep *other_tdep = gdbarch_tdep (arches->gdbarch);
- if (other_tdep->features != features)
+ if (other_tdep->isa_features != features
+ || other_tdep->abi_features != abi_features)
continue;
break;
/* None found, so create a new architecture from the information provided. */
tdep = new (struct gdbarch_tdep);
gdbarch = gdbarch_alloc (&info, tdep);
- tdep->features = features;
+ tdep->isa_features = features;
+ tdep->abi_features = abi_features;
/* Target data types. */
set_gdbarch_short_bit (gdbarch, 16);
set_gdbarch_long_double_format (gdbarch, floatformats_ia64_quad);
set_gdbarch_ptr_bit (gdbarch, riscv_isa_xlen (gdbarch) * 8);
set_gdbarch_char_signed (gdbarch, 0);
+ set_gdbarch_type_align (gdbarch, riscv_type_align);
/* Information about the target architecture. */
set_gdbarch_return_value (gdbarch, riscv_return_value);
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
set_gdbarch_frame_align (gdbarch, riscv_frame_align);
- /* Functions to access frame data. */
- set_gdbarch_unwind_pc (gdbarch, riscv_unwind_pc);
- set_gdbarch_unwind_sp (gdbarch, riscv_unwind_sp);
-
/* Functions handling dummy frames. */
set_gdbarch_call_dummy_location (gdbarch, ON_STACK);
set_gdbarch_push_dummy_code (gdbarch, riscv_push_dummy_code);
set_gdbarch_push_dummy_call (gdbarch, riscv_push_dummy_call);
- set_gdbarch_dummy_id (gdbarch, riscv_dummy_id);
/* Frame unwinders. Use DWARF debug info if available, otherwise use our own
unwinder. */
/* Register architecture. */
riscv_add_reggroups (gdbarch);
+ /* Internal <-> external register number maps. */
+ set_gdbarch_dwarf2_reg_to_regnum (gdbarch, riscv_dwarf_reg_to_regnum);
+
/* We reserve all possible register numbers for the known registers.
This means the target description mechanism will add any target
specific registers after this number. This helps make debugging GDB
riscv_setup_register_aliases (gdbarch, &riscv_freg_feature);
riscv_setup_register_aliases (gdbarch, &riscv_csr_feature);
+ /* Compile command hooks. */
+ set_gdbarch_gcc_target_options (gdbarch, riscv_gcc_target_options);
+ set_gdbarch_gnu_triplet_regexp (gdbarch, riscv_gnu_triplet_regexp);
+
/* Hook in OS ABI-specific overrides, if they have been registered. */
gdbarch_init_osabi (info, gdbarch);
+ register_riscv_ravenscar_ops (gdbarch);
+
return gdbarch;
}