gdb: LoongArch: Add LBT extension support

Loongson Binary Translation (LBT) is used to accelerate binary
translation, which contains 4 scratch registers (scr0 to scr3),
x86/ARM eflags (eflags) and x87 fpu stack pointer (ftop). This
patch support gdb to fetch/store these registers.

Signed-off-by: Feiyang Chen <chenfeiyang@loongson.cn> #  Framework
Signed-off-by: Binbin Zhou <zhoubinbin@loongson.cn>   #  Detail Optimizes
Signed-off-by: Hui Li <lihui@loongson.cn>             #  Error Fixes
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
This commit is contained in:
Feiyang Chen 2024-01-25 16:32:36 +08:00 committed by Tiezhu Yang
parent 1e9569f383
commit e4d74c01e7
10 changed files with 283 additions and 0 deletions

View File

@ -27,6 +27,7 @@
#include "../features/loongarch/fpu.c"
#include "../features/loongarch/lsx.c"
#include "../features/loongarch/lasx.c"
#include "../features/loongarch/lbt.c"
#ifndef GDBSERVER
#define STATIC_IN_GDB static
@ -69,6 +70,9 @@ loongarch_create_target_description (const struct loongarch_gdbarch_features fea
regnum = create_feature_loongarch_lsx (tdesc.get (), regnum);
regnum = create_feature_loongarch_lasx (tdesc.get (), regnum);
/* For now we only support creating scr registers, eflags and ftop. */
regnum = create_feature_loongarch_lbt (tdesc.get (), regnum);
return tdesc;
}

View File

@ -45,6 +45,12 @@ enum loongarch_regnum
LOONGARCH_LINUX_NUM_LSXREGSET = 32,
LOONGARCH_FIRST_LASX_REGNUM = LOONGARCH_FIRST_LSX_REGNUM + LOONGARCH_LINUX_NUM_LSXREGSET,
LOONGARCH_LINUX_NUM_LASXREGSET = 32,
LOONGARCH_FIRST_SCR_REGNUM = LOONGARCH_FIRST_LASX_REGNUM + LOONGARCH_LINUX_NUM_LASXREGSET,
LOONGARCH_LINUX_NUM_SCR = 4,
LOONGARCH_LAST_SCR_REGNUM = LOONGARCH_FIRST_SCR_REGNUM + LOONGARCH_LINUX_NUM_SCR - 1,
LOONGARCH_EFLAGS_REGNUM = LOONGARCH_LAST_SCR_REGNUM + 1,
LOONGARCH_FTOP_REGNUM = LOONGARCH_EFLAGS_REGNUM + 1,
};
enum loongarch_fputype
@ -53,6 +59,8 @@ enum loongarch_fputype
DOUBLE_FLOAT = 2,
};
#define LOONGARCH_LBT_REGS_SIZE (8 * LOONGARCH_LINUX_NUM_SCR + 4 + 4)
/* The set of LoongArch architectural features that we track that impact how
we configure the actual gdbarch instance. We hold one of these in the
gdbarch_tdep structure, and use it to distinguish between different

View File

@ -239,6 +239,7 @@ FEATURE_XMLFILES = aarch64-core.xml \
loongarch/fpu.xml \
loongarch/lsx.xml \
loongarch/lasx.xml \
loongarch/lbt.xml \
riscv/rv32e-xregs.xml \
riscv/32bit-cpu.xml \
riscv/32bit-fpu.xml \

View File

@ -0,0 +1,19 @@
/* THIS FILE IS GENERATED. -*- buffer-read-only: t -*- vi:set ro:
Original: lbt.xml */
#include "gdbsupport/tdesc.h"
static int
create_feature_loongarch_lbt (struct target_desc *result, long regnum)
{
struct tdesc_feature *feature;
feature = tdesc_create_feature (result, "org.gnu.gdb.loongarch.lbt");
tdesc_create_reg (feature, "scr0", regnum++, 1, "lbt", 64, "uint64");
tdesc_create_reg (feature, "scr1", regnum++, 1, "lbt", 64, "uint64");
tdesc_create_reg (feature, "scr2", regnum++, 1, "lbt", 64, "uint64");
tdesc_create_reg (feature, "scr3", regnum++, 1, "lbt", 64, "uint64");
tdesc_create_reg (feature, "eflags", regnum++, 1, "lbt", 32, "uint32");
tdesc_create_reg (feature, "ftop", regnum++, 1, "lbt", 32, "uint32");
return regnum;
}

View File

@ -0,0 +1,16 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2022-2024 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.lbt">
<reg name="scr0" bitsize="64" type="uint64" group="lbt"/>
<reg name="scr1" bitsize="64" type="uint64" group="lbt"/>
<reg name="scr2" bitsize="64" type="uint64" group="lbt"/>
<reg name="scr3" bitsize="64" type="uint64" group="lbt"/>
<reg name="eflags" bitsize="32" type="uint32" group="lbt"/>
<reg name="ftop" bitsize="32" type="uint32" group="lbt"/>
</feature>

View File

@ -265,6 +265,70 @@ store_lasxregs_to_thread (struct regcache *regcache, int regnum, pid_t tid)
}
/* Fill GDB's register array with the lbt register values
from the current thread. */
static void
fetch_lbt_from_thread (struct regcache *regcache, int regnum, pid_t tid)
{
gdb_byte regset[LOONGARCH_LBT_REGS_SIZE];
if (regnum == -1
|| (regnum >= LOONGARCH_FIRST_SCR_REGNUM
&& regnum <= LOONGARCH_FTOP_REGNUM))
{
struct iovec iov;
iov.iov_base = regset;
iov.iov_len = LOONGARCH_LBT_REGS_SIZE;
if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, (long) &iov) < 0)
{
/* If kernel dose not support lbt, just return. */
if (errno == EINVAL)
return;
perror_with_name (_("Couldn't get NT_LARCH_LBT registers"));
}
else
loongarch_lbtregset.supply_regset (nullptr, regcache, -1,
regset, LOONGARCH_LBT_REGS_SIZE);
}
}
/* Store to the current thread the valid lbt register values
in the GDB's register array. */
static void
store_lbt_to_thread (struct regcache *regcache, int regnum, pid_t tid)
{
gdb_byte regset[LOONGARCH_LBT_REGS_SIZE];
if (regnum == -1
|| (regnum >= LOONGARCH_FIRST_SCR_REGNUM
&& regnum <= LOONGARCH_FTOP_REGNUM))
{
struct iovec iov;
iov.iov_base = regset;
iov.iov_len = LOONGARCH_LBT_REGS_SIZE;
if (ptrace (PTRACE_GETREGSET, tid, NT_LARCH_LBT, (long) &iov) < 0)
{
/* If kernel dose not support lbt, just return. */
if (errno == EINVAL)
return;
perror_with_name (_("Couldn't get NT_LARCH_LBT registers"));
}
else
{
loongarch_lbtregset.collect_regset (nullptr, regcache, regnum,
regset, LOONGARCH_LBT_REGS_SIZE);
if (ptrace (PTRACE_SETREGSET, tid, NT_LARCH_LBT, (long) &iov) < 0)
perror_with_name (_("Couldn't set NT_LARCH_LBT registers"));
}
}
}
/* Implement the "fetch_registers" target_ops method. */
void
@ -277,6 +341,7 @@ loongarch_linux_nat_target::fetch_registers (struct regcache *regcache,
fetch_fpregs_from_thread(regcache, regnum, tid);
fetch_lsxregs_from_thread(regcache, regnum, tid);
fetch_lasxregs_from_thread(regcache, regnum, tid);
fetch_lbt_from_thread (regcache, regnum, tid);
}
/* Implement the "store_registers" target_ops method. */
@ -291,6 +356,7 @@ loongarch_linux_nat_target::store_registers (struct regcache *regcache,
store_fpregs_to_thread(regcache, regnum, tid);
store_lsxregs_to_thread(regcache, regnum, tid);
store_lasxregs_to_thread(regcache, regnum, tid);
store_lbt_to_thread (regcache, regnum, tid);
}
/* Return the address in the core dump or inferior of register REGNO. */

View File

@ -338,6 +338,107 @@ const struct regset loongarch_lasxregset =
loongarch_fill_lasxregset,
};
/* Unpack an lbt regset into GDB's register cache. */
static void
loongarch_supply_lbtregset (const struct regset *regset,
struct regcache *regcache, int regnum,
const void *regs, size_t len)
{
int scrsize = register_size (regcache->arch (), LOONGARCH_FIRST_SCR_REGNUM);
int eflagssize = register_size (regcache->arch (), LOONGARCH_EFLAGS_REGNUM);
const gdb_byte *buf = nullptr;
if (regnum == -1)
{
for (int i = 0; i < LOONGARCH_LINUX_NUM_SCR; i++)
{
buf = (const gdb_byte *) regs + scrsize * i;
regcache->raw_supply (LOONGARCH_FIRST_SCR_REGNUM + i,
(const void *) buf);
}
buf = (const gdb_byte*) regs + scrsize * LOONGARCH_LINUX_NUM_SCR;
regcache->raw_supply (LOONGARCH_EFLAGS_REGNUM, (const void *) buf);
buf = (const gdb_byte*) regs
+ scrsize * LOONGARCH_LINUX_NUM_SCR
+ eflagssize;
regcache->raw_supply (LOONGARCH_FTOP_REGNUM, (const void *) buf);
}
else if (regnum >= LOONGARCH_FIRST_SCR_REGNUM
&& regnum <= LOONGARCH_LAST_SCR_REGNUM)
{
buf = (const gdb_byte*) regs
+ scrsize * (regnum - LOONGARCH_FIRST_SCR_REGNUM);
regcache->raw_supply (regnum, (const void *) buf);
}
else if (regnum == LOONGARCH_EFLAGS_REGNUM)
{
buf = (const gdb_byte*) regs + scrsize * LOONGARCH_LINUX_NUM_SCR;
regcache->raw_supply (regnum, (const void *) buf);
}
else if (regnum == LOONGARCH_FTOP_REGNUM)
{
buf = (const gdb_byte*) regs
+ scrsize * LOONGARCH_LINUX_NUM_SCR
+ eflagssize;
regcache->raw_supply (regnum, (const void *) buf);
}
}
/* Pack the GDB's register cache value into an lbt regset. */
static void
loongarch_fill_lbtregset (const struct regset *regset,
const struct regcache *regcache, int regnum,
void *regs, size_t len)
{
int scrsize = register_size (regcache->arch (), LOONGARCH_FIRST_SCR_REGNUM);
int eflagssize = register_size (regcache->arch (), LOONGARCH_EFLAGS_REGNUM);
gdb_byte *buf = nullptr;
if (regnum == -1)
{
for (int i = 0; i < LOONGARCH_LINUX_NUM_SCR; i++)
{
buf = (gdb_byte *) regs + scrsize * i;
regcache->raw_collect (LOONGARCH_FIRST_SCR_REGNUM + i, (void *) buf);
}
buf = (gdb_byte *) regs + scrsize * LOONGARCH_LINUX_NUM_SCR;
regcache->raw_collect (LOONGARCH_EFLAGS_REGNUM, (void *) buf);
buf = (gdb_byte *) regs + scrsize * LOONGARCH_LINUX_NUM_SCR + eflagssize;
regcache->raw_collect (LOONGARCH_FTOP_REGNUM, (void *) buf);
}
else if (regnum >= LOONGARCH_FIRST_SCR_REGNUM
&& regnum <= LOONGARCH_LAST_SCR_REGNUM)
{
buf = (gdb_byte *) regs + scrsize * (regnum - LOONGARCH_FIRST_SCR_REGNUM);
regcache->raw_collect (regnum, (void *) buf);
}
else if (regnum == LOONGARCH_EFLAGS_REGNUM)
{
buf = (gdb_byte *) regs + scrsize * LOONGARCH_LINUX_NUM_SCR;
regcache->raw_collect (regnum, (void *) buf);
}
else if (regnum == LOONGARCH_FTOP_REGNUM)
{
buf = (gdb_byte *) regs + scrsize * LOONGARCH_LINUX_NUM_SCR + eflagssize;
regcache->raw_collect (regnum, (void *) buf);
}
}
/* Define the lbt register regset. */
const struct regset loongarch_lbtregset =
{
nullptr,
loongarch_supply_lbtregset,
loongarch_fill_lbtregset,
};
/* Implement the "init" method of struct tramp_frame. */
#define LOONGARCH_RT_SIGFRAME_UCONTEXT_OFFSET 128
@ -394,6 +495,10 @@ loongarch_iterate_over_regset_sections (struct gdbarch *gdbarch,
fccsize * LOONGARCH_LINUX_NUM_FCC + fcsrsize;
int lsxrsize = register_size (gdbarch, LOONGARCH_FIRST_LSX_REGNUM);
int lasxrsize = register_size (gdbarch, LOONGARCH_FIRST_LASX_REGNUM);
int scrsize = register_size (gdbarch, LOONGARCH_FIRST_SCR_REGNUM);
int eflagssize = register_size (gdbarch, LOONGARCH_EFLAGS_REGNUM);
int ftopsize = register_size (gdbarch, LOONGARCH_FTOP_REGNUM);
int lbtsize = scrsize * LOONGARCH_LINUX_NUM_SCR + eflagssize + ftopsize;
cb (".reg", LOONGARCH_LINUX_NUM_GREGSET * gprsize,
LOONGARCH_LINUX_NUM_GREGSET * gprsize, &loongarch_gregset, nullptr, cb_data);
@ -402,6 +507,8 @@ loongarch_iterate_over_regset_sections (struct gdbarch *gdbarch,
&loongarch_lsxregset, nullptr, cb_data);
cb (".reg-loongarch-lasx", lasxrsize, lasxrsize,
&loongarch_lasxregset, nullptr, cb_data);
cb (".reg-loongarch-lbt", lbtsize, lbtsize,
&loongarch_lbtregset, nullptr, cb_data);
}

View File

@ -1743,6 +1743,24 @@ loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
if (!valid_p)
return nullptr;
const struct tdesc_feature *feature_lbt
= tdesc_find_feature (tdesc, "org.gnu.gdb.loongarch.lbt");
if (feature_lbt == nullptr)
return nullptr;
/* Validate the description provides the lbt registers and
allocate their numbers. */
regnum = LOONGARCH_FIRST_SCR_REGNUM;
for (int i = 0; i < LOONGARCH_LINUX_NUM_SCR; i++)
valid_p &= tdesc_numbered_register (feature_lbt, tdesc_data.get (), regnum++,
loongarch_cr_normal_name[i] + 1);
valid_p &= tdesc_numbered_register (feature_lbt, tdesc_data.get (), regnum++,
"eflags");
valid_p &= tdesc_numbered_register (feature_lbt, tdesc_data.get (), regnum++,
"ftop");
if (!valid_p)
return nullptr;
/* LoongArch code is always little-endian. */
info.byte_order_for_code = BFD_ENDIAN_LITTLE;

View File

@ -32,6 +32,7 @@ extern const struct regset loongarch_gregset;
extern const struct regset loongarch_fpregset;
extern const struct regset loongarch_lsxregset;
extern const struct regset loongarch_lasxregset;
extern const struct regset loongarch_lbtregset;
/* Target-dependent structure in gdbarch. */
struct loongarch_gdbarch_tdep : gdbarch_tdep_base

View File

@ -225,6 +225,47 @@ loongarch_store_lasxregset (struct regcache *regcache, const void *buf)
supply_register (regcache, LOONGARCH_FIRST_LASX_REGNUM + i, *regset + i);
}
/* Collect lbt regs from REGCACHE into BUF. */
static void
loongarch_fill_lbtregset (struct regcache *regcache, void *buf)
{
gdb_byte *regbuf = (gdb_byte*)buf;
int scrsize = register_size (regcache->tdesc, LOONGARCH_FIRST_SCR_REGNUM);
int eflagssize = register_size (regcache->tdesc, LOONGARCH_EFLAGS_REGNUM);
int i;
for (i = 0; i < LOONGARCH_LINUX_NUM_SCR; i++)
collect_register (regcache, LOONGARCH_FIRST_SCR_REGNUM + i, regbuf + scrsize * i);
collect_register (regcache, LOONGARCH_EFLAGS_REGNUM,
regbuf + LOONGARCH_LINUX_NUM_SCR * scrsize);
collect_register (regcache, LOONGARCH_FTOP_REGNUM,
regbuf + LOONGARCH_LINUX_NUM_SCR * scrsize + eflagssize);
}
/* Supply lbt regs from BUF into REGCACHE. */
static void
loongarch_store_lbtregset (struct regcache *regcache, const void *buf)
{
gdb_byte *regbuf = (gdb_byte*)buf;
int scrsize = register_size (regcache->tdesc, LOONGARCH_FIRST_SCR_REGNUM);
int eflagssize = register_size (regcache->tdesc, LOONGARCH_EFLAGS_REGNUM);
int i;
for (i = 0; i < LOONGARCH_LINUX_NUM_SCR; i++)
supply_register (regcache, LOONGARCH_FIRST_SCR_REGNUM + i, regbuf + scrsize * i);
supply_register (regcache, LOONGARCH_EFLAGS_REGNUM,
regbuf + LOONGARCH_LINUX_NUM_SCR * scrsize);
supply_register (regcache, LOONGARCH_FTOP_REGNUM,
regbuf + LOONGARCH_LINUX_NUM_SCR * scrsize + eflagssize);
}
/* LoongArch/Linux regsets. */
static struct regset_info loongarch_regsets[] = {
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_PRSTATUS, sizeof (elf_gregset_t),
@ -235,6 +276,8 @@ static struct regset_info loongarch_regsets[] = {
OPTIONAL_REGS, loongarch_fill_lsxregset, loongarch_store_lsxregset },
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_LARCH_LASX, sizeof (elf_lasxregset_t),
OPTIONAL_REGS, loongarch_fill_lasxregset, loongarch_store_lasxregset },
{ PTRACE_GETREGSET, PTRACE_SETREGSET, NT_LARCH_LBT, LOONGARCH_LBT_REGS_SIZE,
OPTIONAL_REGS, loongarch_fill_lbtregset, loongarch_store_lbtregset },
NULL_REGSET
};