From: German Gomez <german.go...@arm.com> Add user API for setting the PAC mask. The function is intended to be called in Dwfl_Thread_Callbacks.set_initial_registers.
Testing notes: ... consider the following program.c | int a = 0; | void leaf(void) { | for (;;) | a += a; | } | void parent(void) { | leaf(); | } | int main(void) { | parent(); | return 0; | } ... compiled with "gcc-10 -O0 -g -mbranch-protection=pac-ret+leaf program.c" ... should yield the correct call stack, without mangled addresses: | $ eu-stack -p <PID> | | PID 760267 - process | TID 760267: | #0 0x0000aaaaaebd0804 leaf | #1 0x0000aaaaaebd0818 parent | #2 0x0000aaaaaebd0838 main | #3 0x0000ffffbd52ad50 __libc_start_main | #4 0x0000aaaaaebd0694 $x Signed-off-by: German Gomez <german.go...@arm.com> Signed-off-by: Steve Capper <steve.cap...@arm.com> --- libdw/libdw.map | 1 + libdwfl/dwfl_frame_regs.c | 10 ++++++++++ libdwfl/libdwfl.h | 6 ++++++ libdwfl/linux-pid-attach.c | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/libdw/libdw.map b/libdw/libdw.map index 3c5ce8dc..84393f4b 100644 --- a/libdw/libdw.map +++ b/libdw/libdw.map @@ -377,4 +377,5 @@ ELFUTILS_0.188 { ELFUTILS_0.191 { global: dwarf_cu_dwp_section_info; + dwfl_thread_state_aarch64_pauth; } ELFUTILS_0.188; diff --git a/libdwfl/dwfl_frame_regs.c b/libdwfl/dwfl_frame_regs.c index a4bd3884..29e3d0b1 100644 --- a/libdwfl/dwfl_frame_regs.c +++ b/libdwfl/dwfl_frame_regs.c @@ -71,3 +71,13 @@ dwfl_frame_reg (Dwfl_Frame *state, unsigned regno, Dwarf_Word *val) return res; } INTDEF(dwfl_frame_reg) + +void +dwfl_thread_state_aarch64_pauth(Dwfl_Thread *thread, Dwarf_Word insn_mask) +{ + Dwfl_Frame *state = thread->unwound; + assert (state && state->unwound == NULL); + assert (state->initial_frame); + thread->aarch64.pauth_insn_mask = insn_mask; +} +INTDEF(dwfl_thread_state_aarch64_pauth) diff --git a/libdwfl/libdwfl.h b/libdwfl/libdwfl.h index 49ad6664..3ab07514 100644 --- a/libdwfl/libdwfl.h +++ b/libdwfl/libdwfl.h @@ -763,6 +763,12 @@ bool dwfl_thread_state_registers (Dwfl_Thread *thread, int firstreg, void dwfl_thread_state_register_pc (Dwfl_Thread *thread, Dwarf_Word pc) __nonnull_attribute__ (1); +/* Called by Dwfl_Thread_Callbacks.set_initial_registers implementation. + On AARCH64 platforms with Pointer Authentication, the bits from this mask + indicate the position of the PAC bits in return addresses. */ +void dwfl_thread_state_aarch64_pauth (Dwfl_Thread *thread, Dwarf_Word insn_mask) + __nonnull_attribute__ (1); + /* Iterate through the threads for a process. Returns zero if all threads have been processed by the callback, returns -1 on error, or the value of the callback when not DWARF_CB_OK. -1 returned on error will set dwfl_errno (). diff --git a/libdwfl/linux-pid-attach.c b/libdwfl/linux-pid-attach.c index de867857..e68af4a9 100644 --- a/libdwfl/linux-pid-attach.c +++ b/libdwfl/linux-pid-attach.c @@ -320,6 +320,28 @@ pid_thread_state_registers_cb (int firstreg, unsigned nregs, return INTUSE(dwfl_thread_state_registers) (thread, firstreg, nregs, regs); } +#if defined(__aarch64__) + +#include <linux/ptrace.h> /* struct user_pac_mask */ + +static void +pid_set_aarch64_pauth(Dwfl_Thread *thread, pid_t tid) +{ + struct user_pac_mask pac_mask; + struct iovec iovec; + + iovec.iov_base = &pac_mask; + iovec.iov_len = sizeof (pac_mask); + + /* If the ptrace returns an error, the system may not support pointer + authentication. In that case, set the masks to 0 (no PAC bits). */ + if (ptrace(PTRACE_GETREGSET, tid, NT_ARM_PAC_MASK, &iovec)) + pac_mask.insn_mask = 0; + + INTUSE(dwfl_thread_state_aarch64_pauth) (thread, pac_mask.insn_mask); +} +#endif /* __aarch64__ */ + static bool pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg) { @@ -332,8 +354,16 @@ pid_set_initial_registers (Dwfl_Thread *thread, void *thread_arg) pid_arg->tid_attached = tid; Dwfl_Process *process = thread->process; Ebl *ebl = process->ebl; - return ebl_set_initial_registers_tid (ebl, tid, - pid_thread_state_registers_cb, thread); + if (!ebl_set_initial_registers_tid (ebl, tid, + pid_thread_state_registers_cb, thread)) + return false; + +#if defined(__aarch64__) + /* Set aarch64 pointer authentication data. */ + pid_set_aarch64_pauth(thread, tid); +#endif + + return true; } static void -- 2.39.2