mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-08-06 14:49:38 +08:00
[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.
This commit is contained in:
@ -180,6 +180,93 @@ read_aarch64_ctx (CORE_ADDR ctx_addr, enum bfd_endian byte_order,
|
||||
return magic;
|
||||
}
|
||||
|
||||
/* Given CACHE, use the trad_frame* functions to restore the FPSIMD
|
||||
registers from a signal frame.
|
||||
|
||||
VREG_NUM is the number of the V register being restored, OFFSET is the
|
||||
address containing the register value, BYTE_ORDER is the endianness and
|
||||
HAS_SVE tells us if we have a valid SVE context or not. */
|
||||
|
||||
static void
|
||||
aarch64_linux_restore_vreg (struct trad_frame_cache *cache, int num_regs,
|
||||
int vreg_num, CORE_ADDR offset,
|
||||
enum bfd_endian byte_order, bool has_sve)
|
||||
{
|
||||
/* WARNING: SIMD state is laid out in memory in target-endian format.
|
||||
|
||||
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, so
|
||||
no byteswap is needed. */
|
||||
|
||||
if (byte_order == BFD_ENDIAN_BIG)
|
||||
{
|
||||
gdb_byte buf[V_REGISTER_SIZE];
|
||||
|
||||
if (target_read_memory (offset, buf, V_REGISTER_SIZE) != 0)
|
||||
{
|
||||
size_t size = V_REGISTER_SIZE/2;
|
||||
|
||||
/* Read the two halves of the V register in reverse byte order. */
|
||||
CORE_ADDR u64 = extract_unsigned_integer (buf, size,
|
||||
byte_order);
|
||||
CORE_ADDR l64 = extract_unsigned_integer (buf + size, size,
|
||||
byte_order);
|
||||
|
||||
/* Copy the reversed bytes to the buffer. */
|
||||
store_unsigned_integer (buf, size, BFD_ENDIAN_LITTLE, l64);
|
||||
store_unsigned_integer (buf + size , size, BFD_ENDIAN_LITTLE, u64);
|
||||
|
||||
/* Now we can store the correct bytes for the V register. */
|
||||
trad_frame_set_reg_value_bytes (cache, AARCH64_V0_REGNUM + vreg_num,
|
||||
buf, V_REGISTER_SIZE);
|
||||
trad_frame_set_reg_value_bytes (cache,
|
||||
num_regs + AARCH64_Q0_REGNUM
|
||||
+ vreg_num, buf, Q_REGISTER_SIZE);
|
||||
trad_frame_set_reg_value_bytes (cache,
|
||||
num_regs + AARCH64_D0_REGNUM
|
||||
+ vreg_num, buf, D_REGISTER_SIZE);
|
||||
trad_frame_set_reg_value_bytes (cache,
|
||||
num_regs + AARCH64_S0_REGNUM
|
||||
+ vreg_num, buf, S_REGISTER_SIZE);
|
||||
trad_frame_set_reg_value_bytes (cache,
|
||||
num_regs + AARCH64_H0_REGNUM
|
||||
+ vreg_num, buf, H_REGISTER_SIZE);
|
||||
trad_frame_set_reg_value_bytes (cache,
|
||||
num_regs + AARCH64_B0_REGNUM
|
||||
+ vreg_num, buf, B_REGISTER_SIZE);
|
||||
|
||||
if (has_sve)
|
||||
trad_frame_set_reg_value_bytes (cache,
|
||||
num_regs + AARCH64_SVE_V0_REGNUM
|
||||
+ vreg_num, buf, V_REGISTER_SIZE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Little endian, just point at the address containing the register
|
||||
value. */
|
||||
trad_frame_set_reg_addr (cache, AARCH64_V0_REGNUM + vreg_num, offset);
|
||||
trad_frame_set_reg_addr (cache, num_regs + AARCH64_Q0_REGNUM + vreg_num,
|
||||
offset);
|
||||
trad_frame_set_reg_addr (cache, num_regs + AARCH64_D0_REGNUM + vreg_num,
|
||||
offset);
|
||||
trad_frame_set_reg_addr (cache, num_regs + AARCH64_S0_REGNUM + vreg_num,
|
||||
offset);
|
||||
trad_frame_set_reg_addr (cache, num_regs + AARCH64_H0_REGNUM + vreg_num,
|
||||
offset);
|
||||
trad_frame_set_reg_addr (cache, num_regs + AARCH64_B0_REGNUM + vreg_num,
|
||||
offset);
|
||||
|
||||
if (has_sve)
|
||||
trad_frame_set_reg_addr (cache, num_regs + AARCH64_SVE_V0_REGNUM
|
||||
+ vreg_num, offset);
|
||||
|
||||
}
|
||||
|
||||
/* Implement the "init" method of struct tramp_frame. */
|
||||
|
||||
static void
|
||||
@ -332,27 +419,16 @@ aarch64_linux_sigframe_init (const struct tramp_frame *self,
|
||||
|
||||
/* If there was no SVE section then set up the V registers. */
|
||||
if (sve_regs == 0)
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
CORE_ADDR offset = (fpsimd + AARCH64_FPSIMD_V0_OFFSET
|
||||
{
|
||||
for (int i = 0; i < 32; i++)
|
||||
{
|
||||
CORE_ADDR offset = (fpsimd + AARCH64_FPSIMD_V0_OFFSET
|
||||
+ (i * AARCH64_FPSIMD_VREG_SIZE));
|
||||
|
||||
trad_frame_set_reg_addr (this_cache, AARCH64_V0_REGNUM + i, offset);
|
||||
trad_frame_set_reg_addr (this_cache,
|
||||
num_regs + AARCH64_Q0_REGNUM + i, offset);
|
||||
trad_frame_set_reg_addr (this_cache,
|
||||
num_regs + AARCH64_D0_REGNUM + i, offset);
|
||||
trad_frame_set_reg_addr (this_cache,
|
||||
num_regs + AARCH64_S0_REGNUM + i, offset);
|
||||
trad_frame_set_reg_addr (this_cache,
|
||||
num_regs + AARCH64_H0_REGNUM + i, offset);
|
||||
trad_frame_set_reg_addr (this_cache,
|
||||
num_regs + AARCH64_B0_REGNUM + i, offset);
|
||||
if (tdep->has_sve ())
|
||||
trad_frame_set_reg_addr (this_cache,
|
||||
num_regs + AARCH64_SVE_V0_REGNUM + i,
|
||||
offset);
|
||||
}
|
||||
aarch64_linux_restore_vreg (this_cache, num_regs, i, offset,
|
||||
byte_order, tdep->has_sve ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trad_frame_set_id (this_cache, frame_id_build (sp, func));
|
||||
|
Reference in New Issue
Block a user