[v2,4/5] libdwfl, aarch64: extend dwfl_thread_state_registers to handle PAC

Message ID 20240826105257.2669-5-steve.capper@arm.com
State Committed
Headers
Series Enable PAC support in elfutils |

Commit Message

Steve Capper Aug. 26, 2024, 10:52 a.m. UTC
  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(-)
  

Patch

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);
 }