Skip to content

Commit 1270608

Browse files
enh-googleGerrit Code Review
authored and
Gerrit Code Review
committed
Merge "libunwindstack: support for Armv8.3-A Pointer Authentication"
2 parents aa0358b + 6835d01 commit 1270608

17 files changed

+288
-57
lines changed

libunwindstack/DwarfCfa.cpp

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626

2727
#include <unwindstack/DwarfError.h>
2828
#include <unwindstack/DwarfLocation.h>
29+
#include <unwindstack/Elf.h>
2930
#include <unwindstack/Log.h>
31+
#include <unwindstack/MachineArm64.h>
3032

3133
#include "DwarfCfa.h"
3234
#include "DwarfEncoding.h"
@@ -204,8 +206,12 @@ template <typename AddressType>
204206
bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
205207
uint64_t* cur_pc) {
206208
const auto* cfa = &DwarfCfaInfo::kTable[op];
207-
if (cfa->name[0] == '\0') {
208-
log(indent, "Illegal");
209+
if (cfa->name[0] == '\0' || (arch_ != ARCH_ARM64 && op == 0x2d)) {
210+
if (op == 0x2d) {
211+
log(indent, "Illegal (Only valid on aarch64)");
212+
} else {
213+
log(indent, "Illegal");
214+
}
209215
log(indent, "Raw Data: 0x%02x", op);
210216
return true;
211217
}
@@ -514,6 +520,24 @@ bool DwarfCfa<AddressType>::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* l
514520
return true;
515521
}
516522

523+
template <typename AddressType>
524+
bool DwarfCfa<AddressType>::cfa_aarch64_negate_ra_state(dwarf_loc_regs_t* loc_regs) {
525+
// Only supported on aarch64.
526+
if (arch_ != ARCH_ARM64) {
527+
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
528+
return false;
529+
}
530+
531+
auto cfa_location = loc_regs->find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
532+
if (cfa_location == loc_regs->end()) {
533+
(*loc_regs)[Arm64Reg::ARM64_PREG_RA_SIGN_STATE] = {.type = DWARF_LOCATION_PSEUDO_REGISTER,
534+
.values = {1}};
535+
} else {
536+
cfa_location->second.values[0] ^= 1;
537+
}
538+
return true;
539+
}
540+
517541
const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
518542
{
519543
// 0x00 DW_CFA_nop
@@ -699,7 +723,13 @@ const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
699723
{"", 0, 0, {}, {}}, // 0x2a illegal cfa
700724
{"", 0, 0, {}, {}}, // 0x2b illegal cfa
701725
{"", 0, 0, {}, {}}, // 0x2c illegal cfa
702-
{"", 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
726+
{
727+
"DW_CFA_AARCH64_negate_ra_state", // 0x2d DW_CFA_AARCH64_negate_ra_state
728+
3,
729+
0,
730+
{},
731+
{},
732+
},
703733
{
704734
"DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
705735
2,

libunwindstack/DwarfCfa.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@
3131

3232
namespace unwindstack {
3333

34+
// Forward declarations.
35+
enum ArchEnum : uint8_t;
36+
3437
// DWARF Standard home: http://dwarfstd.org/
3538
// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
3639
// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
@@ -72,7 +75,8 @@ class DwarfCfa {
7275
typedef typename std::make_signed<AddressType>::type SignedType;
7376

7477
public:
75-
DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
78+
DwarfCfa(DwarfMemory* memory, const DwarfFde* fde, ArchEnum arch)
79+
: memory_(memory), fde_(fde), arch_(arch) {}
7680
virtual ~DwarfCfa() = default;
7781

7882
bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
@@ -99,6 +103,7 @@ class DwarfCfa {
99103
DwarfErrorData last_error_;
100104
DwarfMemory* memory_;
101105
const DwarfFde* fde_;
106+
ArchEnum arch_;
102107

103108
AddressType cur_pc_;
104109
const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
@@ -128,6 +133,7 @@ class DwarfCfa {
128133
bool cfa_val_offset_sf(dwarf_loc_regs_t*);
129134
bool cfa_val_expression(dwarf_loc_regs_t*);
130135
bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
136+
bool cfa_aarch64_negate_ra_state(dwarf_loc_regs_t*);
131137

132138
using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
133139
constexpr static process_func kCallbackTable[64] = {
@@ -221,8 +227,9 @@ class DwarfCfa {
221227
nullptr,
222228
// 0x2c illegal cfa
223229
nullptr,
224-
// 0x2d DW_CFA_GNU_window_save (Treat this as illegal)
225-
nullptr,
230+
// 0x2d DW_CFA_AARCH64_negate_ra_state (aarch64 only)
231+
// DW_CFA_GNU_window_save on other architectures.
232+
&DwarfCfa::cfa_aarch64_negate_ra_state,
226233
// 0x2e DW_CFA_GNU_args_size
227234
&DwarfCfa::cfa_nop,
228235
// 0x2f DW_CFA_GNU_negative_offset_extended

libunwindstack/DwarfSection.cpp

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <unwindstack/DwarfMemory.h>
2222
#include <unwindstack/DwarfSection.h>
2323
#include <unwindstack/DwarfStructs.h>
24+
#include <unwindstack/Elf.h>
2425
#include <unwindstack/Log.h>
2526
#include <unwindstack/Memory.h>
2627
#include <unwindstack/Regs.h>
@@ -49,7 +50,7 @@ bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* f
4950

5051
// Now get the location information for this pc.
5152
dwarf_loc_regs_t loc_regs;
52-
if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
53+
if (!GetCfaLocationInfo(pc, fde, &loc_regs, regs->Arch())) {
5354
return false;
5455
}
5556
loc_regs.cie = fde->cie;
@@ -464,6 +465,13 @@ bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint3
464465
eval_info->return_address_undefined = true;
465466
}
466467
break;
468+
case DWARF_LOCATION_PSEUDO_REGISTER: {
469+
if (!eval_info->regs_info.regs->SetPseudoRegister(reg, loc->values[0])) {
470+
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
471+
return false;
472+
}
473+
break;
474+
}
467475
default:
468476
break;
469477
}
@@ -491,6 +499,10 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
491499
// Always set the dex pc to zero when evaluating.
492500
cur_regs->set_dex_pc(0);
493501

502+
// Reset necessary pseudo registers before evaluation.
503+
// This is needed for ARM64, for example.
504+
regs->ResetPseudoRegisters();
505+
494506
EvalInfo<AddressType> eval_info{.loc_regs = &loc_regs,
495507
.cie = cie,
496508
.regular_memory = regular_memory,
@@ -527,8 +539,10 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
527539

528540
AddressType* reg_ptr;
529541
if (reg >= cur_regs->total_regs()) {
530-
// Skip this unknown register.
531-
continue;
542+
if (entry.second.type != DWARF_LOCATION_PSEUDO_REGISTER) {
543+
// Skip this unknown register.
544+
continue;
545+
}
532546
}
533547

534548
reg_ptr = eval_info.regs_info.Save(reg);
@@ -554,8 +568,8 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
554568

555569
template <typename AddressType>
556570
bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
557-
dwarf_loc_regs_t* loc_regs) {
558-
DwarfCfa<AddressType> cfa(&memory_, fde);
571+
dwarf_loc_regs_t* loc_regs, ArchEnum arch) {
572+
DwarfCfa<AddressType> cfa(&memory_, fde, arch);
559573

560574
// Look for the cached copy of the cie data.
561575
auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
@@ -576,8 +590,9 @@ bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfF
576590
}
577591

578592
template <typename AddressType>
579-
bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) {
580-
DwarfCfa<AddressType> cfa(&memory_, fde);
593+
bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, const DwarfFde* fde,
594+
ArchEnum arch) {
595+
DwarfCfa<AddressType> cfa(&memory_, fde, arch);
581596

582597
// Always print the cie information.
583598
const DwarfCie* cie = fde->cie;

libunwindstack/RegsArm64.cpp

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,10 @@
3030
namespace unwindstack {
3131

3232
RegsArm64::RegsArm64()
33-
: RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
33+
: RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {
34+
ResetPseudoRegisters();
35+
pac_mask_ = 0;
36+
}
3437

3538
ArchEnum RegsArm64::Arch() {
3639
return ARCH_ARM64;
@@ -45,6 +48,23 @@ uint64_t RegsArm64::sp() {
4548
}
4649

4750
void RegsArm64::set_pc(uint64_t pc) {
51+
// If the target is aarch64 then the return address may have been
52+
// signed using the Armv8.3-A Pointer Authentication extension. The
53+
// original return address can be restored by stripping out the
54+
// authentication code using a mask or xpaclri. xpaclri is a NOP on
55+
// pre-Armv8.3-A architectures.
56+
if ((0 != pc) && IsRASigned()) {
57+
if (pac_mask_) {
58+
pc &= ~pac_mask_;
59+
#if defined(__aarch64__)
60+
} else {
61+
register uint64_t x30 __asm("x30") = pc;
62+
// This is XPACLRI.
63+
asm("hint 0x7" : "+r"(x30));
64+
pc = x30;
65+
#endif
66+
}
67+
}
4868
regs_[ARM64_REG_PC] = pc;
4969
}
5070

@@ -144,6 +164,37 @@ bool RegsArm64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* proce
144164
return true;
145165
}
146166

167+
void RegsArm64::ResetPseudoRegisters(void) {
168+
// DWARF for AArch64 says RA_SIGN_STATE should be initialized to 0.
169+
this->SetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, 0);
170+
}
171+
172+
bool RegsArm64::SetPseudoRegister(uint16_t id, uint64_t value) {
173+
if ((id >= Arm64Reg::ARM64_PREG_FIRST) && (id < Arm64Reg::ARM64_PREG_LAST)) {
174+
pseudo_regs_[id - Arm64Reg::ARM64_PREG_FIRST] = value;
175+
return true;
176+
}
177+
return false;
178+
}
179+
180+
bool RegsArm64::GetPseudoRegister(uint16_t id, uint64_t* value) {
181+
if ((id >= Arm64Reg::ARM64_PREG_FIRST) && (id < Arm64Reg::ARM64_PREG_LAST)) {
182+
*value = pseudo_regs_[id - Arm64Reg::ARM64_PREG_FIRST];
183+
return true;
184+
}
185+
return false;
186+
}
187+
188+
bool RegsArm64::IsRASigned() {
189+
uint64_t value;
190+
auto result = this->GetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, &value);
191+
return (result && (value != 0));
192+
}
193+
194+
void RegsArm64::SetPACMask(uint64_t mask) {
195+
pac_mask_ = mask;
196+
}
197+
147198
Regs* RegsArm64::Clone() {
148199
return new RegsArm64(*this);
149200
}

libunwindstack/include/unwindstack/DwarfLocation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ enum DwarfLocationEnum : uint8_t {
3333
DWARF_LOCATION_REGISTER,
3434
DWARF_LOCATION_EXPRESSION,
3535
DWARF_LOCATION_VAL_EXPRESSION,
36+
DWARF_LOCATION_PSEUDO_REGISTER,
3637
};
3738

3839
struct DwarfLocation {

libunwindstack/include/unwindstack/DwarfSection.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
namespace unwindstack {
3232

3333
// Forward declarations.
34+
enum ArchEnum : uint8_t;
3435
class Memory;
3536
class Regs;
3637
template <typename AddressType>
@@ -90,13 +91,14 @@ class DwarfSection {
9091

9192
virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
9293

93-
virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
94+
virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde, ArchEnum arch) = 0;
9495

9596
virtual void GetFdes(std::vector<const DwarfFde*>* fdes) = 0;
9697

9798
virtual const DwarfFde* GetFdeFromPc(uint64_t pc) = 0;
9899

99-
virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
100+
virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs,
101+
ArchEnum arch) = 0;
100102

101103
virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
102104

@@ -140,9 +142,10 @@ class DwarfSectionImpl : public DwarfSection {
140142
bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
141143
Regs* regs, bool* finished) override;
142144

143-
bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
145+
bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs,
146+
ArchEnum arch) override;
144147

145-
bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
148+
bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde, ArchEnum arch) override;
146149

147150
protected:
148151
bool GetNextCieOrFde(const DwarfFde** fde_entry);

libunwindstack/include/unwindstack/MachineArm64.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,13 @@ enum Arm64Reg : uint16_t {
6060

6161
ARM64_REG_SP = ARM64_REG_R31,
6262
ARM64_REG_LR = ARM64_REG_R30,
63+
64+
// Pseudo registers. These are not machine registers.
65+
66+
// AARCH64 Return address signed state pseudo-register
67+
ARM64_PREG_RA_SIGN_STATE = 34,
68+
ARM64_PREG_FIRST = ARM64_PREG_RA_SIGN_STATE,
69+
ARM64_PREG_LAST,
6370
};
6471

6572
} // namespace unwindstack

libunwindstack/include/unwindstack/Regs.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ class Regs {
6464
uint64_t dex_pc() { return dex_pc_; }
6565
void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
6666

67+
virtual void ResetPseudoRegisters() {}
68+
virtual bool SetPseudoRegister(uint16_t, uint64_t) { return false; }
69+
virtual bool GetPseudoRegister(uint16_t, uint64_t*) { return false; }
70+
6771
virtual bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) = 0;
6872

6973
virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;

libunwindstack/include/unwindstack/RegsArm64.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <functional>
2323

2424
#include <unwindstack/Elf.h>
25+
#include <unwindstack/MachineArm64.h>
2526
#include <unwindstack/Regs.h>
2627

2728
namespace unwindstack {
@@ -48,11 +49,25 @@ class RegsArm64 : public RegsImpl<uint64_t> {
4849
void set_pc(uint64_t pc) override;
4950
void set_sp(uint64_t sp) override;
5051

52+
void ResetPseudoRegisters() override;
53+
54+
bool SetPseudoRegister(uint16_t id, uint64_t value) override;
55+
56+
bool GetPseudoRegister(uint16_t id, uint64_t* value) override;
57+
58+
bool IsRASigned();
59+
60+
void SetPACMask(uint64_t mask);
61+
5162
Regs* Clone() override final;
5263

5364
static Regs* Read(void* data);
5465

5566
static Regs* CreateFromUcontext(void* ucontext);
67+
68+
protected:
69+
uint64_t pseudo_regs_[Arm64Reg::ARM64_PREG_LAST - Arm64Reg::ARM64_PREG_FIRST];
70+
uint64_t pac_mask_;
5671
};
5772

5873
} // namespace unwindstack

0 commit comments

Comments
 (0)