aboutsummaryrefslogtreecommitdiff
path: root/gdb/nat
diff options
context:
space:
mode:
authorLuis Machado <luis.machado@linaro.org>2020-03-18 13:06:05 -0300
committerLuis Machado <luis.machado@linaro.org>2020-12-10 11:45:08 -0300
commit6afcd2d4161165fd976c55a9792dbcccd2d5d106 (patch)
treefb937c4e5014dfc25d5b05162d78f5e13af25f4c /gdb/nat
parent15cc148fb817bc1eb91aa16e5d94e39ebafc11ee (diff)
downloadgdb-6afcd2d4161165fd976c55a9792dbcccd2d5d106.zip
gdb-6afcd2d4161165fd976c55a9792dbcccd2d5d106.tar.gz
gdb-6afcd2d4161165fd976c55a9792dbcccd2d5d106.tar.bz2
[AArch64] SVE/FPSIMD fixup for big endian
The FPSIMD dump in signal frames and ptrace FPSIMD dump in the SVE context structure follows the target endianness, whereas the SVE dumps are endianness-independent (LE). Therefore, when the system is in BE mode, we need to reverse the bytes for the FPSIMD data. Given the V registers are larger than 64-bit, I've added a way for value bytes to be set, as opposed to passing a 64-bit fixed quantity. This fits nicely with the unwinding *_got_bytes function and makes the trad-frame more flexible and capable of saving larger registers. The memory for the bytes is allocated via the frame obstack, so it gets freed after we're done inspecting the frame. gdb/ChangeLog: 2020-12-10 Luis Machado <luis.machado@linaro.org> * aarch64-linux-tdep.c (aarch64_linux_restore_vreg) New function. (aarch64_linux_sigframe_init): Call aarch64_linux_restore_vreg. * aarch64-tdep.h (V_REGISTER_SIZE): Move to ... * arch/aarch64.h: ... here. * nat/aarch64-sve-linux-ptrace.c: Include endian.h. (aarch64_maybe_swab128): New function. (aarch64_sve_regs_copy_to_reg_buf) (aarch64_sve_regs_copy_from_reg_buf): Adjust FPSIMD entries. * trad-frame.c (trad_frame_reset_saved_regs): Initialize the data field. (TF_REG_VALUE_BYTES): New enum value. (trad_frame_value_bytes_p): New function. (trad_frame_set_value_bytes): New function. (trad_frame_set_reg_value_bytes): New function. (trad_frame_get_prev_register): Handle register values saved as bytes. * trad-frame.h (trad_frame_set_reg_value_bytes): New prototype. (struct trad_frame_saved_reg) <data>: New field. (trad_frame_set_value_bytes): New prototype. (trad_frame_value_bytes_p): New prototype.
Diffstat (limited to 'gdb/nat')
-rw-r--r--gdb/nat/aarch64-sve-linux-ptrace.c77
1 files changed, 63 insertions, 14 deletions
diff --git a/gdb/nat/aarch64-sve-linux-ptrace.c b/gdb/nat/aarch64-sve-linux-ptrace.c
index 2ce90cc..5f08995 100644
--- a/gdb/nat/aarch64-sve-linux-ptrace.c
+++ b/gdb/nat/aarch64-sve-linux-ptrace.c
@@ -26,6 +26,7 @@
#include "arch/aarch64.h"
#include "gdbsupport/common-regcache.h"
#include "gdbsupport/byte-vector.h"
+#include <endian.h>
/* See nat/aarch64-sve-linux-ptrace.h. */
@@ -142,6 +143,24 @@ aarch64_sve_get_sveregs (int tid)
return buf;
}
+/* If we are running in BE mode, byteswap the contents
+ of SRC to DST for SIZE bytes. Other, just copy the contents
+ from SRC to DST. */
+
+static void
+aarch64_maybe_swab128 (gdb_byte *dst, const gdb_byte *src, size_t size)
+{
+ gdb_assert (src != nullptr && dst != nullptr);
+ gdb_assert (size > 1);
+
+#if (__BYTE_ORDER == __BIG_ENDIAN)
+ for (int i = 0; i < size - 1; i++)
+ dst[i] = src[size - i];
+#else
+ memcpy (dst, src, size);
+#endif
+}
+
/* See nat/aarch64-sve-linux-ptrace.h. */
void
@@ -184,34 +203,50 @@ aarch64_sve_regs_copy_to_reg_buf (struct reg_buffer_common *reg_buf,
}
else
{
+ /* WARNING: SIMD state is laid out in memory in target-endian format,
+ while SVE state is laid out in an endianness-independent format (LE).
+
+ So we have a couple cases to consider:
+
+ 1 - If the target is big endian, then SIMD state is big endian,
+ requiring a byteswap.
+
+ 2 - If the target is little endian, then SIMD state is little endian,
+ which matches the SVE format, so no byteswap is needed. */
+
/* There is no SVE state yet - the register dump contains a fpsimd
structure instead. These registers still exist in the hardware, but
the kernel has not yet initialised them, and so they will be null. */
- char *zero_reg = (char *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
+ gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
struct user_fpsimd_state *fpsimd
= (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET);
+ /* Make sure we have a zeroed register buffer. We will need the zero
+ padding below. */
+ memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
+
/* Copy across the V registers from fpsimd structure to the Z registers,
ensuring the non overlapping state is set to null. */
- memset (zero_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
-
for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
{
- memcpy (zero_reg, &fpsimd->vregs[i], sizeof (__int128_t));
- reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, zero_reg);
+ /* Handle big endian/little endian SIMD/SVE conversion. */
+ aarch64_maybe_swab128 (reg, (const gdb_byte *) &fpsimd->vregs[i],
+ V_REGISTER_SIZE);
+ reg_buf->raw_supply (AARCH64_SVE_Z0_REGNUM + i, reg);
}
reg_buf->raw_supply (AARCH64_FPSR_REGNUM, &fpsimd->fpsr);
reg_buf->raw_supply (AARCH64_FPCR_REGNUM, &fpsimd->fpcr);
/* Clear the SVE only registers. */
+ memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
- reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, zero_reg);
+ reg_buf->raw_supply (AARCH64_SVE_P0_REGNUM + i, reg);
- reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, zero_reg);
+ reg_buf->raw_supply (AARCH64_SVE_FFR_REGNUM, reg);
}
}
@@ -240,11 +275,11 @@ aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf,
kernel, which is why we try to avoid it. */
bool has_sve_state = false;
- char *zero_reg = (char *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
+ gdb_byte *reg = (gdb_byte *) alloca (SVE_PT_SVE_ZREG_SIZE (vq));
struct user_fpsimd_state *fpsimd
= (struct user_fpsimd_state *)(base + SVE_PT_FPSIMD_OFFSET);
- memset (zero_reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
+ memset (reg, 0, SVE_PT_SVE_ZREG_SIZE (vq));
/* Check in the reg_buf if any of the Z registers are set after the
first 128 bits, or if any of the other SVE registers are set. */
@@ -252,7 +287,7 @@ aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf,
for (int i = 0; i < AARCH64_SVE_Z_REGS_NUM; i++)
{
has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_Z0_REGNUM + i,
- zero_reg, sizeof (__int128_t));
+ reg, sizeof (__int128_t));
if (has_sve_state)
break;
}
@@ -261,19 +296,31 @@ aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf,
for (int i = 0; i < AARCH64_SVE_P_REGS_NUM; i++)
{
has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_P0_REGNUM + i,
- zero_reg, 0);
+ reg, 0);
if (has_sve_state)
break;
}
if (!has_sve_state)
has_sve_state |= reg_buf->raw_compare (AARCH64_SVE_FFR_REGNUM,
- zero_reg, 0);
+ reg, 0);
/* If no SVE state exists, then use the existing fpsimd structure to
write out state and return. */
if (!has_sve_state)
{
+ /* WARNING: SIMD state is laid out in memory in target-endian format,
+ while SVE state is laid out in an endianness-independent format
+ (LE).
+
+ So we have a couple cases to consider:
+
+ 1 - If the target is big endian, then SIMD state is big endian,
+ requiring a byteswap.
+
+ 2 - If the target is little endian, then SIMD state is little
+ endian, which matches the SVE format, so no byteswap is needed. */
+
/* The collects of the Z registers will overflow the size of a vreg.
There is enough space in the structure to allow for this, but we
cannot overflow into the next register as we might not be
@@ -284,8 +331,10 @@ aarch64_sve_regs_copy_from_reg_buf (const struct reg_buffer_common *reg_buf,
if (REG_VALID
== reg_buf->get_register_status (AARCH64_SVE_Z0_REGNUM + i))
{
- reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, zero_reg);
- memcpy (&fpsimd->vregs[i], zero_reg, sizeof (__int128_t));
+ reg_buf->raw_collect (AARCH64_SVE_Z0_REGNUM + i, reg);
+ /* Handle big endian/little endian SIMD/SVE conversion. */
+ aarch64_maybe_swab128 ((gdb_byte *) &fpsimd->vregs[i], reg,
+ V_REGISTER_SIZE);
}
}