This commit adds floating-point support for LoongArch gdb.
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
#include "../features/loongarch/base32.c"
#include "../features/loongarch/base64.c"
+#include "../features/loongarch/fpu.c"
#ifndef GDBSERVER
#define STATIC_IN_GDB static
else if (features.xlen == 8)
arch_name.append ("64");
+ if (features.fputype == SINGLE_FLOAT)
+ arch_name.append ("f");
+ else if (features.fputype == DOUBLE_FLOAT)
+ arch_name.append ("d");
+
set_tdesc_architecture (tdesc.get (), arch_name.c_str ());
long regnum = 0;
else if (features.xlen == 8)
regnum = create_feature_loongarch_base64 (tdesc.get (), regnum);
+ /* For now we only support creating single float and double float. */
+ regnum = create_feature_loongarch_fpu (tdesc.get (), regnum);
+
return tdesc;
}
#include "gdbsupport/tdesc.h"
/* Register numbers of various important registers. */
-enum
+enum loongarch_regnum
{
LOONGARCH_RA_REGNUM = 1, /* Return Address. */
LOONGARCH_SP_REGNUM = 3, /* Stack Pointer. */
LOONGARCH_LINUX_NUM_GREGSET = 45, /* 32 GPR, ORIG_A0, PC, BADV, RESERVED 10. */
LOONGARCH_ARG_REGNUM = 8, /* r4-r11: general-purpose argument registers.
f0-f7: floating-point argument registers. */
+ LOONGARCH_FIRST_FP_REGNUM = LOONGARCH_LINUX_NUM_GREGSET,
+ LOONGARCH_FCC_REGNUM = LOONGARCH_FIRST_FP_REGNUM + 32,
+ LOONGARCH_FCSR_REGNUM = LOONGARCH_FCC_REGNUM + 1,
+ LOONGARCH_LINUX_NUM_FPREGSET = 34,
+};
+
+enum loongarch_fputype
+{
+ SINGLE_FLOAT = 1,
+ DOUBLE_FLOAT = 2,
};
/* The set of LoongArch architectural features that we track that impact how
0 value so we can spot if one of these is used uninitialised. */
int xlen = 0;
+ /* The type of floating-point. This is either 1 (single float) or 2
+ (double float). No other value is valid. Initialise to the invalid
+ 0 value so we can spot if one of these is used uninitialised. */
+ int fputype = 0;
+
/* Equality operator. */
bool operator== (const struct loongarch_gdbarch_features &rhs) const
{
i386/x32-core.xml \
loongarch/base32.xml \
loongarch/base64.xml \
+ loongarch/fpu.xml \
riscv/rv32e-xregs.xml \
riscv/32bit-cpu.xml \
riscv/32bit-fpu.xml \
--- /dev/null
+/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
+ Original: fpu.xml */
+
+#include "gdbsupport/tdesc.h"
+
+static int
+create_feature_loongarch_fpu (struct target_desc *result, long regnum)
+{
+ struct tdesc_feature *feature;
+
+ feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.fpu");
+ tdesc_type_with_fields *type_with_fields;
+ type_with_fields = tdesc_create_union (feature, "fputype");
+ tdesc_type *field_type;
+ field_type = tdesc_named_type (feature, "ieee_single");
+ tdesc_add_field (type_with_fields, "f", field_type);
+ field_type = tdesc_named_type (feature, "ieee_double");
+ tdesc_add_field (type_with_fields, "d", field_type);
+
+ tdesc_create_reg (feature, "f0", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f1", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f2", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f3", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f4", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f5", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f6", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f7", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f8", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f9", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f10", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f11", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f12", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f13", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f14", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f15", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f16", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f17", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f18", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f19", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f20", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f21", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f22", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f23", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f24", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f25", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f26", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f27", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f28", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f29", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f30", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "f31", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "fcc", regnum++, 1, "float", 64, "fputype");
+ tdesc_create_reg (feature, "fcsr", regnum++, 1, "float", 32, "uint32");
+ return regnum;
+}
--- /dev/null
+<?xml version="1.0"?>
+<!-- Copyright (C) 2021 Free Software Foundation, Inc.
+
+ Copying and distribution of this file, with or without modification,
+ are permitted in any medium without royalty provided the copyright
+ notice and this notice are preserved. -->
+
+<!DOCTYPE feature SYSTEM "gdb-target.dtd">
+<feature name="org.gnu.gdb.loongarch.fpu">
+
+ <union id="fputype">
+ <field name="f" type="ieee_single"/>
+ <field name="d" type="ieee_double"/>
+ </union>
+
+ <reg name="f0" bitsize="64" type="fputype" group="float"/>
+ <reg name="f1" bitsize="64" type="fputype" group="float"/>
+ <reg name="f2" bitsize="64" type="fputype" group="float"/>
+ <reg name="f3" bitsize="64" type="fputype" group="float"/>
+ <reg name="f4" bitsize="64" type="fputype" group="float"/>
+ <reg name="f5" bitsize="64" type="fputype" group="float"/>
+ <reg name="f6" bitsize="64" type="fputype" group="float"/>
+ <reg name="f7" bitsize="64" type="fputype" group="float"/>
+ <reg name="f8" bitsize="64" type="fputype" group="float"/>
+ <reg name="f9" bitsize="64" type="fputype" group="float"/>
+ <reg name="f10" bitsize="64" type="fputype" group="float"/>
+ <reg name="f11" bitsize="64" type="fputype" group="float"/>
+ <reg name="f12" bitsize="64" type="fputype" group="float"/>
+ <reg name="f13" bitsize="64" type="fputype" group="float"/>
+ <reg name="f14" bitsize="64" type="fputype" group="float"/>
+ <reg name="f15" bitsize="64" type="fputype" group="float"/>
+ <reg name="f16" bitsize="64" type="fputype" group="float"/>
+ <reg name="f17" bitsize="64" type="fputype" group="float"/>
+ <reg name="f18" bitsize="64" type="fputype" group="float"/>
+ <reg name="f19" bitsize="64" type="fputype" group="float"/>
+ <reg name="f20" bitsize="64" type="fputype" group="float"/>
+ <reg name="f21" bitsize="64" type="fputype" group="float"/>
+ <reg name="f22" bitsize="64" type="fputype" group="float"/>
+ <reg name="f23" bitsize="64" type="fputype" group="float"/>
+ <reg name="f24" bitsize="64" type="fputype" group="float"/>
+ <reg name="f25" bitsize="64" type="fputype" group="float"/>
+ <reg name="f26" bitsize="64" type="fputype" group="float"/>
+ <reg name="f27" bitsize="64" type="fputype" group="float"/>
+ <reg name="f28" bitsize="64" type="fputype" group="float"/>
+ <reg name="f29" bitsize="64" type="fputype" group="float"/>
+ <reg name="f30" bitsize="64" type="fputype" group="float"/>
+ <reg name="f31" bitsize="64" type="fputype" group="float"/>
+ <reg name="fcc" bitsize="64" type="fputype" group="float"/>
+ <reg name="fcsr" bitsize="32" type="uint32" group="float"/>
+</feature>
}
}
+/* Fill GDB's register array with the fp, fcc and fcsr
+ register values from the current thread. */
+
+static void
+fetch_fpregs_from_thread (struct regcache *regcache, int regnum, pid_t tid)
+{
+ elf_fpregset_t regset;
+
+ if ((regnum == -1)
+ || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM))
+ {
+ struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get NT_FPREGSET registers"));
+ else
+ loongarch_fpregset.supply_regset (nullptr, regcache, regnum,
+ ®set, sizeof (regset));
+ }
+}
+
+/* Store to the current thread the valid fp, fcc and fcsr
+ register values in the GDB's register array. */
+
+static void
+store_fpregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
+{
+ elf_fpregset_t regset;
+
+ if ((regnum == -1)
+ || (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM))
+ {
+ struct iovec iovec = { .iov_base = ®set, .iov_len = sizeof (regset) };
+
+ if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't get NT_FPREGSET registers"));
+ else
+ {
+ loongarch_fpregset.collect_regset (nullptr, regcache, regnum,
+ ®set, sizeof (regset));
+ if (ptrace (PTRACE_SETREGSET, tid, NT_FPREGSET, (long) &iovec) < 0)
+ perror_with_name (_("Couldn't set NT_FPREGSET registers"));
+ }
+ }
+}
+
/* Implement the "fetch_registers" target_ops method. */
void
pid_t tid = get_ptrace_pid (regcache->ptid ());
fetch_gregs_from_thread(regcache, regnum, tid);
+ fetch_fpregs_from_thread(regcache, regnum, tid);
}
/* Implement the "store_registers" target_ops method. */
pid_t tid = get_ptrace_pid (regcache->ptid ());
store_gregs_to_thread (regcache, regnum, tid);
+ store_fpregs_to_thread(regcache, regnum, tid);
}
/* Return the address in the core dump or inferior of register REGNO. */
void
supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregset)
{
+ loongarch_fpregset.supply_regset (nullptr, regcache, -1, fpregset,
+ sizeof (gdb_fpregset_t));
}
void
fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregset,
int regnum)
{
+ loongarch_fpregset.collect_regset (nullptr, regcache, regnum, fpregset,
+ sizeof (gdb_fpregset_t));
}
/* Initialize LoongArch Linux native support. */
}
}
-/* Register set definitions. */
+/* Define the general register regset. */
const struct regset loongarch_gregset =
{
loongarch_fill_gregset,
};
+/* Unpack an elf_fpregset_t into GDB's register cache. */
+static void
+loongarch_supply_fpregset (const struct regset *r,
+ struct regcache *regcache, int regnum,
+ const void *fprs, size_t len)
+{
+ const gdb_byte *buf = nullptr;
+ int fprsize = register_size (regcache->arch (), LOONGARCH_FIRST_FP_REGNUM);
+
+ if (regnum == -1)
+ {
+ for (int i = 0; i < LOONGARCH_LINUX_NUM_FPREGSET; i++)
+ {
+ buf = (const gdb_byte *)fprs + fprsize * i;
+ regcache->raw_supply (LOONGARCH_FIRST_FP_REGNUM + i, (const void *)buf);
+ }
+ }
+ else if (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM)
+ {
+ buf = (const gdb_byte *)fprs + fprsize * (regnum - LOONGARCH_FIRST_FP_REGNUM);
+ regcache->raw_supply (regnum, (const void *)buf);
+ }
+}
+
+/* Pack the GDB's register cache value into an elf_fpregset_t. */
+static void
+loongarch_fill_fpregset (const struct regset *r,
+ const struct regcache *regcache, int regnum,
+ void *fprs, size_t len)
+{
+ gdb_byte *buf = nullptr;
+ int fprsize = register_size (regcache->arch (), LOONGARCH_FIRST_FP_REGNUM);
+
+ if (regnum == -1)
+ {
+ for (int i = 0; i < LOONGARCH_LINUX_NUM_FPREGSET; i++)
+ {
+ buf = (gdb_byte *)fprs + fprsize * i;
+ regcache->raw_collect (LOONGARCH_FIRST_FP_REGNUM + i, (void *)buf);
+ }
+ }
+ else if (regnum >= LOONGARCH_FIRST_FP_REGNUM && regnum <= LOONGARCH_FCSR_REGNUM)
+ {
+ buf = (gdb_byte *)fprs + fprsize * (regnum - LOONGARCH_FIRST_FP_REGNUM);
+ regcache->raw_collect (regnum, (void *)buf);
+ }
+}
+
+/* Define the FP register regset. */
+const struct regset loongarch_fpregset =
+{
+ nullptr,
+ loongarch_supply_fpregset,
+ loongarch_fill_fpregset,
+};
+
/* Implement the "init" method of struct tramp_frame. */
#define LOONGARCH_RT_SIGFRAME_UCONTEXT_OFFSET 128
{ TRAMP_SENTINEL_INSN, ULONGEST_MAX }
},
loongarch_linux_rt_sigframe_init,
- NULL
+ nullptr
};
/* Implement the "iterate_over_regset_sections" gdbarch method. */
void *cb_data,
const struct regcache *regcache)
{
- int regsize = register_size (gdbarch, 0);
+ int gprsize = register_size (gdbarch, 0);
+ int fprsize = register_size (gdbarch, LOONGARCH_FIRST_FP_REGNUM);
- cb (".reg", LOONGARCH_LINUX_NUM_GREGSET * regsize,
- LOONGARCH_LINUX_NUM_GREGSET * regsize, &loongarch_gregset, NULL, cb_data);
+ cb (".reg", LOONGARCH_LINUX_NUM_GREGSET * gprsize,
+ LOONGARCH_LINUX_NUM_GREGSET * gprsize, &loongarch_gregset, nullptr, cb_data);
+ cb (".reg2", LOONGARCH_LINUX_NUM_FPREGSET * fprsize,
+ LOONGARCH_LINUX_NUM_FPREGSET * fprsize, &loongarch_fpregset, nullptr, cb_data);
}
/* The following value is derived from __NR_rt_sigreturn in
static void
pass_in_far (struct regcache *regcache, unsigned int far, const gdb_byte *val)
{
- unsigned int regnum = LOONGARCH_ARG_REGNUM - far + LOONGARCH_BADV_REGNUM + 1;
+ unsigned int regnum = LOONGARCH_ARG_REGNUM - far + LOONGARCH_FIRST_FP_REGNUM;
regcache->cooked_write (regnum, val);
}
{
if (regnum >= 0 && regnum < 32)
return regnum;
+ else if (regnum >= 32 && regnum < 66)
+ return LOONGARCH_FIRST_FP_REGNUM + regnum - 32;
else
return -1;
}
if (abfd != nullptr && bfd_get_flavour (abfd) == bfd_target_elf_flavour)
{
unsigned char eclass = elf_elfheader (abfd)->e_ident[EI_CLASS];
+ int e_flags = elf_elfheader (abfd)->e_flags;
if (eclass == ELFCLASS32)
features.xlen = 4;
else
internal_error (__FILE__, __LINE__,
_("unknown ELF header class %d"), eclass);
+
+ if (EF_LOONGARCH_IS_SINGLE_FLOAT (e_flags))
+ features.fputype = SINGLE_FLOAT;
+ else if (EF_LOONGARCH_IS_DOUBLE_FLOAT (e_flags))
+ features.fputype = DOUBLE_FLOAT;
}
return features;
/* If the XLEN field is still 0 then we got nothing useful from INFO.BFD,
maybe there was no bfd object. In this case we fall back to a minimal
- useful target with no floating point, the x-register size is selected
- based on the architecture from INFO. */
+ useful target, the x-register size is selected based on the architecture
+ from INFO. */
if (features.xlen == 0)
features.xlen = info.bfd_arch_info->bits_per_address == 32 ? 4 : 8;
+ /* If the FPUTYPE field is still 0 then we got nothing useful from INFO.BFD,
+ maybe there was no bfd object. In this case we fall back to a usual useful
+ target with double float. */
+ if (features.fputype == 0)
+ features.fputype = DOUBLE_FLOAT;
+
/* Now build a target description based on the feature set. */
return loongarch_lookup_target_description (features);
}
static struct gdbarch *
loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
{
+ size_t regnum = 0;
+ struct loongarch_gdbarch_features features;
+ tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
+ loongarch_gdbarch_tdep *tdep = new loongarch_gdbarch_tdep;
const struct target_desc *tdesc = info.target_desc;
/* Ensure we always have a target description. */
if (feature_cpu == nullptr)
return nullptr;
- int xlen_bitsize = tdesc_register_bitsize (feature_cpu, "pc");
- struct loongarch_gdbarch_features features;
- features.xlen = (xlen_bitsize / 8);
-
- size_t regnum = 0;
- tdesc_arch_data_up tdesc_data = tdesc_data_alloc ();
- loongarch_gdbarch_tdep *tdep = new loongarch_gdbarch_tdep;
/* Validate the description provides the mandatory base registers
and allocate their numbers. */
if (!valid_p)
return nullptr;
+ const struct tdesc_feature *feature_fpu
+ = tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.fpu");
+ if (feature_fpu == nullptr)
+ return nullptr;
+
+ /* Validate the description provides the fpu registers and
+ allocate their numbers. */
+ regnum = LOONGARCH_FIRST_FP_REGNUM;
+ for (int i = 0; i < 32; i++)
+ valid_p &= tdesc_numbered_register (feature_fpu, tdesc_data.get (), regnum++,
+ loongarch_f_normal_name[i] + 1);
+ valid_p &= tdesc_numbered_register (feature_fpu, tdesc_data.get (), regnum++, "fcc");
+ valid_p &= tdesc_numbered_register (feature_fpu, tdesc_data.get (), regnum++, "fcsr");
+ if (!valid_p)
+ return nullptr;
+
/* LoongArch code is always little-endian. */
info.byte_order_for_code = BFD_ENDIAN_LITTLE;
struct loongarch_gdbarch_features abi_features
= loongarch_features_from_bfd (info.abfd);
- /* If the ABI_FEATURES xlen is 0 then this indicates we got no useful abi
- features from the INFO object. In this case we just treat the
- hardware features as defining the abi. */
+ /* If the ABI_FEATURES xlen or fputype is 0 then this indicates we got
+ no useful abi features from the INFO object. In this case we just
+ treat the hardware features as defining the abi. */
if (abi_features.xlen == 0)
- abi_features = features;
+ {
+ int xlen_bitsize = tdesc_register_bitsize (feature_cpu, "pc");
+ features.xlen = (xlen_bitsize / 8);
+ features.fputype = abi_features.fputype;
+ abi_features = features;
+ }
+ if (abi_features.fputype == 0)
+ {
+ features.xlen = abi_features.xlen;
+ features.fputype = DOUBLE_FLOAT;
+ abi_features = features;
+ }
/* Find a candidate among the list of pre-declared architectures. */
for (arches = gdbarch_list_lookup_by_info (arches, &info);
/* Register set definitions. */
extern const struct regset loongarch_gregset;
+extern const struct regset loongarch_fpregset;
/* Target-dependent structure in gdbarch. */
struct loongarch_gdbarch_tdep : gdbarch_tdep