summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/aarch64/Ginit.c6
-rw-r--r--src/aarch64/Gregs.c3
-rw-r--r--src/dwarf/Gparser.c80
3 files changed, 89 insertions, 0 deletions
diff --git a/src/aarch64/Ginit.c b/src/aarch64/Ginit.c
index fe6e511d..17b8fcbc 100644
--- a/src/aarch64/Ginit.c
+++ b/src/aarch64/Ginit.c
@@ -408,6 +408,11 @@ get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
}
+static unw_word_t empty_ptrauth_mask(unw_addr_space_t addr_space_unused, void *as_arg_unused)
+{
+ return 0;
+}
+
HIDDEN void
aarch64_local_addr_space_init (void)
{
@@ -421,6 +426,7 @@ aarch64_local_addr_space_init (void)
local_addr_space.acc.access_fpreg = access_fpreg;
local_addr_space.acc.resume = aarch64_local_resume;
local_addr_space.acc.get_proc_name = get_static_proc_name;
+ local_addr_space.acc.ptrauth_insn_mask = empty_ptrauth_mask;
local_addr_space.big_endian = target_is_big_endian();
unw_flush_cache (&local_addr_space, 0, 0);
}
diff --git a/src/aarch64/Gregs.c b/src/aarch64/Gregs.c
index a8843734..5ae47254 100644
--- a/src/aarch64/Gregs.c
+++ b/src/aarch64/Gregs.c
@@ -88,6 +88,9 @@ tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp,
case UNW_AARCH64_PSTATE:
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_SP:
if (write)
diff --git a/src/dwarf/Gparser.c b/src/dwarf/Gparser.c
index 7cfc4e50..edd34526 100644
--- a/src/dwarf/Gparser.c
+++ b/src/dwarf/Gparser.c
@@ -87,6 +87,13 @@ empty_rstate_stack(dwarf_stackable_reg_state_t **rs_stack)
pop_rstate_stack(rs_stack);
}
+#ifdef UNW_TARGET_AARCH64
+
+static void
+aarch64_negate_ra_sign_state(dwarf_state_record_t *sr);
+
+#endif
+
/* Run a CFI program to update the register state. */
static int
run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr,
@@ -404,6 +411,11 @@ run_cfi_program (struct dwarf_cursor *c, dwarf_state_record_t *sr,
(regnum - 16) * sizeof (unw_word_t));
Debug (15, "CFA_GNU_window_save\n");
break;
+#elif UNW_TARGET_AARCH64
+ /* This is a specific opcode on aarch64, DW_CFA_AARCH64_negate_ra_state */
+ Debug (15, "DW_CFA_AARCH64_negate_ra_state\n");
+ aarch64_negate_ra_sign_state(sr);
+ break;
#else
/* FALL THROUGH */
#endif
@@ -767,6 +779,61 @@ eval_location_expr (struct dwarf_cursor *c, unw_word_t stack_val, unw_addr_space
return 0;
}
+
+#ifdef UNW_TARGET_AARCH64
+#include "libunwind-aarch64.h"
+
+static void
+aarch64_negate_ra_sign_state(dwarf_state_record_t *sr)
+{
+ unw_word_t ra_sign_state = sr->rs_current.reg.val[UNW_AARCH64_RA_SIGN_STATE];
+ ra_sign_state ^= 0x1;
+ set_reg(sr, UNW_AARCH64_RA_SIGN_STATE, DWARF_WHERE_SAME, ra_sign_state);
+}
+
+static unw_word_t
+aarch64_strip_pac_remote(unw_accessors_t *a, unw_addr_space_t as, void *arg, unw_word_t old_ip)
+{
+ if (a->ptrauth_insn_mask)
+ {
+ unw_word_t ip, insn_mask;
+
+ insn_mask = a->ptrauth_insn_mask(as, arg);
+ ip = old_ip & (~insn_mask);
+
+ Debug(15, "stripping pac from address, before: %lx, after: %lx\n", old_ip, ip);
+ return ip;
+ }
+ else
+ {
+ Debug(15, "return address %lx might be signed, but no means to obtain mask\n", old_ip);
+ return old_ip;
+ }
+}
+
+static unw_word_t
+aarch64_strip_pac_local(unw_word_t in_addr)
+{
+ unw_word_t out_addr = in_addr;
+
+#ifdef __aarch64__
+ // Strip the PAC with XPACLRI instruction
+ register unsigned long long x30 __asm__("x30") = in_addr;
+ __asm__("hint 0x7" : "+r" (x30));
+ out_addr = x30;
+#endif
+
+ return out_addr;
+}
+
+static unw_word_t
+aarch64_get_ra_sign_state(struct dwarf_reg_state *rs)
+{
+ return rs->reg.val[UNW_AARCH64_RA_SIGN_STATE];
+}
+
+#endif
+
static int
apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs)
{
@@ -892,6 +959,19 @@ apply_reg_state (struct dwarf_cursor *c, struct dwarf_reg_state *rs)
ret = dwarf_get (c, c->loc[rs->ret_addr_column], &ip);
if (ret < 0)
return ret;
+#ifdef UNW_TARGET_AARCH64
+ if (aarch64_get_ra_sign_state(rs))
+ {
+ if (c->as != unw_local_addr_space)
+ {
+ ip = aarch64_strip_pac_remote(a, as, arg, ip);
+ }
+ else
+ {
+ ip = aarch64_strip_pac_local(ip);
+ }
+ }
+#endif
c->ip = ip;
ret = 1;
}