aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gdb/ChangeLog22
-rw-r--r--gdb/aarch64-linux-tdep.c114
-rw-r--r--gdb/aarch64-tdep.h1
-rw-r--r--gdb/arch/aarch64.h2
-rw-r--r--gdb/nat/aarch64-sve-linux-ptrace.c77
-rw-r--r--gdb/trad-frame.c46
-rw-r--r--gdb/trad-frame.h19
7 files changed, 246 insertions, 35 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 9352954..ace56aa 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,25 @@
+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.
+
2020-12-07 Mihails Strasuns <mihails.strasuns@intel.com>
* jit.c (mem_bfd*, bfd_open_from_target_memory): Removed.
diff --git a/gdb/aarch64-linux-tdep.c b/gdb/aarch64-linux-tdep.c
index ce697ff..1e8d8e9 100644
--- a/gdb/aarch64-linux-tdep.c
+++ b/gdb/aarch64-linux-tdep.c
@@ -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));
diff --git a/gdb/aarch64-tdep.h b/gdb/aarch64-tdep.h
index 76ff812..7d035c1 100644
--- a/gdb/aarch64-tdep.h
+++ b/gdb/aarch64-tdep.h
@@ -48,7 +48,6 @@ struct regset;
#define H_REGISTER_SIZE 2
#define S_REGISTER_SIZE 4
#define D_REGISTER_SIZE 8
-#define V_REGISTER_SIZE 16
#define Q_REGISTER_SIZE 16
/* Total number of general (X) registers. */
diff --git a/gdb/arch/aarch64.h b/gdb/arch/aarch64.h
index 857bb22..b753524 100644
--- a/gdb/arch/aarch64.h
+++ b/gdb/arch/aarch64.h
@@ -58,6 +58,8 @@ enum aarch64_regnum
AARCH64_LAST_V_ARG_REGNUM = AARCH64_V0_REGNUM + 7
};
+#define V_REGISTER_SIZE 16
+
/* Pseudo register base numbers. */
#define AARCH64_Q0_REGNUM 0
#define AARCH64_D0_REGNUM (AARCH64_Q0_REGNUM + AARCH64_D_REGISTER_COUNT)
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);
}
}
diff --git a/gdb/trad-frame.c b/gdb/trad-frame.c
index a6a8479..8a1aa81 100644
--- a/gdb/trad-frame.c
+++ b/gdb/trad-frame.c
@@ -56,6 +56,7 @@ trad_frame_reset_saved_regs (struct gdbarch *gdbarch,
{
regs[regnum].realreg = regnum;
regs[regnum].addr = -1;
+ regs[regnum].data = nullptr;
}
}
@@ -83,7 +84,7 @@ trad_frame_alloc_saved_regs (struct frame_info *this_frame)
return trad_frame_alloc_saved_regs (gdbarch);
}
-enum { TF_REG_VALUE = -1, TF_REG_UNKNOWN = -2 };
+enum { TF_REG_VALUE = -1, TF_REG_UNKNOWN = -2, TF_REG_VALUE_BYTES = -3 };
int
trad_frame_value_p (struct trad_frame_saved_reg this_saved_regs[], int regnum)
@@ -106,6 +107,16 @@ trad_frame_realreg_p (struct trad_frame_saved_reg this_saved_regs[],
&& this_saved_regs[regnum].addr == -1);
}
+/* See trad-frame.h. */
+
+bool
+trad_frame_value_bytes_p (struct trad_frame_saved_reg this_saved_regs[],
+ int regnum)
+{
+ return (this_saved_regs[regnum].realreg == TF_REG_VALUE_BYTES
+ && this_saved_regs[regnum].data != nullptr);
+}
+
void
trad_frame_set_value (struct trad_frame_saved_reg this_saved_regs[],
int regnum, LONGEST val)
@@ -224,6 +235,35 @@ trad_frame_set_unknown (struct trad_frame_saved_reg this_saved_regs[],
this_saved_regs[regnum].addr = -1;
}
+/* See trad-frame.h. */
+
+void
+trad_frame_set_value_bytes (struct trad_frame_saved_reg this_saved_regs[],
+ int regnum, const gdb_byte *bytes,
+ size_t size)
+{
+ this_saved_regs[regnum].realreg = TF_REG_VALUE_BYTES;
+
+ /* Allocate the space and copy the data bytes. */
+ this_saved_regs[regnum].data = FRAME_OBSTACK_CALLOC (size, gdb_byte);
+ memcpy (this_saved_regs[regnum].data, bytes, size);
+}
+
+/* See trad-frame.h. */
+
+void
+trad_frame_set_reg_value_bytes (struct trad_frame_cache *this_trad_cache,
+ int regnum, const gdb_byte *bytes,
+ size_t size)
+{
+ /* External interface for users of trad_frame_cache
+ (who cannot access the prev_regs object directly). */
+ trad_frame_set_value_bytes (this_trad_cache->prev_regs, regnum, bytes,
+ size);
+}
+
+
+
struct value *
trad_frame_get_prev_register (struct frame_info *this_frame,
struct trad_frame_saved_reg this_saved_regs[],
@@ -240,6 +280,10 @@ trad_frame_get_prev_register (struct frame_info *this_frame,
/* The register's value is available. */
return frame_unwind_got_constant (this_frame, regnum,
this_saved_regs[regnum].addr);
+ else if (trad_frame_value_bytes_p (this_saved_regs, regnum))
+ /* The register's value is available as a sequence of bytes. */
+ return frame_unwind_got_bytes (this_frame, regnum,
+ this_saved_regs[regnum].data);
else
return frame_unwind_got_optimized (this_frame, regnum);
}
diff --git a/gdb/trad-frame.h b/gdb/trad-frame.h
index 7b57856..38db439 100644
--- a/gdb/trad-frame.h
+++ b/gdb/trad-frame.h
@@ -52,6 +52,12 @@ void trad_frame_set_reg_regmap (struct trad_frame_cache *this_trad_cache,
void trad_frame_set_reg_value (struct trad_frame_cache *this_cache,
int regnum, LONGEST val);
+/* Given the cache in THIS_TRAD_CACHE, set the value of REGNUM to the bytes
+ contained in BYTES with size SIZE. */
+void trad_frame_set_reg_value_bytes (struct trad_frame_cache *this_trad_cache,
+ int regnum, const gdb_byte *bytes,
+ size_t size);
+
struct value *trad_frame_get_register (struct trad_frame_cache *this_trad_cache,
struct frame_info *this_frame,
int regnum);
@@ -86,6 +92,8 @@ struct trad_frame_saved_reg
{
LONGEST addr; /* A CORE_ADDR fits in a longest. */
int realreg;
+ /* Register data (for values that don't fit in ADDR). */
+ gdb_byte *data;
};
/* Encode REGNUM value in the trad-frame. */
@@ -104,6 +112,12 @@ void trad_frame_set_addr (struct trad_frame_saved_reg this_trad_cache[],
void trad_frame_set_unknown (struct trad_frame_saved_reg this_saved_regs[],
int regnum);
+/* Encode REGNUM value in the trad-frame as a sequence of bytes. This is
+ useful when the value is larger than what primitive types can hold. */
+void trad_frame_set_value_bytes (struct trad_frame_saved_reg this_saved_regs[],
+ int regnum, const gdb_byte *bytes,
+ size_t size);
+
/* Convenience functions, return non-zero if the register has been
encoded as specified. */
int trad_frame_value_p (struct trad_frame_saved_reg this_saved_regs[],
@@ -113,6 +127,11 @@ int trad_frame_addr_p (struct trad_frame_saved_reg this_saved_regs[],
int trad_frame_realreg_p (struct trad_frame_saved_reg this_saved_regs[],
int regnum);
+/* Return TRUE if REGNUM is stored as a sequence of bytes, and FALSE
+ otherwise. */
+bool trad_frame_value_bytes_p (struct trad_frame_saved_reg this_saved_regs[],
+ int regnum);
+
/* Reset the save regs cache, setting register values to -1. */
void trad_frame_reset_saved_regs (struct gdbarch *gdbarch,
struct trad_frame_saved_reg *regs);