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.capper@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(-)
@@ -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 */
@@ -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]))
{
@@ -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);
}