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

Reply via email to