diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/aarch64/Ginit.c | 6 | ||||
-rw-r--r-- | src/aarch64/Gregs.c | 3 | ||||
-rw-r--r-- | src/dwarf/Gparser.c | 80 |
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; } |