diff options
-rw-r--r-- | gdb/ChangeLog | 10 | ||||
-rw-r--r-- | gdb/aarch64-linux-nat.c | 5 | ||||
-rw-r--r-- | gdb/nat/aarch64-sve-linux-ptrace.c | 92 | ||||
-rw-r--r-- | gdb/nat/aarch64-sve-linux-ptrace.h | 12 |
4 files changed, 73 insertions, 46 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index b3155ad..4e73bf7 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,15 @@ 2019-04-15 Alan Hayward <alan.hayward@arm.com> + * aarch64-linux-nat.c (store_sveregs_to_thread): Set vector length. + * nat/aarch64-sve-linux-ptrace.c (aarch64_sve_set_vq): New + function. + (aarch64_sve_regs_copy_to_reg_buf): Remove VG checks. + (aarch64_sve_regs_copy_from_reg_buf): Likewise. + * nat/aarch64-sve-linux-ptrace.h (aarch64_sve_set_vq): New + declaration. + +2019-04-15 Alan Hayward <alan.hayward@arm.com> + * aarch64-linux-nat.c (aarch64_linux_nat_target::thread_architecture): Add override. * aarch64-tdep.c (aarch64_gdbarch_init): Ensure different tdesc for diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c index c5070c8..8ca9614 100644 --- a/gdb/aarch64-linux-nat.c +++ b/gdb/aarch64-linux-nat.c @@ -412,6 +412,11 @@ store_sveregs_to_thread (struct regcache *regcache) struct iovec iovec; int tid = regcache->ptid ().lwp (); + /* First store vector length to the thread. This is done first to ensure the + ptrace buffers read from the kernel are the correct size. */ + if (!aarch64_sve_set_vq (tid, regcache)) + perror_with_name (_("Unable to set VG register.")); + /* Obtain a dump of SVE registers from ptrace. */ std::unique_ptr<gdb_byte[]> base = aarch64_sve_get_sveregs (tid); diff --git a/gdb/nat/aarch64-sve-linux-ptrace.c b/gdb/nat/aarch64-sve-linux-ptrace.c index 30faab2..635b4c9 100644 --- a/gdb/nat/aarch64-sve-linux-ptrace.c +++ b/gdb/nat/aarch64-sve-linux-ptrace.c @@ -27,8 +27,6 @@ #include "common/common-regcache.h" #include "common/byte-vector.h" -static bool vq_change_warned = false; - /* See nat/aarch64-sve-linux-ptrace.h. */ uint64_t @@ -63,6 +61,48 @@ aarch64_sve_get_vq (int tid) /* See nat/aarch64-sve-linux-ptrace.h. */ +bool +aarch64_sve_set_vq (int tid, uint64_t vq) +{ + struct iovec iovec; + struct user_sve_header header; + + iovec.iov_len = sizeof (header); + iovec.iov_base = &header; + + if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_SVE, &iovec) < 0) + { + /* SVE is not supported. */ + return false; + } + + header.vl = sve_vl_from_vq (vq); + + if (ptrace (PTRACE_SETREGSET, tid, NT_ARM_SVE, &iovec) < 0) + { + /* Vector length change failed. */ + return false; + } + + return true; +} + +/* See nat/aarch64-sve-linux-ptrace.h. */ + +bool +aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf) +{ + if (reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM) != REG_VALID) + return false; + + uint64_t reg_vg = 0; + reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, ®_vg); + + return aarch64_sve_set_vq (tid, sve_vq_from_vg (reg_vg)); +} + +/* See nat/aarch64-sve-linux-ptrace.h. */ + std::unique_ptr<gdb_byte[]> aarch64_sve_get_sveregs (int tid) { @@ -95,37 +135,18 @@ aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf, { char *base = (char *) buf; struct user_sve_header *header = (struct user_sve_header *) buf; - uint64_t vq, vg_reg_buf = 0; - vq = sve_vq_from_vl (header->vl); + uint64_t vq = sve_vq_from_vl (header->vl); + uint64_t vg = sve_vg_from_vl (header->vl); /* Sanity check the data in the header. */ if (!sve_vl_valid (header->vl) || SVE_PT_SIZE (vq, header->flags) != header->size) error (_("Invalid SVE header from kernel.")); - if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM)) - reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, &vg_reg_buf); - - if (vg_reg_buf == 0) - { - /* VG has not been set. */ - vg_reg_buf = sve_vg_from_vl (header->vl); - reg_buf->raw_supply (AARCH64_SVE_VG_REGNUM, &vg_reg_buf); - } - else if (vg_reg_buf != sve_vg_from_vl (header->vl) && !vq_change_warned) - { - /* Vector length on the running process has changed. GDB currently does - not support this and will result in GDB showing incorrect partially - incorrect data for the vector registers. Warn once and continue. We - do not expect many programs to exhibit this behaviour. To fix this - we need to spot the change earlier and generate a new target - descriptor. */ - warning (_("SVE Vector length has changed (%ld to %d). " - "Vector registers may show incorrect data."), - vg_reg_buf, sve_vg_from_vl (header->vl)); - vq_change_warned = true; - } + /* Update VG. Note, the registers in the regcache will already be of the + correct length. */ + reg_buf->raw_supply (AARCH64_SVE_VG_REGNUM, &vg); if (HAS_SVE_STATE (*header)) { @@ -187,30 +208,13 @@ aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf, { struct user_sve_header *header = (struct user_sve_header *) buf; char *base = (char *) buf; - uint64_t vq, vg_reg_buf = 0; - - vq = sve_vq_from_vl (header->vl); + uint64_t vq = sve_vq_from_vl (header->vl); /* Sanity check the data in the header. */ if (!sve_vl_valid (header->vl) || SVE_PT_SIZE (vq, header->flags) != header->size) error (_("Invalid SVE header from kernel.")); - if (REG_VALID == reg_buf->get_register_status (AARCH64_SVE_VG_REGNUM)) - reg_buf->raw_collect (AARCH64_SVE_VG_REGNUM, &vg_reg_buf); - - if (vg_reg_buf != 0 && vg_reg_buf != sve_vg_from_vl (header->vl)) - { - /* Vector length on the running process has changed. GDB currently does - not support this and will result in GDB writing invalid data back to - the vector registers. Error and exit. We do not expect many programs - to exhibit this behaviour. To fix this we need to spot the change - earlier and generate a new target descriptor. */ - error (_("SVE Vector length has changed (%ld to %d). " - "Cannot write back registers."), - vg_reg_buf, sve_vg_from_vl (header->vl)); - } - if (!HAS_SVE_STATE (*header)) { /* There is no SVE state yet - the register dump contains a fpsimd diff --git a/gdb/nat/aarch64-sve-linux-ptrace.h b/gdb/nat/aarch64-sve-linux-ptrace.h index 167fc8e..ee994f2 100644 --- a/gdb/nat/aarch64-sve-linux-ptrace.h +++ b/gdb/nat/aarch64-sve-linux-ptrace.h @@ -39,17 +39,25 @@ uint64_t aarch64_sve_get_vq (int tid); +/* Set VQ in the kernel for the given tid, using either the value VQ or + reading from the register VG in the register buffer. */ + +bool aarch64_sve_set_vq (int tid, uint64_t vq); +bool aarch64_sve_set_vq (int tid, struct reg_buffer_common *reg_buf); + /* Read the current SVE register set using ptrace, allocating space as required. */ extern std::unique_ptr<gdb_byte[]> aarch64_sve_get_sveregs (int tid); -/* Put the registers from linux structure buf into register buffer. */ +/* Put the registers from linux structure buf into register buffer. Assumes the + vector lengths in the register buffer match the size in the kernel. */ extern void aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf, const void *buf); -/* Put the registers from register buffer into linux structure buf. */ +/* Put the registers from register buffer into linux structure buf. Assumes the + vector lengths in the register buffer match the size in the kernel. */ extern void aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf, |