On AArch64 systems with pointer authentication enabled, one needs to know the PAC mask in order to unwind functions that employ PAC.
This patch extends dwfl_thread_state_registers to handle the PAC mask information by introducing a special register -2. (-1 is used in a similar manner already for handling the program counter). The AArch64 linux process attach logic is also extended to query ptrace for the PAC mask. A subsequent patch will add support for retrieving the PAC mask from an AArch64 linux core file. Signed-off-by: Steve Capper <steve.cap...@arm.com> --- backends/aarch64_initreg.c | 10 ++++++++++ libdwfl/dwfl_frame_regs.c | 6 ++++++ libdwfl/linux-pid-attach.c | 9 +++++++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/backends/aarch64_initreg.c b/backends/aarch64_initreg.c index 4661068a..5ec45ea6 100644 --- a/backends/aarch64_initreg.c +++ b/backends/aarch64_initreg.c @@ -36,6 +36,7 @@ # include <linux/uio.h> # include <sys/user.h> # include <sys/ptrace.h> +# include <asm/ptrace.h> /* Deal with old glibc defining user_pt_regs instead of user_regs_struct. */ # ifndef HAVE_SYS_USER_REGS # define user_regs_struct user_pt_regs @@ -57,12 +58,18 @@ aarch64_set_initial_registers_tid (pid_t tid __attribute__ ((unused)), /* General registers. */ struct user_regs_struct gregs; + struct user_pac_mask pac_mask; struct iovec iovec; iovec.iov_base = &gregs; iovec.iov_len = sizeof (gregs); if (ptrace (PTRACE_GETREGSET, tid, NT_PRSTATUS, &iovec) != 0) return false; + iovec.iov_base = &pac_mask; + iovec.iov_len = sizeof (pac_mask); + if (ptrace (PTRACE_GETREGSET, tid, NT_ARM_PAC_MASK, &iovec) != 0) + pac_mask.insn_mask = 0; + /* X0..X30 plus SP. */ if (! setfunc (0, 32, (Dwarf_Word *) &gregs.regs[0], arg)) return false; @@ -71,6 +78,9 @@ aarch64_set_initial_registers_tid (pid_t tid __attribute__ ((unused)), if (! setfunc (-1, 1, (Dwarf_Word *) &gregs.pc, arg)) return false; + if (! setfunc (-2, 1, (Dwarf_Word *) &pac_mask.insn_mask, arg)) + return false; + /* ELR cannot be found. */ /* RA_SIGN_STATE cannot be found */ diff --git a/libdwfl/dwfl_frame_regs.c b/libdwfl/dwfl_frame_regs.c index a4bd3884..572ac676 100644 --- a/libdwfl/dwfl_frame_regs.c +++ b/libdwfl/dwfl_frame_regs.c @@ -39,6 +39,12 @@ dwfl_thread_state_registers (Dwfl_Thread *thread, int firstreg, Dwfl_Frame *state = thread->unwound; assert (state && state->unwound == NULL); assert (state->initial_frame); + + if (firstreg == -2 && nregs == 1) { + thread->aarch64.pauth_insn_mask = regs[0]; + return true; + } + for (unsigned regno = firstreg; regno < firstreg + nregs; regno++) if (! __libdwfl_frame_reg_set (state, regno, regs[regno - firstreg])) { diff --git a/libdwfl/linux-pid-attach.c b/libdwfl/linux-pid-attach.c index de867857..0eec1e88 100644 --- a/libdwfl/linux-pid-attach.c +++ b/libdwfl/linux-pid-attach.c @@ -309,13 +309,18 @@ pid_thread_state_registers_cb (int firstreg, unsigned nregs, const Dwarf_Word *regs, void *arg) { Dwfl_Thread *thread = (Dwfl_Thread *) arg; - if (firstreg < 0) + if (firstreg == -1) { - assert (firstreg == -1); assert (nregs == 1); INTUSE(dwfl_thread_state_register_pc) (thread, *regs); return true; } + else if (firstreg == -2) + { + assert (nregs == 1); + INTUSE(dwfl_thread_state_registers) (thread, firstreg, nregs, regs); + return true; + } assert (nregs > 0); return INTUSE(dwfl_thread_state_registers) (thread, firstreg, nregs, regs); } -- 2.43.0