Merge "libunwindstack: support for Armv8.3-A Pointer Authentication"

This commit is contained in:
Elliott Hughes 2020-07-17 18:01:19 +00:00 committed by Gerrit Code Review
commit 1270608dbf
17 changed files with 288 additions and 57 deletions

View file

@ -26,7 +26,9 @@
#include <unwindstack/DwarfError.h>
#include <unwindstack/DwarfLocation.h>
#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
#include <unwindstack/MachineArm64.h>
#include "DwarfCfa.h"
#include "DwarfEncoding.h"
@ -204,8 +206,12 @@ template <typename AddressType>
bool DwarfCfa<AddressType>::LogInstruction(uint32_t indent, uint64_t cfa_offset, uint8_t op,
uint64_t* cur_pc) {
const auto* cfa = &DwarfCfaInfo::kTable[op];
if (cfa->name[0] == '\0') {
log(indent, "Illegal");
if (cfa->name[0] == '\0' || (arch_ != ARCH_ARM64 && op == 0x2d)) {
if (op == 0x2d) {
log(indent, "Illegal (Only valid on aarch64)");
} else {
log(indent, "Illegal");
}
log(indent, "Raw Data: 0x%02x", op);
return true;
}
@ -514,6 +520,24 @@ bool DwarfCfa<AddressType>::cfa_gnu_negative_offset_extended(dwarf_loc_regs_t* l
return true;
}
template <typename AddressType>
bool DwarfCfa<AddressType>::cfa_aarch64_negate_ra_state(dwarf_loc_regs_t* loc_regs) {
// Only supported on aarch64.
if (arch_ != ARCH_ARM64) {
last_error_.code = DWARF_ERROR_ILLEGAL_VALUE;
return false;
}
auto cfa_location = loc_regs->find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
if (cfa_location == loc_regs->end()) {
(*loc_regs)[Arm64Reg::ARM64_PREG_RA_SIGN_STATE] = {.type = DWARF_LOCATION_PSEUDO_REGISTER,
.values = {1}};
} else {
cfa_location->second.values[0] ^= 1;
}
return true;
}
const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
{
// 0x00 DW_CFA_nop
@ -699,7 +723,13 @@ const DwarfCfaInfo::Info DwarfCfaInfo::kTable[64] = {
{"", 0, 0, {}, {}}, // 0x2a illegal cfa
{"", 0, 0, {}, {}}, // 0x2b illegal cfa
{"", 0, 0, {}, {}}, // 0x2c illegal cfa
{"", 0, 0, {}, {}}, // 0x2d DW_CFA_GNU_window_save (Treat as illegal)
{
"DW_CFA_AARCH64_negate_ra_state", // 0x2d DW_CFA_AARCH64_negate_ra_state
3,
0,
{},
{},
},
{
"DW_CFA_GNU_args_size", // 0x2e DW_CFA_GNU_args_size
2,

View file

@ -31,6 +31,9 @@
namespace unwindstack {
// Forward declarations.
enum ArchEnum : uint8_t;
// DWARF Standard home: http://dwarfstd.org/
// This code is based on DWARF 4: http://http://dwarfstd.org/doc/DWARF4.pdf
// See section 6.4.2.1 for a description of the DW_CFA_xxx values.
@ -72,7 +75,8 @@ class DwarfCfa {
typedef typename std::make_signed<AddressType>::type SignedType;
public:
DwarfCfa(DwarfMemory* memory, const DwarfFde* fde) : memory_(memory), fde_(fde) {}
DwarfCfa(DwarfMemory* memory, const DwarfFde* fde, ArchEnum arch)
: memory_(memory), fde_(fde), arch_(arch) {}
virtual ~DwarfCfa() = default;
bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
@ -99,6 +103,7 @@ class DwarfCfa {
DwarfErrorData last_error_;
DwarfMemory* memory_;
const DwarfFde* fde_;
ArchEnum arch_;
AddressType cur_pc_;
const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
@ -128,6 +133,7 @@ class DwarfCfa {
bool cfa_val_offset_sf(dwarf_loc_regs_t*);
bool cfa_val_expression(dwarf_loc_regs_t*);
bool cfa_gnu_negative_offset_extended(dwarf_loc_regs_t*);
bool cfa_aarch64_negate_ra_state(dwarf_loc_regs_t*);
using process_func = bool (DwarfCfa::*)(dwarf_loc_regs_t*);
constexpr static process_func kCallbackTable[64] = {
@ -221,8 +227,9 @@ class DwarfCfa {
nullptr,
// 0x2c illegal cfa
nullptr,
// 0x2d DW_CFA_GNU_window_save (Treat this as illegal)
nullptr,
// 0x2d DW_CFA_AARCH64_negate_ra_state (aarch64 only)
// DW_CFA_GNU_window_save on other architectures.
&DwarfCfa::cfa_aarch64_negate_ra_state,
// 0x2e DW_CFA_GNU_args_size
&DwarfCfa::cfa_nop,
// 0x2f DW_CFA_GNU_negative_offset_extended

View file

@ -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 @@ bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* f
// 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 @@ bool DwarfSectionImpl<AddressType>::EvalRegister(const DwarfLocation* loc, uint3
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 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
// 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 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
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 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
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 @@ bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfF
}
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;

View file

@ -30,7 +30,10 @@
namespace unwindstack {
RegsArm64::RegsArm64()
: RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
: RegsImpl<uint64_t>(ARM64_REG_LAST, Location(LOCATION_REGISTER, ARM64_REG_LR)) {
ResetPseudoRegisters();
pac_mask_ = 0;
}
ArchEnum RegsArm64::Arch() {
return ARCH_ARM64;
@ -45,6 +48,23 @@ uint64_t RegsArm64::sp() {
}
void RegsArm64::set_pc(uint64_t pc) {
// If the target is aarch64 then the return address may have been
// signed using the Armv8.3-A Pointer Authentication extension. The
// original return address can be restored by stripping out the
// authentication code using a mask or xpaclri. xpaclri is a NOP on
// pre-Armv8.3-A architectures.
if ((0 != pc) && IsRASigned()) {
if (pac_mask_) {
pc &= ~pac_mask_;
#if defined(__aarch64__)
} else {
register uint64_t x30 __asm("x30") = pc;
// This is XPACLRI.
asm("hint 0x7" : "+r"(x30));
pc = x30;
#endif
}
}
regs_[ARM64_REG_PC] = pc;
}
@ -144,6 +164,37 @@ bool RegsArm64::StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* proce
return true;
}
void RegsArm64::ResetPseudoRegisters(void) {
// DWARF for AArch64 says RA_SIGN_STATE should be initialized to 0.
this->SetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, 0);
}
bool RegsArm64::SetPseudoRegister(uint16_t id, uint64_t value) {
if ((id >= Arm64Reg::ARM64_PREG_FIRST) && (id < Arm64Reg::ARM64_PREG_LAST)) {
pseudo_regs_[id - Arm64Reg::ARM64_PREG_FIRST] = value;
return true;
}
return false;
}
bool RegsArm64::GetPseudoRegister(uint16_t id, uint64_t* value) {
if ((id >= Arm64Reg::ARM64_PREG_FIRST) && (id < Arm64Reg::ARM64_PREG_LAST)) {
*value = pseudo_regs_[id - Arm64Reg::ARM64_PREG_FIRST];
return true;
}
return false;
}
bool RegsArm64::IsRASigned() {
uint64_t value;
auto result = this->GetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, &value);
return (result && (value != 0));
}
void RegsArm64::SetPACMask(uint64_t mask) {
pac_mask_ = mask;
}
Regs* RegsArm64::Clone() {
return new RegsArm64(*this);
}

View file

@ -33,6 +33,7 @@ enum DwarfLocationEnum : uint8_t {
DWARF_LOCATION_REGISTER,
DWARF_LOCATION_EXPRESSION,
DWARF_LOCATION_VAL_EXPRESSION,
DWARF_LOCATION_PSEUDO_REGISTER,
};
struct DwarfLocation {

View file

@ -31,6 +31,7 @@
namespace unwindstack {
// Forward declarations.
enum ArchEnum : uint8_t;
class Memory;
class Regs;
template <typename AddressType>
@ -90,13 +91,14 @@ class DwarfSection {
virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) = 0;
virtual bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde, ArchEnum arch) = 0;
virtual void GetFdes(std::vector<const DwarfFde*>* fdes) = 0;
virtual const DwarfFde* GetFdeFromPc(uint64_t pc) = 0;
virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs,
ArchEnum arch) = 0;
virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
@ -140,9 +142,10 @@ class DwarfSectionImpl : public DwarfSection {
bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
Regs* regs, bool* finished) override;
bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs,
ArchEnum arch) override;
bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde) override;
bool Log(uint8_t indent, uint64_t pc, const DwarfFde* fde, ArchEnum arch) override;
protected:
bool GetNextCieOrFde(const DwarfFde** fde_entry);

View file

@ -60,6 +60,13 @@ enum Arm64Reg : uint16_t {
ARM64_REG_SP = ARM64_REG_R31,
ARM64_REG_LR = ARM64_REG_R30,
// Pseudo registers. These are not machine registers.
// AARCH64 Return address signed state pseudo-register
ARM64_PREG_RA_SIGN_STATE = 34,
ARM64_PREG_FIRST = ARM64_PREG_RA_SIGN_STATE,
ARM64_PREG_LAST,
};
} // namespace unwindstack

View file

@ -64,6 +64,10 @@ class Regs {
uint64_t dex_pc() { return dex_pc_; }
void set_dex_pc(uint64_t dex_pc) { dex_pc_ = dex_pc; }
virtual void ResetPseudoRegisters() {}
virtual bool SetPseudoRegister(uint16_t, uint64_t) { return false; }
virtual bool GetPseudoRegister(uint16_t, uint64_t*) { return false; }
virtual bool StepIfSignalHandler(uint64_t elf_offset, Elf* elf, Memory* process_memory) = 0;
virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;

View file

@ -22,6 +22,7 @@
#include <functional>
#include <unwindstack/Elf.h>
#include <unwindstack/MachineArm64.h>
#include <unwindstack/Regs.h>
namespace unwindstack {
@ -48,11 +49,25 @@ class RegsArm64 : public RegsImpl<uint64_t> {
void set_pc(uint64_t pc) override;
void set_sp(uint64_t sp) override;
void ResetPseudoRegisters() override;
bool SetPseudoRegister(uint16_t id, uint64_t value) override;
bool GetPseudoRegister(uint16_t id, uint64_t* value) override;
bool IsRASigned();
void SetPACMask(uint64_t mask);
Regs* Clone() override final;
static Regs* Read(void* data);
static Regs* CreateFromUcontext(void* ucontext);
protected:
uint64_t pseudo_regs_[Arm64Reg::ARM64_PREG_LAST - Arm64Reg::ARM64_PREG_FIRST];
uint64_t pac_mask_;
};
} // namespace unwindstack

View file

@ -26,6 +26,7 @@
#include <unwindstack/DwarfLocation.h>
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfStructs.h>
#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
#include "DwarfCfa.h"
@ -57,7 +58,7 @@ class DwarfCfaLogTest : public ::testing::Test {
fde_.pc_end = 0x2000;
fde_.pc_end = 0x10000;
fde_.cie = &cie_;
cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_, ARCH_UNKNOWN));
}
MemoryFake memory_;
@ -72,8 +73,8 @@ TYPED_TEST_SUITE_P(DwarfCfaLogTest);
TYPED_TEST_P(DwarfCfaLogTest, cfa_illegal) {
for (uint8_t i = 0x17; i < 0x3f; i++) {
if (i == 0x2e || i == 0x2f) {
// Skip gnu extension ops.
if (i == 0x2d || i == 0x2e || i == 0x2f) {
// Skip gnu extension ops and aarch64 specialized op.
continue;
}
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
@ -763,6 +764,26 @@ TYPED_TEST_P(DwarfCfaLogTest, cfa_register_override) {
ASSERT_EQ("", GetFakeLogBuf());
}
TYPED_TEST_P(DwarfCfaLogTest, cfa_aarch64_negate_ra_state) {
// Verify that if the cfa op is handled properly depending on aarch.
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2d});
ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
std::string expected = "4 unwind Illegal (Only valid on aarch64)\n";
expected += "4 unwind Raw Data: 0x2d\n";
ASSERT_EQ(expected, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
ResetLogs();
this->cfa_.reset(new DwarfCfa<TypeParam>(this->dmem_.get(), &this->fde_, ARCH_ARM64));
ASSERT_TRUE(this->cfa_->Log(0, this->fde_.pc_start, 0x2000, 0x2001));
expected = "4 unwind DW_CFA_AARCH64_negate_ra_state\n";
expected += "4 unwind Raw Data: 0x2d\n";
ASSERT_EQ(expected, GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
}
REGISTER_TYPED_TEST_SUITE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
cfa_advance_loc, cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4,
@ -771,7 +792,8 @@ REGISTER_TYPED_TEST_SUITE_P(DwarfCfaLogTest, cfa_illegal, cfa_nop, cfa_offset, c
cfa_def_cfa_register, cfa_def_cfa_offset, cfa_def_cfa_offset_sf,
cfa_def_cfa_expression, cfa_expression, cfa_val_offset,
cfa_val_offset_sf, cfa_val_expression, cfa_gnu_args_size,
cfa_gnu_negative_offset_extended, cfa_register_override);
cfa_gnu_negative_offset_extended, cfa_register_override,
cfa_aarch64_negate_ra_state);
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaLogTestTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfCfaLogTest, DwarfCfaLogTestTypes);

View file

@ -25,7 +25,9 @@
#include <unwindstack/DwarfLocation.h>
#include <unwindstack/DwarfMemory.h>
#include <unwindstack/DwarfStructs.h>
#include <unwindstack/Elf.h>
#include <unwindstack/Log.h>
#include <unwindstack/MachineArm64.h>
#include "DwarfCfa.h"
@ -55,7 +57,7 @@ class DwarfCfaTest : public ::testing::Test {
fde_.pc_start = 0x2000;
fde_.cie = &cie_;
cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_));
cfa_.reset(new DwarfCfa<TypeParam>(dmem_.get(), &fde_, ARCH_UNKNOWN));
}
MemoryFake memory_;
@ -70,8 +72,8 @@ TYPED_TEST_SUITE_P(DwarfCfaTest);
TYPED_TEST_P(DwarfCfaTest, cfa_illegal) {
for (uint8_t i = 0x17; i < 0x3f; i++) {
if (i == 0x2e || i == 0x2f) {
// Skip gnu extension ops.
if (i == 0x2d || i == 0x2e || i == 0x2f) {
// Skip gnu extension ops and aarch64 specialized op.
continue;
}
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{i});
@ -952,6 +954,57 @@ TYPED_TEST_P(DwarfCfaTest, cfa_register_override) {
ASSERT_EQ("", GetFakeLogBuf());
}
TYPED_TEST_P(DwarfCfaTest, cfa_aarch64_negate_ra_state) {
this->memory_.SetMemory(0x2000, std::vector<uint8_t>{0x2d});
dwarf_loc_regs_t loc_regs;
ASSERT_FALSE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->cfa_->LastErrorCode());
ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
ASSERT_EQ("", GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
ResetLogs();
this->cfa_.reset(new DwarfCfa<TypeParam>(this->dmem_.get(), &this->fde_, ARCH_ARM64));
ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
auto location = loc_regs.find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
ASSERT_NE(loc_regs.end(), location);
ASSERT_EQ(DWARF_LOCATION_PSEUDO_REGISTER, location->second.type);
ASSERT_EQ(1U, location->second.values[0]);
ASSERT_EQ("", GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
// Verify that the value is set to 0 after another evaluation.
ResetLogs();
ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
location = loc_regs.find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
ASSERT_NE(loc_regs.end(), location);
ASSERT_EQ(DWARF_LOCATION_PSEUDO_REGISTER, location->second.type);
ASSERT_EQ(0U, location->second.values[0]);
ASSERT_EQ("", GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
// Verify that the value is set to 1 again after a third op.
ResetLogs();
ASSERT_TRUE(this->cfa_->GetLocationInfo(this->fde_.pc_start, 0x2000, 0x2001, &loc_regs));
ASSERT_EQ(0x2001U, this->dmem_->cur_offset());
location = loc_regs.find(Arm64Reg::ARM64_PREG_RA_SIGN_STATE);
ASSERT_NE(loc_regs.end(), location);
ASSERT_EQ(DWARF_LOCATION_PSEUDO_REGISTER, location->second.type);
ASSERT_EQ(1U, location->second.values[0]);
ASSERT_EQ("", GetFakeLogPrint());
ASSERT_EQ("", GetFakeLogBuf());
}
REGISTER_TYPED_TEST_SUITE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_offset_extended,
cfa_offset_extended_sf, cfa_restore, cfa_restore_extended, cfa_set_loc,
cfa_advance_loc1, cfa_advance_loc2, cfa_advance_loc4, cfa_undefined,
@ -960,7 +1013,7 @@ REGISTER_TYPED_TEST_SUITE_P(DwarfCfaTest, cfa_illegal, cfa_nop, cfa_offset, cfa_
cfa_def_cfa_offset_sf, cfa_def_cfa_expression, cfa_expression,
cfa_val_offset, cfa_val_offset_sf, cfa_val_expression,
cfa_gnu_args_size, cfa_gnu_negative_offset_extended,
cfa_register_override);
cfa_register_override, cfa_aarch64_negate_ra_state);
typedef ::testing::Types<uint32_t, uint64_t> DwarfCfaTestTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(Libunwindstack, DwarfCfaTest, DwarfCfaTestTypes);

View file

@ -20,6 +20,7 @@
#include <unwindstack/DwarfError.h>
#include <unwindstack/DwarfSection.h>
#include <unwindstack/Elf.h>
#include "DwarfEncoding.h"
@ -505,7 +506,7 @@ TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
dwarf_loc_regs_t loc_regs;
ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs, ARCH_UNKNOWN));
ASSERT_EQ(2U, loc_regs.size());
auto entry = loc_regs.find(2);
@ -535,7 +536,7 @@ TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_cached) {
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0x09, 0x04, 0x03});
dwarf_loc_regs_t loc_regs;
ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs));
ASSERT_TRUE(this->section_->GetCfaLocationInfo(0x100, &fde, &loc_regs, ARCH_UNKNOWN));
ASSERT_EQ(2U, loc_regs.size());
auto entry = loc_regs.find(6);
@ -560,7 +561,7 @@ TYPED_TEST_P(DwarfSectionImplTest, Log) {
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x00});
this->memory_.SetMemory(0x6000, std::vector<uint8_t>{0xc2});
ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde));
ASSERT_TRUE(this->section_->Log(2, 0x1000, &fde, ARCH_UNKNOWN));
ASSERT_EQ(
"4 unwind DW_CFA_nop\n"

View file

@ -20,8 +20,10 @@
#include <gtest/gtest.h>
#include <unwindstack/DwarfSection.h>
#include <unwindstack/Elf.h>
#include "MemoryFake.h"
#include "RegsFake.h"
namespace unwindstack {
@ -35,13 +37,14 @@ class MockDwarfSection : public DwarfSection {
MOCK_METHOD(bool, Eval, (const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*),
(override));
MOCK_METHOD(bool, Log, (uint8_t, uint64_t, const DwarfFde*), (override));
MOCK_METHOD(bool, Log, (uint8_t, uint64_t, const DwarfFde*, ArchEnum arch), (override));
MOCK_METHOD(void, GetFdes, (std::vector<const DwarfFde*>*), (override));
MOCK_METHOD(const DwarfFde*, GetFdeFromPc, (uint64_t), (override));
MOCK_METHOD(bool, GetCfaLocationInfo, (uint64_t, const DwarfFde*, dwarf_loc_regs_t*), (override));
MOCK_METHOD(bool, GetCfaLocationInfo,
(uint64_t, const DwarfFde*, dwarf_loc_regs_t*, ArchEnum arch), (override));
MOCK_METHOD(uint64_t, GetCieOffsetFromFde32, (uint32_t), (override));
@ -56,8 +59,11 @@ class DwarfSectionTest : public ::testing::Test {
MemoryFake memory_;
std::unique_ptr<MockDwarfSection> section_;
static RegsFake regs_;
};
RegsFake DwarfSectionTest::regs_(10);
TEST_F(DwarfSectionTest, Step_fail_fde) {
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(nullptr));
@ -73,7 +79,7 @@ TEST_F(DwarfSectionTest, Step_fail_cie_null) {
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
bool finished;
ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
ASSERT_FALSE(section_->Step(0x1000, &regs_, nullptr, &finished));
}
TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
@ -83,11 +89,11 @@ TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
fde.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_, ::testing::_))
.WillOnce(::testing::Return(false));
bool finished;
ASSERT_FALSE(section_->Step(0x1000, nullptr, nullptr, &finished));
ASSERT_FALSE(section_->Step(0x1000, &regs_, nullptr, &finished));
}
TEST_F(DwarfSectionTest, Step_pass) {
@ -97,19 +103,19 @@ TEST_F(DwarfSectionTest, Step_pass) {
fde.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_, ::testing::_))
.WillOnce(::testing::Return(true));
MemoryFake process;
EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, &regs_, ::testing::_))
.WillOnce(::testing::Return(true));
bool finished;
ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished));
}
static bool MockGetCfaLocationInfo(::testing::Unused, const DwarfFde* fde,
dwarf_loc_regs_t* loc_regs) {
dwarf_loc_regs_t* loc_regs, ArchEnum) {
loc_regs->pc_start = fde->pc_start;
loc_regs->pc_end = fde->pc_end;
return true;
@ -123,17 +129,17 @@ TEST_F(DwarfSectionTest, Step_cache) {
fde.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde));
EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
MemoryFake process;
EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, &regs_, ::testing::_))
.WillRepeatedly(::testing::Return(true));
bool finished;
ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
ASSERT_TRUE(section_->Step(0x1500, nullptr, &process, &finished));
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished));
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished));
ASSERT_TRUE(section_->Step(0x1500, &regs_, &process, &finished));
}
TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
@ -143,26 +149,26 @@ TEST_F(DwarfSectionTest, Step_cache_not_in_pc) {
fde0.pc_end = 0x2000;
fde0.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x1000)).WillOnce(::testing::Return(&fde0));
EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_))
EXPECT_CALL(*section_, GetCfaLocationInfo(0x1000, &fde0, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
MemoryFake process;
EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
EXPECT_CALL(*section_, Eval(&cie, &process, ::testing::_, &regs_, ::testing::_))
.WillRepeatedly(::testing::Return(true));
bool finished;
ASSERT_TRUE(section_->Step(0x1000, nullptr, &process, &finished));
ASSERT_TRUE(section_->Step(0x1000, &regs_, &process, &finished));
DwarfFde fde1{};
fde1.pc_start = 0x500;
fde1.pc_end = 0x800;
fde1.cie = &cie;
EXPECT_CALL(*section_, GetFdeFromPc(0x600)).WillOnce(::testing::Return(&fde1));
EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_))
EXPECT_CALL(*section_, GetCfaLocationInfo(0x600, &fde1, ::testing::_, ::testing::_))
.WillOnce(::testing::Invoke(MockGetCfaLocationInfo));
ASSERT_TRUE(section_->Step(0x600, nullptr, &process, &finished));
ASSERT_TRUE(section_->Step(0x700, nullptr, &process, &finished));
ASSERT_TRUE(section_->Step(0x600, &regs_, &process, &finished));
ASSERT_TRUE(section_->Step(0x700, &regs_, &process, &finished));
}
} // namespace unwindstack

View file

@ -247,6 +247,14 @@ TEST_F(RegsTest, mips64_verify_sp_pc) {
EXPECT_EQ(0xc200000000U, mips64.pc());
}
TEST_F(RegsTest, arm64_strip_pac_mask) {
RegsArm64 arm64;
arm64.SetPseudoRegister(Arm64Reg::ARM64_PREG_RA_SIGN_STATE, 1);
arm64.SetPACMask(0x007fff8000000000ULL);
arm64.set_pc(0x0020007214bb3a04ULL);
EXPECT_EQ(0x0000007214bb3a04ULL, arm64.pc());
}
TEST_F(RegsTest, machine_type) {
RegsArm arm_regs;
EXPECT_EQ(ARCH_ARM, arm_regs.Arch());

View file

@ -55,7 +55,7 @@ static DwarfLocationEnum GetReturnAddressLocation(uint64_t rel_pc, DwarfSection*
return DWARF_LOCATION_INVALID;
}
dwarf_loc_regs_t regs;
if (!section->GetCfaLocationInfo(rel_pc, fde, &regs)) {
if (!section->GetCfaLocationInfo(rel_pc, fde, &regs, ARCH_UNKNOWN)) {
return DWARF_LOCATION_INVALID;
}

View file

@ -96,7 +96,7 @@ void DumpDwarfSection(Elf* elf, DwarfSection* section, uint64_t) {
printf(" <%s>", name.c_str());
}
printf("\n");
if (!section->Log(2, UINT64_MAX, fde)) {
if (!section->Log(2, UINT64_MAX, fde, elf->arch())) {
printf("Failed to process cfa information for entry at 0x%" PRIx64 "\n", fde->pc_start);
}
}

View file

@ -64,7 +64,8 @@ void PrintExpression(Memory* memory, uint8_t class_type, uint64_t end, uint64_t
}
}
void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uint8_t class_type) {
void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uint8_t class_type,
ArchEnum arch) {
const DwarfFde* fde = section->GetFdeFromPc(pc);
if (fde == nullptr) {
printf(" No fde found.\n");
@ -72,7 +73,7 @@ void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uin
}
dwarf_loc_regs_t regs;
if (!section->GetCfaLocationInfo(pc, fde, &regs)) {
if (!section->GetCfaLocationInfo(pc, fde, &regs, arch)) {
printf(" Cannot get location information.\n");
return;
}
@ -128,6 +129,11 @@ void PrintRegInformation(DwarfSection* section, Memory* memory, uint64_t pc, uin
break;
}
case DWARF_LOCATION_PSEUDO_REGISTER: {
printf("%" PRId64 " (pseudo)\n", loc->values[0]);
break;
}
case DWARF_LOCATION_UNDEFINED:
printf("undefine\n");
break;
@ -199,7 +205,7 @@ int GetInfo(const char* file, uint64_t offset, uint64_t pc) {
DwarfSection* section = interface->eh_frame();
if (section != nullptr) {
printf("\neh_frame:\n");
PrintRegInformation(section, elf.memory(), pc, elf.class_type());
PrintRegInformation(section, elf.memory(), pc, elf.class_type(), elf.arch());
} else {
printf("\nno eh_frame information\n");
}
@ -207,7 +213,7 @@ int GetInfo(const char* file, uint64_t offset, uint64_t pc) {
section = interface->debug_frame();
if (section != nullptr) {
printf("\ndebug_frame:\n");
PrintRegInformation(section, elf.memory(), pc, elf.class_type());
PrintRegInformation(section, elf.memory(), pc, elf.class_type(), elf.arch());
printf("\n");
} else {
printf("\nno debug_frame information\n");
@ -219,7 +225,8 @@ int GetInfo(const char* file, uint64_t offset, uint64_t pc) {
section = gnu_debugdata_interface->eh_frame();
if (section != nullptr) {
printf("\ngnu_debugdata (eh_frame):\n");
PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type(),
elf.arch());
printf("\n");
} else {
printf("\nno gnu_debugdata (eh_frame)\n");
@ -228,7 +235,8 @@ int GetInfo(const char* file, uint64_t offset, uint64_t pc) {
section = gnu_debugdata_interface->debug_frame();
if (section != nullptr) {
printf("\ngnu_debugdata (debug_frame):\n");
PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type());
PrintRegInformation(section, gnu_debugdata_interface->memory(), pc, elf.class_type(),
elf.arch());
printf("\n");
} else {
printf("\nno gnu_debugdata (debug_frame)\n");