libunwindstack: support for Armv8.3-A Pointer Authentication
This patch adds support for handling return addresses signed with
pointer authentication. It simply strips the authentication code
without verifying its correctness, and thus works with both A and B
keys and through key-change boundaries.
Additons:
* DW_CFA_AARCH64_negate_ra_state: new CFA operation.
* RA_SIGN_STATE: new pseudo register.
* Pass the arch to DwarfCfa so that the new op is only executed
on aarch64.
The stripping uses the xpaclri instruction. This is a hint space
instruction which is compatible with pre Armv8.3-A devices. For cases
where it cannot be used, a mask can be set instead.
Test: libunwindstack_test
Without this patch all UnwindTest.* testcases should fail if
compiled with Pointer Authentication.
The tests should be executed with both -mbranch-protection=pac-ret and
pac-ret+leaf flags so that either some or all functions have pointer
authentication instructions.
Change-Id: Id7c3f1d0e2fc7fccb19bd1430826264405a9df7c
diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp
index 18bd490..9e2a3cd 100644
--- a/libunwindstack/DwarfSection.cpp
+++ b/libunwindstack/DwarfSection.cpp
@@ -21,6 +21,7 @@
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfSection.h>
#include <unwindstack/DwarfStructs.h>
+#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
#include <unwindstack/Memory.h>
#include <unwindstack/Regs.h>
@@ -49,7 +50,7 @@
// Now get the location information for this pc.
dwarf_loc_regs_t loc_regs;
- if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
+ if (!GetCfaLocationInfo(pc, fde, &loc_regs, regs->Arch())) {
return false;
}
loc_regs.cie = fde->cie;
@@ -464,6 +465,13 @@
eval_info->return_address_undefined = true;
}
break;
+ case DWARF_LOCATION_PSEUDO_REGISTER: {
+ if (!eval_info->regs_info.regs->SetPseudoRegister(reg, loc->values[0])) {
+ last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
+ return false;
+ }
+ break;
+ }
default:
break;
}
@@ -491,6 +499,10 @@
// Always set the dex pc to zero when evaluating.
cur_regs->set_dex_pc(0);
+ // Reset necessary pseudo registers before evaluation.
+ // This is needed for ARM64, for example.
+ regs->ResetPseudoRegisters();
+
EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
.cie = cie,
.regular_memory = regular_memory,
@@ -527,8 +539,10 @@
AddressType* reg_ptr;
if (reg >= cur_regs->total_regs()) {
- // Skip this unknown register.
- continue;
+ if (entry.second.type != DWARF_LOCATION_PSEUDO_REGISTER) {
+ // Skip this unknown register.
+ continue;
+ }
}
reg_ptr = eval_info.regs_info.Save(reg);
@@ -554,8 +568,8 @@
template <typename AddressType>
bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
- dwarf_loc_regs_t* loc_regs) {
- DwarfCfa<AddressType> cfa(&memory_, fde);
+ dwarf_loc_regs_t* loc_regs, ArchEnum arch) {
+ DwarfCfa<AddressType> cfa(&memory_, fde, arch);
// Look for the cached copy of the cie data.
auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
@@ -576,8 +590,9 @@
}
template <typename AddressType>
-bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
- DwarfCfa<AddressType> cfa(&memory_, fde);
+bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde,
+ ArchEnum arch) {
+ DwarfCfa<AddressType> cfa(&memory_, fde, arch);
// Always print the cie information.
const DwarfCie* cie = fde->cie;