[4/4] libdwfl, eu-stack, aarch64: Add API for setting AARCH64 PAC mask.
Commit Message
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.gomez@arm.com>
---
libdw/libdw.map | 5 +++++
libdwfl/dwfl_frame_regs.c | 10 ++++++++++
libdwfl/libdwfl.h | 6 ++++++
libdwfl/linux-pid-attach.c | 34 ++++++++++++++++++++++++++++++++--
4 files changed, 53 insertions(+), 2 deletions(-)
@@ -366,3 +366,8 @@ ELFUTILS_0.186 {
dwarf_linecontext;
dwarf_linefunctionname;
} ELFUTILS_0.177;
+
+ELFUTILS_0.188 {
+ global:
+ dwfl_thread_state_aarch64_pauth;
+} ELFUTILS_0.186;
@@ -59,3 +59,13 @@ dwfl_thread_state_register_pc (Dwfl_Thread *thread, Dwarf_Word pc)
state->pc_state = DWFL_FRAME_STATE_PC_SET;
}
INTDEF(dwfl_thread_state_register_pc)
+
+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)
@@ -753,6 +753,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 ().
@@ -321,6 +321,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)
{
@@ -333,8 +355,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