summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Clark <james.clark@arm.com>2022-05-20 14:09:05 +0000
committerStephen M. Webb <stephen.webb@bregmasoft.ca>2022-11-07 20:00:42 -0500
commit7001a99f30b62dcfe4728dba7f9f6bc5da913077 (patch)
tree3d577226b11893ec8802ccb0f885fa69b5871565
parent267e97fe2d60831542d8ca18f807374170883984 (diff)
downloadlibunwind-7001a99f30b62dcfe4728dba7f9f6bc5da913077.tar.gz
arm64: Add support for VG register in signal frames
Allow local and remote unwinding through signal frames when variable length SVE registers are pushed onto the stack. The vector length is saved by the kernel into the signal context, but it is not scaled in the same way that dwarf expects it to be. Therefore a conditional has been added to tdep_access_regs() that scales the value depending on whether it is going to be accessed through a register or through memory. Signed-off-by: James Clark <james.clark@arm.com> Change-Id: Ie16aa22b36127ba5aa81f20280b1df7e4ba6d49b
-rw-r--r--src/aarch64/Gregs.c37
-rw-r--r--src/aarch64/Gstep.c55
-rw-r--r--src/aarch64/offsets.h7
3 files changed, 97 insertions, 2 deletions
diff --git a/src/aarch64/Gregs.c b/src/aarch64/Gregs.c
index e6328c0a..54fc2f3f 100644
--- a/src/aarch64/Gregs.c
+++ b/src/aarch64/Gregs.c
@@ -24,6 +24,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+#include "dwarf_i.h"
#include "unwind_i.h"
HIDDEN int
@@ -86,13 +87,47 @@ tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp,
case UNW_AARCH64_X29:
case UNW_AARCH64_PC:
case UNW_AARCH64_PSTATE:
- case UNW_AARCH64_VG:
loc = c->dwarf.loc[reg];
break;
case UNW_AARCH64_RA_SIGN_STATE:
Debug (1, "Reading from ra sign state not supported: %u\n", reg);
return -UNW_EBADREG;
+ case UNW_AARCH64_VG:
+ if (write)
+ {
+ Debug (1, "Writing to VG pseudo register not supported\n");
+ return -UNW_EBADREG;
+ }
+
+ if (DWARF_IS_REG_LOC(c->dwarf.loc[reg]))
+ loc = c->dwarf.loc[reg];
+ else
+ {
+ unw_addr_space_t as = c->dwarf.as;
+ unw_accessors_t *a = unw_get_accessors_int (as);
+ void *arg = c->dwarf.as_arg;
+ unw_word_t addr = DWARF_GET_LOC (c->dwarf.loc[reg]);
+ uint16_t val16;
+
+ /*
+ * If it's a memory location it means it must be in the signal context
+ * and it's saved as VL rather than VG so it needs to be scaled.
+ *
+ * linux/Documentation/arm64/sve.rst:
+ * * Vector length (VL) = size of a Z-register in bytes
+ *
+ * https://github.com/ARM-software/abi-aa/blob/main/aadwarf64/aadwarf64.rst:
+ * * Vector granule (VG) = Size in bits of the SVE vector registers
+ * in the current call frame divided by 64.
+ */
+ int ret = dwarf_readu16 (as, a, &addr, &val16, arg);
+ if (!ret)
+ *valp = (val16 * 8) / 64;
+ return ret;
+ }
+ break;
+
case UNW_AARCH64_SP:
if (write)
return -UNW_EREADONLYREG;
diff --git a/src/aarch64/Gstep.c b/src/aarch64/Gstep.c
index 560430eb..f4ef369d 100644
--- a/src/aarch64/Gstep.c
+++ b/src/aarch64/Gstep.c
@@ -24,6 +24,7 @@ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+#include "dwarf_i.h"
#include "unwind_i.h"
#include "offsets.h"
@@ -51,6 +52,58 @@ is_plt_entry (struct dwarf_cursor *c)
return ret;
}
+/*
+ * Save the location of VL (vector length) from the signal frame to the VG (vector
+ * granule) register if it exists, otherwise do nothing. If there is an error,
+ * the location is also not modified.
+ */
+static int
+get_sve_vl_signal_loc (struct dwarf_cursor* dwarf, unw_word_t sc_addr)
+{
+ uint32_t size;
+ unw_addr_space_t as = dwarf->as;
+ unw_accessors_t *a = unw_get_accessors_int (as);
+ void *arg = dwarf->as_arg;
+ unw_word_t res_addr = sc_addr + LINUX_SC_RESERVED_OFF;
+
+ /*
+ * Max possible size of reserved section is 4096. Jump by size of each record
+ * until the SVE signal context section is found.
+ */
+ for(unw_word_t section_off = 0; section_off < 4096; section_off += size)
+ {
+ uint32_t magic;
+ int ret;
+ unw_word_t item_addr = res_addr + section_off + LINUX_SC_RESERVED_MAGIC_OFF;
+ if ((ret = dwarf_readu32(as, a, &item_addr, &magic, arg)) < 0)
+ return ret;
+ item_addr = res_addr + section_off + LINUX_SC_RESERVED_SIZE_OFF;
+ if ((ret = dwarf_readu32(as, a, &item_addr, &size, arg)) < 0)
+ return ret;
+
+ switch(magic)
+ {
+ case 0:
+ /* End marker, size must be 0 */
+ return size ? -UNW_EUNSPEC : 1;
+
+ /* Magic number marking the SVE context section */
+ case 0x53564501:
+ /* Must be big enough so that the 16 bit VL value can be accessed */
+ if (size < LINUX_SC_RESERVED_SVE_VL_OFF + 2)
+ return -UNW_EUNSPEC;
+ dwarf->loc[UNW_AARCH64_VG] = DWARF_MEM_LOC(c->dwarf, res_addr + section_off +
+ LINUX_SC_RESERVED_SVE_VL_OFF);
+ return 1;
+
+ default:
+ /* Don't care about any of the other sections */
+ break;
+ }
+ }
+ return 1;
+}
+
static int
aarch64_handle_signal_frame (unw_cursor_t *cursor)
{
@@ -131,7 +184,7 @@ aarch64_handle_signal_frame (unw_cursor_t *cursor)
c->dwarf.pi_valid = 0;
c->dwarf.use_prev_instr = 0;
- return 1;
+ return get_sve_vl_signal_loc (&c->dwarf, sc_addr);
}
int
diff --git a/src/aarch64/offsets.h b/src/aarch64/offsets.h
index e78251d0..52291edb 100644
--- a/src/aarch64/offsets.h
+++ b/src/aarch64/offsets.h
@@ -47,3 +47,10 @@
#define LINUX_SC_SP_OFF 0x100
#define LINUX_SC_PC_OFF 0x108
#define LINUX_SC_PSTATE_OFF 0x110
+#define LINUX_SC_RESERVED_OFF 0x120
+
+// struct _aarch64_ctx { __u32 magic; __u32 size; };
+// struct sve_context { struct _aarch64_ctx head; __u16 vl; __u16 __reserved[3]; };
+#define LINUX_SC_RESERVED_MAGIC_OFF 0x0
+#define LINUX_SC_RESERVED_SIZE_OFF 0x4
+#define LINUX_SC_RESERVED_SVE_VL_OFF 0x8 \ No newline at end of file