From ee98c0daf93476e323134492cf604e71b05ca883 Mon Sep 17 00:00:00 2001 From: "Maciej W. Rozycki" Date: Mon, 3 Feb 2020 12:07:02 +0000 Subject: [PATCH] RISC-V/Linux/native: Determine FLEN dynamically Fix RISC-V native Linux support to handle a 64-bit FPU (FLEN == 64) with both RV32 and RV64 systems, which is a part of the current Linux ABI for hard-float systems, rather than assuming that (FLEN == XLEN) in target description determination and that (FLEN == 64) in register access. We can do better however and not rely on any particular value of FLEN and probe for it dynamically, by observing that the PTRACE_GETREGSET ptrace(2) call will only accept an exact regset size, and that will reflect FLEN. Therefore iterate over the call in target description determination with a geometrically increasing regset size until a match is marked by a successful ptrace(2) call completion or we run beyond the maximum size we can support. Update register accessors accordingly, using FLEN determined to size the buffer used for NT_PRSTATUS requests and then to exchange data with the regcache. Also handle a glibc bug where ELF_NFPREG is defined in terms of NFPREG, however NFPREG is nowhere defined. gdb/ * riscv-linux-nat.c [!NFPREG] (NFPREG): New macro. (supply_fpregset_regnum, fill_fpregset): Handle regset buffer offsets according to FLEN determined. (riscv_linux_nat_target::read_description): Determine FLEN dynamically. (riscv_linux_nat_target::fetch_registers): Size regset buffer according to FLEN determined. (riscv_linux_nat_target::store_registers): Likewise. --- gdb/ChangeLog | 11 +++++ gdb/riscv-linux-nat.c | 112 ++++++++++++++++++++++++++++++++---------- 2 files changed, 97 insertions(+), 26 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 81a3b9c1dea..5ac2596cca9 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,14 @@ +2020-02-03 Maciej W. Rozycki + + * riscv-linux-nat.c [!NFPREG] (NFPREG): New macro. + (supply_fpregset_regnum, fill_fpregset): Handle regset buffer + offsets according to FLEN determined. + (riscv_linux_nat_target::read_description): Determine FLEN + dynamically. + (riscv_linux_nat_target::fetch_registers): Size regset buffer + according to FLEN determined. + (riscv_linux_nat_target::store_registers): Likewise. + 2020-02-01 Shahab Vahedi * target-descriptions.c (tdesc_register_in_reggroup_p): Return 0 diff --git a/gdb/riscv-linux-nat.c b/gdb/riscv-linux-nat.c index 7a96b7b0743..043bbd44b68 100644 --- a/gdb/riscv-linux-nat.c +++ b/gdb/riscv-linux-nat.c @@ -28,6 +28,11 @@ #include +/* Work around glibc header breakage causing ELF_NFPREG not to be usable. */ +#ifndef NFPREG +# define NFPREG 33 +#endif + /* RISC-V Linux native additions to the default linux support. */ class riscv_linux_nat_target final : public linux_nat_target @@ -88,21 +93,35 @@ static void supply_fpregset_regnum (struct regcache *regcache, const prfpregset_t *fpregs, int regnum) { + int flen = register_size (regcache->arch (), RISCV_FIRST_FP_REGNUM); + union + { + const prfpregset_t *fpregs; + const gdb_byte *buf; + } + fpbuf = { .fpregs = fpregs }; int i; if (regnum == -1) { /* We only support the FP registers and FCSR here. */ - for (i = RISCV_FIRST_FP_REGNUM; i <= RISCV_LAST_FP_REGNUM; i++) - regcache->raw_supply (i, &fpregs->__d.__f[i - RISCV_FIRST_FP_REGNUM]); + for (i = RISCV_FIRST_FP_REGNUM; + i <= RISCV_LAST_FP_REGNUM; + i++, fpbuf.buf += flen) + regcache->raw_supply (i, fpbuf.buf); - regcache->raw_supply (RISCV_CSR_FCSR_REGNUM, &fpregs->__d.__fcsr); + regcache->raw_supply (RISCV_CSR_FCSR_REGNUM, fpbuf.buf); } else if (regnum >= RISCV_FIRST_FP_REGNUM && regnum <= RISCV_LAST_FP_REGNUM) - regcache->raw_supply (regnum, - &fpregs->__d.__f[regnum - RISCV_FIRST_FP_REGNUM]); + { + fpbuf.buf += flen * (regnum - RISCV_FIRST_FP_REGNUM); + regcache->raw_supply (regnum, fpbuf.buf); + } else if (regnum == RISCV_CSR_FCSR_REGNUM) - regcache->raw_supply (RISCV_CSR_FCSR_REGNUM, &fpregs->__d.__fcsr); + { + fpbuf.buf += flen * (RISCV_LAST_FP_REGNUM - RISCV_FIRST_FP_REGNUM + 1); + regcache->raw_supply (RISCV_CSR_FCSR_REGNUM, fpbuf.buf); + } } /* Copy all floating point registers from regset FPREGS into REGCACHE. */ @@ -145,19 +164,35 @@ void fill_fpregset (const struct regcache *regcache, prfpregset_t *fpregs, int regnum) { + int flen = register_size (regcache->arch (), RISCV_FIRST_FP_REGNUM); + union + { + prfpregset_t *fpregs; + gdb_byte *buf; + } + fpbuf = { .fpregs = fpregs }; + int i; + if (regnum == -1) { /* We only support the FP registers and FCSR here. */ - for (int i = RISCV_FIRST_FP_REGNUM; i <= RISCV_LAST_FP_REGNUM; i++) - regcache->raw_collect (i, &fpregs->__d.__f[i - RISCV_FIRST_FP_REGNUM]); + for (i = RISCV_FIRST_FP_REGNUM; + i <= RISCV_LAST_FP_REGNUM; + i++, fpbuf.buf += flen) + regcache->raw_collect (i, fpbuf.buf); - regcache->raw_collect (RISCV_CSR_FCSR_REGNUM, &fpregs->__d.__fcsr); + regcache->raw_collect (RISCV_CSR_FCSR_REGNUM, fpbuf.buf); } else if (regnum >= RISCV_FIRST_FP_REGNUM && regnum <= RISCV_LAST_FP_REGNUM) - regcache->raw_collect (regnum, - &fpregs->__d.__f[regnum - RISCV_FIRST_FP_REGNUM]); + { + fpbuf.buf += flen * (regnum - RISCV_FIRST_FP_REGNUM); + regcache->raw_collect (regnum, fpbuf.buf); + } else if (regnum == RISCV_CSR_FCSR_REGNUM) - regcache->raw_collect (RISCV_CSR_FCSR_REGNUM, &fpregs->__d.__fcsr); + { + fpbuf.buf += flen * (RISCV_LAST_FP_REGNUM - RISCV_FIRST_FP_REGNUM + 1); + regcache->raw_collect (RISCV_CSR_FCSR_REGNUM, fpbuf.buf); + } } /* Return a target description for the current target. */ @@ -166,8 +201,8 @@ const struct target_desc * riscv_linux_nat_target::read_description () { struct riscv_gdbarch_features features; - struct iovec iov; elf_fpregset_t regs; + int flen; int tid; /* Figuring out xlen is easy. */ @@ -175,19 +210,40 @@ riscv_linux_nat_target::read_description () tid = inferior_ptid.lwp (); - iov.iov_base = ®s; - iov.iov_len = sizeof (regs); + /* Start with no f-registers. */ + features.flen = 0; - /* Can we fetch the f-registers? */ - if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, - (PTRACE_TYPE_ARG3) &iov) == -1) - features.flen = 0; /* No f-registers. */ - else + /* How much worth of f-registers can we fetch if any? */ + for (flen = sizeof (regs.__f.__f[0]); ; flen *= 2) { - /* TODO: We need a way to figure out the actual length of the - f-registers. We could have 64-bit x-registers, with 32-bit - f-registers. For now, just assumed xlen and flen match. */ - features.flen = features.xlen; + size_t regset_size; + struct iovec iov; + + /* Regsets have a uniform slot size, so we count FSCR like + an FP data register. */ + regset_size = ELF_NFPREG * flen; + if (regset_size > sizeof (regs)) + break; + + iov.iov_base = ®s; + iov.iov_len = regset_size; + if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, + (PTRACE_TYPE_ARG3) &iov) == -1) + { + switch (errno) + { + case EINVAL: + continue; + case EIO: + break; + default: + perror_with_name (_("Couldn't get registers")); + break; + } + } + else + features.flen = flen; + break; } return riscv_create_target_description (features); @@ -228,7 +284,9 @@ riscv_linux_nat_target::fetch_registers (struct regcache *regcache, int regnum) elf_fpregset_t regs; iov.iov_base = ®s; - iov.iov_len = sizeof (regs); + iov.iov_len = ELF_NFPREG * register_size (regcache->arch (), + RISCV_FIRST_FP_REGNUM); + gdb_assert (iov.iov_len <= sizeof (regs)); if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (PTRACE_TYPE_ARG3) &iov) == -1) @@ -289,7 +347,9 @@ riscv_linux_nat_target::store_registers (struct regcache *regcache, int regnum) elf_fpregset_t regs; iov.iov_base = ®s; - iov.iov_len = sizeof (regs); + iov.iov_len = ELF_NFPREG * register_size (regcache->arch (), + RISCV_FIRST_FP_REGNUM); + gdb_assert (iov.iov_len <= sizeof (regs)); if (ptrace (PTRACE_GETREGSET, tid, NT_FPREGSET, (PTRACE_TYPE_ARG3) &iov) == -1) -- 2.30.2