Merge "Add DwarfSection classes."
am: 6a80c3e1a8
Change-Id: I353f0d1fb1c3203134e8590a1ef7267407240bed
This commit is contained in:
commit
5659a2b6fe
12 changed files with 1727 additions and 22 deletions
|
@ -50,6 +50,7 @@ cc_defaults {
|
|||
"DwarfCfa.cpp",
|
||||
"DwarfMemory.cpp",
|
||||
"DwarfOp.cpp",
|
||||
"DwarfSection.cpp",
|
||||
"Elf.cpp",
|
||||
"ElfInterface.cpp",
|
||||
"ElfInterfaceArm.cpp",
|
||||
|
@ -98,6 +99,8 @@ cc_defaults {
|
|||
"tests/DwarfMemoryTest.cpp",
|
||||
"tests/DwarfOpLogTest.cpp",
|
||||
"tests/DwarfOpTest.cpp",
|
||||
"tests/DwarfSectionTest.cpp",
|
||||
"tests/DwarfSectionImplTest.cpp",
|
||||
"tests/ElfInterfaceArmTest.cpp",
|
||||
"tests/ElfInterfaceTest.cpp",
|
||||
"tests/ElfTest.cpp",
|
||||
|
@ -123,6 +126,10 @@ cc_defaults {
|
|||
"liblog",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libgmock",
|
||||
],
|
||||
|
||||
target: {
|
||||
linux: {
|
||||
host_ldlibs: [
|
||||
|
|
|
@ -63,7 +63,7 @@ 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) : memory_(memory), fde_(fde) {}
|
||||
virtual ~DwarfCfa() = default;
|
||||
|
||||
bool GetLocationInfo(uint64_t pc, uint64_t start_offset, uint64_t end_offset,
|
||||
|
@ -88,7 +88,7 @@ class DwarfCfa {
|
|||
private:
|
||||
DwarfError last_error_;
|
||||
DwarfMemory* memory_;
|
||||
const DwarfFDE* fde_;
|
||||
const DwarfFde* fde_;
|
||||
|
||||
AddressType cur_pc_;
|
||||
const dwarf_loc_regs_t* cie_loc_regs_ = nullptr;
|
||||
|
|
|
@ -27,6 +27,8 @@ enum DwarfError : uint8_t {
|
|||
DWARF_ERROR_STACK_INDEX_NOT_VALID,
|
||||
DWARF_ERROR_NOT_IMPLEMENTED,
|
||||
DWARF_ERROR_TOO_MANY_ITERATIONS,
|
||||
DWARF_ERROR_CFA_NOT_DEFINED,
|
||||
DWARF_ERROR_UNSUPPORTED_VERSION,
|
||||
};
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DWARF_ERROR_H
|
||||
|
|
543
libunwindstack/DwarfSection.cpp
Normal file
543
libunwindstack/DwarfSection.cpp
Normal file
|
@ -0,0 +1,543 @@
|
|||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "DwarfCfa.h"
|
||||
#include "DwarfError.h"
|
||||
#include "DwarfLocation.h"
|
||||
#include "DwarfMemory.h"
|
||||
#include "DwarfOp.h"
|
||||
#include "DwarfSection.h"
|
||||
#include "DwarfStructs.h"
|
||||
#include "Log.h"
|
||||
#include "Memory.h"
|
||||
#include "Regs.h"
|
||||
|
||||
const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
|
||||
uint64_t fde_offset;
|
||||
if (!GetFdeOffsetFromPc(pc, &fde_offset)) {
|
||||
return nullptr;
|
||||
}
|
||||
const DwarfFde* fde = GetFdeFromOffset(fde_offset);
|
||||
// Guaranteed pc >= pc_start, need to check pc in the fde range.
|
||||
if (pc < fde->pc_end) {
|
||||
return fde;
|
||||
}
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
|
||||
const DwarfFde* fde = GetFdeFromPc(pc);
|
||||
if (fde == nullptr || fde->cie == nullptr) {
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now get the location information for this pc.
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
if (!GetCfaLocationInfo(pc, fde, &loc_regs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now eval the actual registers.
|
||||
return Eval(fde->cie, process_memory, loc_regs, regs);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uint8_t version,
|
||||
Memory* regular_memory, AddressType* value) {
|
||||
DwarfOp<AddressType> op(&memory_, regular_memory);
|
||||
|
||||
// Need to evaluate the op data.
|
||||
uint64_t start = loc.values[1];
|
||||
uint64_t end = start + loc.values[0];
|
||||
if (!op.Eval(start, end, version)) {
|
||||
last_error_ = op.last_error();
|
||||
return false;
|
||||
}
|
||||
if (op.StackSize() == 0) {
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return false;
|
||||
}
|
||||
// We don't support an expression that evaluates to a register number.
|
||||
if (op.is_register()) {
|
||||
last_error_ = DWARF_ERROR_NOT_IMPLEMENTED;
|
||||
return false;
|
||||
}
|
||||
*value = op.StackAt(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
|
||||
const dwarf_loc_regs_t& loc_regs, Regs* regs) {
|
||||
RegsTmpl<AddressType>* cur_regs = reinterpret_cast<RegsTmpl<AddressType>*>(regs);
|
||||
if (cie->return_address_register >= cur_regs->total_regs()) {
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the cfa value;
|
||||
auto cfa_entry = loc_regs.find(CFA_REG);
|
||||
if (cfa_entry == loc_regs.end()) {
|
||||
last_error_ = DWARF_ERROR_CFA_NOT_DEFINED;
|
||||
return false;
|
||||
}
|
||||
|
||||
AddressType prev_pc = regs->pc();
|
||||
AddressType prev_cfa = regs->sp();
|
||||
|
||||
AddressType cfa;
|
||||
const DwarfLocation* loc = &cfa_entry->second;
|
||||
// Only a few location types are valid for the cfa.
|
||||
switch (loc->type) {
|
||||
case DWARF_LOCATION_REGISTER:
|
||||
if (loc->values[0] >= cur_regs->total_regs()) {
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
// If the stack pointer register is the CFA, and the stack
|
||||
// pointer register does not have any associated location
|
||||
// information, use the current cfa value.
|
||||
if (regs->sp_reg() == loc->values[0] && loc_regs.count(regs->sp_reg()) == 0) {
|
||||
cfa = prev_cfa;
|
||||
} else {
|
||||
cfa = (*cur_regs)[loc->values[0]];
|
||||
}
|
||||
cfa += loc->values[1];
|
||||
break;
|
||||
case DWARF_LOCATION_EXPRESSION:
|
||||
case DWARF_LOCATION_VAL_EXPRESSION: {
|
||||
AddressType value;
|
||||
if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
|
||||
return false;
|
||||
}
|
||||
if (loc->type == DWARF_LOCATION_EXPRESSION) {
|
||||
if (!regular_memory->Read(value, &cfa, sizeof(AddressType))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
cfa = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// This code is not guaranteed to work in cases where a register location
|
||||
// is a double indirection to the actual value. For example, if r3 is set
|
||||
// to r5 + 4, and r5 is set to CFA + 4, then this won't necessarily work
|
||||
// because it does not guarantee that r5 is evaluated before r3.
|
||||
// Check that this case does not exist, and error if it does.
|
||||
bool return_address_undefined = false;
|
||||
for (const auto& entry : loc_regs) {
|
||||
uint16_t reg = entry.first;
|
||||
// Already handled the CFA register.
|
||||
if (reg == CFA_REG) continue;
|
||||
|
||||
if (reg >= cur_regs->total_regs()) {
|
||||
// Skip this unknown register.
|
||||
continue;
|
||||
}
|
||||
|
||||
const DwarfLocation* loc = &entry.second;
|
||||
switch (loc->type) {
|
||||
case DWARF_LOCATION_OFFSET:
|
||||
if (!regular_memory->Read(cfa + loc->values[0], &(*cur_regs)[reg], sizeof(AddressType))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case DWARF_LOCATION_VAL_OFFSET:
|
||||
(*cur_regs)[reg] = cfa + loc->values[0];
|
||||
break;
|
||||
case DWARF_LOCATION_REGISTER: {
|
||||
uint16_t cur_reg = loc->values[0];
|
||||
if (cur_reg >= cur_regs->total_regs()) {
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
if (loc_regs.find(cur_reg) != loc_regs.end()) {
|
||||
// This is a double indirection, a register definition references
|
||||
// another register which is also defined as something other
|
||||
// than a register.
|
||||
log(0,
|
||||
"Invalid indirection: register %d references register %d which is "
|
||||
"not a plain register.\n",
|
||||
reg, cur_reg);
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_STATE;
|
||||
return false;
|
||||
}
|
||||
(*cur_regs)[reg] = (*cur_regs)[cur_reg] + loc->values[1];
|
||||
break;
|
||||
}
|
||||
case DWARF_LOCATION_EXPRESSION:
|
||||
case DWARF_LOCATION_VAL_EXPRESSION: {
|
||||
AddressType value;
|
||||
if (!EvalExpression(*loc, cie->version, regular_memory, &value)) {
|
||||
return false;
|
||||
}
|
||||
if (loc->type == DWARF_LOCATION_EXPRESSION) {
|
||||
if (!regular_memory->Read(value, &(*cur_regs)[reg], sizeof(AddressType))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
(*cur_regs)[reg] = value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DWARF_LOCATION_UNDEFINED:
|
||||
if (reg == cie->return_address_register) {
|
||||
return_address_undefined = true;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the return address location.
|
||||
if (return_address_undefined) {
|
||||
cur_regs->set_pc(0);
|
||||
} else {
|
||||
cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
|
||||
}
|
||||
cur_regs->set_sp(cfa);
|
||||
// Stop if the cfa and pc are the same.
|
||||
return prev_cfa != cfa || prev_pc != cur_regs->pc();
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
const DwarfCie* DwarfSectionImpl<AddressType>::GetCie(uint64_t offset) {
|
||||
auto cie_entry = cie_entries_.find(offset);
|
||||
if (cie_entry != cie_entries_.end()) {
|
||||
return &cie_entry->second;
|
||||
}
|
||||
DwarfCie* cie = &cie_entries_[offset];
|
||||
memory_.set_cur_offset(offset);
|
||||
if (!FillInCie(cie)) {
|
||||
// Erase the cached entry.
|
||||
cie_entries_.erase(offset);
|
||||
return nullptr;
|
||||
}
|
||||
return cie;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::FillInCie(DwarfCie* cie) {
|
||||
uint32_t length32;
|
||||
if (!memory_.ReadBytes(&length32, sizeof(length32))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
if (length32 == static_cast<uint32_t>(-1)) {
|
||||
// 64 bit Cie
|
||||
uint64_t length64;
|
||||
if (!memory_.ReadBytes(&length64, sizeof(length64))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
cie->cfa_instructions_end = memory_.cur_offset() + length64;
|
||||
cie->fde_address_encoding = DW_EH_PE_sdata8;
|
||||
|
||||
uint64_t cie_id;
|
||||
if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
if (!IsCie64(cie_id)) {
|
||||
// This is not a Cie, something has gone horribly wrong.
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// 32 bit Cie
|
||||
cie->cfa_instructions_end = memory_.cur_offset() + length32;
|
||||
cie->fde_address_encoding = DW_EH_PE_sdata4;
|
||||
|
||||
uint32_t cie_id;
|
||||
if (!memory_.ReadBytes(&cie_id, sizeof(cie_id))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
if (!IsCie32(cie_id)) {
|
||||
// This is not a Cie, something has gone horribly wrong.
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!memory_.ReadBytes(&cie->version, sizeof(cie->version))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cie->version != 1 && cie->version != 3 && cie->version != 4) {
|
||||
// Unrecognized version.
|
||||
last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the augmentation string.
|
||||
char aug_value;
|
||||
do {
|
||||
if (!memory_.ReadBytes(&aug_value, 1)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
cie->augmentation_string.push_back(aug_value);
|
||||
} while (aug_value != '\0');
|
||||
|
||||
if (cie->version == 4) {
|
||||
// Skip the Address Size field since we only use it for validation.
|
||||
memory_.set_cur_offset(memory_.cur_offset() + 1);
|
||||
|
||||
// Segment Size
|
||||
if (!memory_.ReadBytes(&cie->segment_size, 1)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Code Alignment Factor
|
||||
if (!memory_.ReadULEB128(&cie->code_alignment_factor)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Data Alignment Factor
|
||||
if (!memory_.ReadSLEB128(&cie->data_alignment_factor)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cie->version == 1) {
|
||||
// Return Address is a single byte.
|
||||
uint8_t return_address_register;
|
||||
if (!memory_.ReadBytes(&return_address_register, 1)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
cie->return_address_register = return_address_register;
|
||||
} else if (!memory_.ReadULEB128(&cie->return_address_register)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (cie->augmentation_string[0] != 'z') {
|
||||
cie->cfa_instructions_offset = memory_.cur_offset();
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t aug_length;
|
||||
if (!memory_.ReadULEB128(&aug_length)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
cie->cfa_instructions_offset = memory_.cur_offset() + aug_length;
|
||||
|
||||
for (size_t i = 1; i < cie->augmentation_string.size(); i++) {
|
||||
switch (cie->augmentation_string[i]) {
|
||||
case 'L':
|
||||
if (!memory_.ReadBytes(&cie->lsda_encoding, 1)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 'P': {
|
||||
uint8_t encoding;
|
||||
if (!memory_.ReadBytes(&encoding, 1)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
if (!memory_.ReadEncodedValue<AddressType>(encoding, &cie->personality_handler)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
} break;
|
||||
case 'R':
|
||||
if (!memory_.ReadBytes(&cie->fde_address_encoding, 1)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
const DwarfFde* DwarfSectionImpl<AddressType>::GetFdeFromOffset(uint64_t offset) {
|
||||
auto fde_entry = fde_entries_.find(offset);
|
||||
if (fde_entry != fde_entries_.end()) {
|
||||
return &fde_entry->second;
|
||||
}
|
||||
DwarfFde* fde = &fde_entries_[offset];
|
||||
memory_.set_cur_offset(offset);
|
||||
if (!FillInFde(fde)) {
|
||||
fde_entries_.erase(offset);
|
||||
return nullptr;
|
||||
}
|
||||
return fde;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::FillInFde(DwarfFde* fde) {
|
||||
uint32_t length32;
|
||||
if (!memory_.ReadBytes(&length32, sizeof(length32))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (length32 == static_cast<uint32_t>(-1)) {
|
||||
// 64 bit Fde.
|
||||
uint64_t length64;
|
||||
if (!memory_.ReadBytes(&length64, sizeof(length64))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
fde->cfa_instructions_end = memory_.cur_offset() + length64;
|
||||
|
||||
uint64_t value64;
|
||||
if (!memory_.ReadBytes(&value64, sizeof(value64))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
if (IsCie64(value64)) {
|
||||
// This is a Cie, this means something has gone wrong.
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the Cie pointer, which is necessary to properly read the rest of
|
||||
// of the Fde information.
|
||||
fde->cie_offset = GetCieOffsetFromFde64(value64);
|
||||
} else {
|
||||
// 32 bit Fde.
|
||||
fde->cfa_instructions_end = memory_.cur_offset() + length32;
|
||||
|
||||
uint32_t value32;
|
||||
if (!memory_.ReadBytes(&value32, sizeof(value32))) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
if (IsCie32(value32)) {
|
||||
// This is a Cie, this means something has gone wrong.
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the Cie pointer, which is necessary to properly read the rest of
|
||||
// of the Fde information.
|
||||
fde->cie_offset = GetCieOffsetFromFde32(value32);
|
||||
}
|
||||
uint64_t cur_offset = memory_.cur_offset();
|
||||
|
||||
const DwarfCie* cie = GetCie(fde->cie_offset);
|
||||
if (cie == nullptr) {
|
||||
return false;
|
||||
}
|
||||
fde->cie = cie;
|
||||
|
||||
if (cie->segment_size != 0) {
|
||||
// Skip over the segment selector for now.
|
||||
cur_offset += cie->segment_size;
|
||||
}
|
||||
memory_.set_cur_offset(cur_offset);
|
||||
|
||||
if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_start)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
fde->pc_start = AdjustPcFromFde(fde->pc_start);
|
||||
|
||||
if (!memory_.ReadEncodedValue<AddressType>(cie->fde_address_encoding & 0xf, &fde->pc_end)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
fde->pc_end += fde->pc_start;
|
||||
if (cie->augmentation_string.size() > 0 && cie->augmentation_string[0] == 'z') {
|
||||
// Augmentation Size
|
||||
uint64_t aug_length;
|
||||
if (!memory_.ReadULEB128(&aug_length)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
uint64_t cur_offset = memory_.cur_offset();
|
||||
|
||||
if (!memory_.ReadEncodedValue<AddressType>(cie->lsda_encoding, &fde->lsda_address)) {
|
||||
last_error_ = DWARF_ERROR_MEMORY_INVALID;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set our position to after all of the augmentation data.
|
||||
memory_.set_cur_offset(cur_offset + aug_length);
|
||||
}
|
||||
fde->cfa_instructions_offset = memory_.cur_offset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde,
|
||||
dwarf_loc_regs_t* loc_regs) {
|
||||
DwarfCfa<AddressType> cfa(&memory_, fde);
|
||||
|
||||
// Look for the cached copy of the cie data.
|
||||
auto reg_entry = cie_loc_regs_.find(fde->cie_offset);
|
||||
if (reg_entry == cie_loc_regs_.end()) {
|
||||
if (!cfa.GetLocationInfo(pc, fde->cie->cfa_instructions_offset, fde->cie->cfa_instructions_end,
|
||||
loc_regs)) {
|
||||
last_error_ = cfa.last_error();
|
||||
return false;
|
||||
}
|
||||
cie_loc_regs_[fde->cie_offset] = *loc_regs;
|
||||
}
|
||||
cfa.set_cie_loc_regs(&cie_loc_regs_[fde->cie_offset]);
|
||||
if (!cfa.GetLocationInfo(pc, fde->cfa_instructions_offset, fde->cfa_instructions_end, loc_regs)) {
|
||||
last_error_ = cfa.last_error();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::Log(uint8_t indent, uint64_t pc, uint64_t load_bias,
|
||||
const DwarfFde* fde) {
|
||||
DwarfCfa<AddressType> cfa(&memory_, fde);
|
||||
|
||||
// Always print the cie information.
|
||||
const DwarfCie* cie = fde->cie;
|
||||
if (!cfa.Log(indent, pc, load_bias, cie->cfa_instructions_offset, cie->cfa_instructions_end)) {
|
||||
last_error_ = cfa.last_error();
|
||||
return false;
|
||||
}
|
||||
if (!cfa.Log(indent, pc, load_bias, fde->cfa_instructions_offset, fde->cfa_instructions_end)) {
|
||||
last_error_ = cfa.last_error();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Explicitly instantiate DwarfSectionImpl
|
||||
template class DwarfSectionImpl<uint32_t>;
|
||||
template class DwarfSectionImpl<uint64_t>;
|
137
libunwindstack/DwarfSection.h
Normal file
137
libunwindstack/DwarfSection.h
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_DWARF_SECTION_H
|
||||
#define _LIBUNWINDSTACK_DWARF_SECTION_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <iterator>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "DwarfError.h"
|
||||
#include "DwarfLocation.h"
|
||||
#include "DwarfMemory.h"
|
||||
#include "DwarfStructs.h"
|
||||
|
||||
// Forward declarations.
|
||||
class Memory;
|
||||
class Regs;
|
||||
|
||||
class DwarfSection {
|
||||
public:
|
||||
DwarfSection(Memory* memory) : memory_(memory) {}
|
||||
virtual ~DwarfSection() = default;
|
||||
|
||||
class iterator : public std::iterator<std::bidirectional_iterator_tag, DwarfFde*> {
|
||||
public:
|
||||
iterator(DwarfSection* section, size_t index) : section_(section), index_(index) {}
|
||||
|
||||
iterator& operator++() {
|
||||
index_++;
|
||||
return *this;
|
||||
}
|
||||
iterator& operator++(int increment) {
|
||||
index_ += increment;
|
||||
return *this;
|
||||
}
|
||||
iterator& operator--() {
|
||||
index_--;
|
||||
return *this;
|
||||
}
|
||||
iterator& operator--(int decrement) {
|
||||
index_ -= decrement;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const iterator& rhs) { return this->index_ == rhs.index_; }
|
||||
bool operator!=(const iterator& rhs) { return this->index_ != rhs.index_; }
|
||||
|
||||
const DwarfFde* operator*() { return section_->GetFdeFromIndex(index_); }
|
||||
|
||||
private:
|
||||
DwarfSection* section_ = nullptr;
|
||||
size_t index_ = 0;
|
||||
};
|
||||
|
||||
iterator begin() { return iterator(this, 0); }
|
||||
iterator end() { return iterator(this, fde_count_); }
|
||||
|
||||
DwarfError last_error() { return last_error_; }
|
||||
|
||||
virtual bool Init(uint64_t offset, uint64_t size) = 0;
|
||||
|
||||
virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*) = 0;
|
||||
|
||||
virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
|
||||
|
||||
virtual bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) = 0;
|
||||
|
||||
virtual const DwarfFde* GetFdeFromIndex(size_t index) = 0;
|
||||
|
||||
const DwarfFde* GetFdeFromPc(uint64_t pc);
|
||||
|
||||
virtual const DwarfFde* GetFdeFromOffset(uint64_t fde_offset) = 0;
|
||||
|
||||
virtual bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) = 0;
|
||||
|
||||
virtual bool IsCie32(uint32_t value32) = 0;
|
||||
|
||||
virtual bool IsCie64(uint64_t value64) = 0;
|
||||
|
||||
virtual uint64_t GetCieOffsetFromFde32(uint32_t pointer) = 0;
|
||||
|
||||
virtual uint64_t GetCieOffsetFromFde64(uint64_t pointer) = 0;
|
||||
|
||||
virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
|
||||
|
||||
bool Step(uint64_t pc, Regs* regs, Memory* process_memory);
|
||||
|
||||
protected:
|
||||
DwarfMemory memory_;
|
||||
DwarfError last_error_ = DWARF_ERROR_NONE;
|
||||
|
||||
uint64_t fde_count_;
|
||||
std::unordered_map<uint64_t, DwarfFde> fde_entries_;
|
||||
std::unordered_map<uint64_t, DwarfCie> cie_entries_;
|
||||
std::unordered_map<uint64_t, dwarf_loc_regs_t> cie_loc_regs_;
|
||||
};
|
||||
|
||||
template <typename AddressType>
|
||||
class DwarfSectionImpl : public DwarfSection {
|
||||
public:
|
||||
DwarfSectionImpl(Memory* memory) : DwarfSection(memory) {}
|
||||
virtual ~DwarfSectionImpl() = default;
|
||||
|
||||
bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
|
||||
Regs* regs) override;
|
||||
|
||||
const DwarfCie* GetCie(uint64_t offset);
|
||||
bool FillInCie(DwarfCie* cie);
|
||||
|
||||
const DwarfFde* GetFdeFromOffset(uint64_t offset) override;
|
||||
bool FillInFde(DwarfFde* fde);
|
||||
|
||||
bool GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, dwarf_loc_regs_t* loc_regs) override;
|
||||
|
||||
bool Log(uint8_t indent, uint64_t pc, uint64_t load_bias, const DwarfFde* fde) override;
|
||||
|
||||
protected:
|
||||
bool EvalExpression(const DwarfLocation& loc, uint8_t version, Memory* regular_memory,
|
||||
AddressType* value);
|
||||
};
|
||||
|
||||
#endif // _LIBUNWINDSTACK_DWARF_SECTION_H
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
#include "DwarfEncoding.h"
|
||||
|
||||
struct DwarfCIE {
|
||||
struct DwarfCie {
|
||||
uint8_t version = 0;
|
||||
uint8_t fde_address_encoding = DW_EH_PE_absptr;
|
||||
uint8_t lsda_encoding = DW_EH_PE_omit;
|
||||
|
@ -37,14 +37,14 @@ struct DwarfCIE {
|
|||
uint64_t return_address_register = 0;
|
||||
};
|
||||
|
||||
struct DwarfFDE {
|
||||
struct DwarfFde {
|
||||
uint64_t cie_offset = 0;
|
||||
uint64_t cfa_instructions_offset = 0;
|
||||
uint64_t cfa_instructions_end = 0;
|
||||
uint64_t pc_start = 0;
|
||||
uint64_t pc_end = 0;
|
||||
uint64_t lsda_address = 0;
|
||||
const DwarfCIE* cie = nullptr;
|
||||
const DwarfCie* cie = nullptr;
|
||||
};
|
||||
|
||||
constexpr uint16_t CFA_REG = static_cast<uint16_t>(-1);
|
||||
|
|
|
@ -60,8 +60,8 @@ class DwarfCfaLogTest : public ::testing::Test {
|
|||
MemoryFake memory_;
|
||||
std::unique_ptr<DwarfMemory> dmem_;
|
||||
std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
|
||||
DwarfCIE cie_;
|
||||
DwarfFDE fde_;
|
||||
DwarfCie cie_;
|
||||
DwarfFde fde_;
|
||||
};
|
||||
TYPED_TEST_CASE_P(DwarfCfaLogTest);
|
||||
|
||||
|
|
|
@ -57,8 +57,8 @@ class DwarfCfaTest : public ::testing::Test {
|
|||
MemoryFake memory_;
|
||||
std::unique_ptr<DwarfMemory> dmem_;
|
||||
std::unique_ptr<DwarfCfa<TypeParam>> cfa_;
|
||||
DwarfCIE cie_;
|
||||
DwarfFDE fde_;
|
||||
DwarfCie cie_;
|
||||
DwarfFde fde_;
|
||||
};
|
||||
TYPED_TEST_CASE_P(DwarfCfaTest);
|
||||
|
||||
|
|
|
@ -25,21 +25,9 @@
|
|||
#include "DwarfMemory.h"
|
||||
#include "DwarfOp.h"
|
||||
#include "Log.h"
|
||||
#include "Regs.h"
|
||||
|
||||
#include "MemoryFake.h"
|
||||
|
||||
template <typename TypeParam>
|
||||
class RegsFake : public RegsTmpl<TypeParam> {
|
||||
public:
|
||||
RegsFake(uint16_t total_regs, uint16_t sp_reg)
|
||||
: RegsTmpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
|
||||
virtual ~RegsFake() = default;
|
||||
|
||||
uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
|
||||
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
|
||||
bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
|
||||
};
|
||||
#include "RegsFake.h"
|
||||
|
||||
template <typename TypeParam>
|
||||
class DwarfOpTest : public ::testing::Test {
|
||||
|
|
832
libunwindstack/tests/DwarfSectionImplTest.cpp
Normal file
832
libunwindstack/tests/DwarfSectionImplTest.cpp
Normal file
|
@ -0,0 +1,832 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "DwarfSection.h"
|
||||
|
||||
#include "LogFake.h"
|
||||
#include "MemoryFake.h"
|
||||
#include "RegsFake.h"
|
||||
|
||||
template <typename TypeParam>
|
||||
class MockDwarfSectionImpl : public DwarfSectionImpl<TypeParam> {
|
||||
public:
|
||||
MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl<TypeParam>(memory) {}
|
||||
virtual ~MockDwarfSectionImpl() = default;
|
||||
|
||||
MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
|
||||
|
||||
MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
|
||||
|
||||
MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
|
||||
|
||||
MOCK_METHOD1(IsCie32, bool(uint32_t));
|
||||
|
||||
MOCK_METHOD1(IsCie64, bool(uint64_t));
|
||||
|
||||
MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
|
||||
|
||||
MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
|
||||
|
||||
MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
|
||||
|
||||
void TestSetCachedCieEntry(uint64_t offset, const DwarfCie& cie) {
|
||||
this->cie_entries_[offset] = cie;
|
||||
}
|
||||
void TestClearCachedCieEntry() { this->cie_entries_.clear(); }
|
||||
|
||||
void TestSetCachedFdeEntry(uint64_t offset, const DwarfFde& fde) {
|
||||
this->fde_entries_[offset] = fde;
|
||||
}
|
||||
void TestClearCachedFdeEntry() { this->fde_entries_.clear(); }
|
||||
|
||||
void TestSetCachedCieLocRegs(uint64_t offset, const dwarf_loc_regs_t& loc_regs) {
|
||||
this->cie_loc_regs_[offset] = loc_regs;
|
||||
}
|
||||
void TestClearCachedCieLocRegs() { this->cie_loc_regs_.clear(); }
|
||||
|
||||
void TestClearError() { this->last_error_ = DWARF_ERROR_NONE; }
|
||||
};
|
||||
|
||||
template <typename TypeParam>
|
||||
class DwarfSectionImplTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
memory_.Clear();
|
||||
section_ = new MockDwarfSectionImpl<TypeParam>(&memory_);
|
||||
ResetLogs();
|
||||
}
|
||||
|
||||
void TearDown() override { delete section_; }
|
||||
|
||||
MemoryFake memory_;
|
||||
MockDwarfSectionImpl<TypeParam>* section_ = nullptr;
|
||||
};
|
||||
TYPED_TEST_CASE_P(DwarfSectionImplTest);
|
||||
|
||||
// NOTE: All test class variables need to be referenced as this->.
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
|
||||
DwarfCie cie{.version = 3, .return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x20;
|
||||
regs[9] = 0x3000;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
|
||||
DwarfCie cie{.version = 3, .return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x20;
|
||||
regs[9] = 0x3000;
|
||||
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
|
||||
DwarfCie cie{.version = 3, .return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x20;
|
||||
regs[9] = 0x3000;
|
||||
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
|
||||
TypeParam cfa_value = 0x12345;
|
||||
this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0x12345U, regs.sp());
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
|
||||
DwarfCie cie{.version = 3, .return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x20;
|
||||
regs[9] = 0x3000;
|
||||
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0x80000000U, regs.sp());
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
|
||||
DwarfCie cie{.version = 3, .return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x20;
|
||||
regs[9] = 0x3000;
|
||||
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
|
||||
DwarfCie cie{.return_address_register = 60};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
|
||||
this->section_->TestClearError();
|
||||
loc_regs.erase(CFA_REG);
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
|
||||
this->section_->TestClearError();
|
||||
loc_regs.erase(CFA_REG);
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
|
||||
this->section_->TestClearError();
|
||||
loc_regs.erase(CFA_REG);
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x20;
|
||||
regs[9] = 0x3000;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
EXPECT_EQ(0x2000U, regs.sp());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x20;
|
||||
regs[6] = 0x4000;
|
||||
regs[9] = 0x3000;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
EXPECT_EQ(0x4000U, regs.sp());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[8] = 0x10;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
|
||||
loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[8] = 0x10;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
if (sizeof(TypeParam) == sizeof(uint64_t)) {
|
||||
this->memory_.SetData64(0x2150, 0x12345678abcdef00ULL);
|
||||
} else {
|
||||
this->memory_.SetData32(0x2150, 0x12345678);
|
||||
}
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[3] = 0x234;
|
||||
regs[5] = 0x10;
|
||||
regs[8] = 0x2100;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
|
||||
loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
|
||||
loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0x10U, regs.pc());
|
||||
EXPECT_EQ(0x2100U, regs.sp());
|
||||
EXPECT_EQ(0x2200U, regs[1]);
|
||||
EXPECT_EQ(0x234U, regs[3]);
|
||||
if (sizeof(TypeParam) == sizeof(uint64_t)) {
|
||||
EXPECT_EQ(0x12345678abcdef00ULL, regs[2]);
|
||||
} else {
|
||||
EXPECT_EQ(0x12345678U, regs[2]);
|
||||
}
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x20;
|
||||
regs[8] = 0x10;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0U, regs.pc());
|
||||
EXPECT_EQ(0x10U, regs.sp());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x20;
|
||||
regs[8] = 0x10;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
EXPECT_EQ(0x10U, regs.sp());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
|
||||
DwarfCie cie{.return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x20;
|
||||
regs[8] = 0x10;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
// This should not result in any errors.
|
||||
loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
EXPECT_EQ(0x10U, regs.sp());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
|
||||
DwarfCie cie{.version = 3, .return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[8] = 0x3000;
|
||||
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
|
||||
TypeParam cfa_value = 0x12345;
|
||||
this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0x3000U, regs.sp());
|
||||
EXPECT_EQ(0x12345U, regs.pc());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
|
||||
DwarfCie cie{.version = 3, .return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[8] = 0x3000;
|
||||
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0x3000U, regs.sp());
|
||||
EXPECT_EQ(0x80000000U, regs.pc());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
|
||||
DwarfCie cie{.version = 3, .return_address_register = 5};
|
||||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
regs.set_pc(0x100);
|
||||
regs.set_sp(0x2000);
|
||||
regs[5] = 0x100;
|
||||
regs[8] = 0x2000;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
EXPECT_EQ(0x2000U, regs.sp());
|
||||
EXPECT_EQ(0x100U, regs.pc());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetCie_fail_should_not_cache) {
|
||||
ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
|
||||
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
|
||||
this->section_->TestClearError();
|
||||
ASSERT_TRUE(this->section_->GetCie(0x4000) == nullptr);
|
||||
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetCie_32_version_check) {
|
||||
this->memory_.SetData32(0x5000, 0x100);
|
||||
this->memory_.SetData32(0x5004, 0xffffffff);
|
||||
this->memory_.SetData8(0x5008, 0x1);
|
||||
this->memory_.SetData8(0x5009, '\0');
|
||||
this->memory_.SetData8(0x500a, 4);
|
||||
this->memory_.SetData8(0x500b, 8);
|
||||
this->memory_.SetData8(0x500c, 0x20);
|
||||
|
||||
EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
|
||||
|
||||
const DwarfCie* cie = this->section_->GetCie(0x5000);
|
||||
ASSERT_TRUE(cie != nullptr);
|
||||
EXPECT_EQ(1U, cie->version);
|
||||
EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
|
||||
EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
|
||||
EXPECT_EQ(0U, cie->segment_size);
|
||||
EXPECT_EQ(1U, cie->augmentation_string.size());
|
||||
EXPECT_EQ('\0', cie->augmentation_string[0]);
|
||||
EXPECT_EQ(0U, cie->personality_handler);
|
||||
EXPECT_EQ(0x500dU, cie->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
|
||||
EXPECT_EQ(4U, cie->code_alignment_factor);
|
||||
EXPECT_EQ(8, cie->data_alignment_factor);
|
||||
EXPECT_EQ(0x20U, cie->return_address_register);
|
||||
EXPECT_EQ(DWARF_ERROR_NONE, this->section_->last_error());
|
||||
|
||||
this->section_->TestClearCachedCieEntry();
|
||||
// Set version to 0, 2, 5 and verify we fail.
|
||||
this->memory_.SetData8(0x5008, 0x0);
|
||||
this->section_->TestClearError();
|
||||
ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
|
||||
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
|
||||
|
||||
this->memory_.SetData8(0x5008, 0x2);
|
||||
this->section_->TestClearError();
|
||||
ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
|
||||
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
|
||||
|
||||
this->memory_.SetData8(0x5008, 0x5);
|
||||
this->section_->TestClearError();
|
||||
ASSERT_TRUE(this->section_->GetCie(0x5000) == nullptr);
|
||||
EXPECT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetCie_negative_data_alignment_factor) {
|
||||
this->memory_.SetData32(0x5000, 0x100);
|
||||
this->memory_.SetData32(0x5004, 0xffffffff);
|
||||
this->memory_.SetData8(0x5008, 0x1);
|
||||
this->memory_.SetData8(0x5009, '\0');
|
||||
this->memory_.SetData8(0x500a, 4);
|
||||
this->memory_.SetMemory(0x500b, std::vector<uint8_t>{0xfc, 0xff, 0xff, 0xff, 0x7f});
|
||||
this->memory_.SetData8(0x5010, 0x20);
|
||||
|
||||
EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
|
||||
|
||||
const DwarfCie* cie = this->section_->GetCie(0x5000);
|
||||
ASSERT_TRUE(cie != nullptr);
|
||||
EXPECT_EQ(1U, cie->version);
|
||||
EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
|
||||
EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
|
||||
EXPECT_EQ(0U, cie->segment_size);
|
||||
EXPECT_EQ(1U, cie->augmentation_string.size());
|
||||
EXPECT_EQ('\0', cie->augmentation_string[0]);
|
||||
EXPECT_EQ(0U, cie->personality_handler);
|
||||
EXPECT_EQ(0x5011U, cie->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
|
||||
EXPECT_EQ(4U, cie->code_alignment_factor);
|
||||
EXPECT_EQ(-4, cie->data_alignment_factor);
|
||||
EXPECT_EQ(0x20U, cie->return_address_register);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetCie_64_no_augment) {
|
||||
this->memory_.SetData32(0x8000, 0xffffffff);
|
||||
this->memory_.SetData64(0x8004, 0x200);
|
||||
this->memory_.SetData64(0x800c, 0xffffffff);
|
||||
this->memory_.SetData8(0x8014, 0x1);
|
||||
this->memory_.SetData8(0x8015, '\0');
|
||||
this->memory_.SetData8(0x8016, 4);
|
||||
this->memory_.SetData8(0x8017, 8);
|
||||
this->memory_.SetData8(0x8018, 0x20);
|
||||
|
||||
EXPECT_CALL(*this->section_, IsCie64(0xffffffff)).WillRepeatedly(::testing::Return(true));
|
||||
|
||||
const DwarfCie* cie = this->section_->GetCie(0x8000);
|
||||
ASSERT_TRUE(cie != nullptr);
|
||||
EXPECT_EQ(1U, cie->version);
|
||||
EXPECT_EQ(DW_EH_PE_sdata8, cie->fde_address_encoding);
|
||||
EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
|
||||
EXPECT_EQ(0U, cie->segment_size);
|
||||
EXPECT_EQ(1U, cie->augmentation_string.size());
|
||||
EXPECT_EQ('\0', cie->augmentation_string[0]);
|
||||
EXPECT_EQ(0U, cie->personality_handler);
|
||||
EXPECT_EQ(0x8019U, cie->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x820cU, cie->cfa_instructions_end);
|
||||
EXPECT_EQ(4U, cie->code_alignment_factor);
|
||||
EXPECT_EQ(8, cie->data_alignment_factor);
|
||||
EXPECT_EQ(0x20U, cie->return_address_register);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetCie_augment) {
|
||||
this->memory_.SetData32(0x5000, 0x100);
|
||||
this->memory_.SetData32(0x5004, 0xffffffff);
|
||||
this->memory_.SetData8(0x5008, 0x1);
|
||||
this->memory_.SetMemory(0x5009, std::vector<uint8_t>{'z', 'L', 'P', 'R', '\0'});
|
||||
this->memory_.SetData8(0x500e, 4);
|
||||
this->memory_.SetData8(0x500f, 8);
|
||||
this->memory_.SetData8(0x5010, 0x10);
|
||||
// Augment length.
|
||||
this->memory_.SetData8(0x5011, 0xf);
|
||||
// L data.
|
||||
this->memory_.SetData8(0x5012, DW_EH_PE_textrel | DW_EH_PE_udata2);
|
||||
// P data.
|
||||
this->memory_.SetData8(0x5013, DW_EH_PE_udata4);
|
||||
this->memory_.SetData32(0x5014, 0x12345678);
|
||||
// R data.
|
||||
this->memory_.SetData8(0x5018, DW_EH_PE_udata2);
|
||||
|
||||
EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
|
||||
|
||||
const DwarfCie* cie = this->section_->GetCie(0x5000);
|
||||
ASSERT_TRUE(cie != nullptr);
|
||||
EXPECT_EQ(1U, cie->version);
|
||||
EXPECT_EQ(DW_EH_PE_udata2, cie->fde_address_encoding);
|
||||
EXPECT_EQ(DW_EH_PE_textrel | DW_EH_PE_udata2, cie->lsda_encoding);
|
||||
EXPECT_EQ(0U, cie->segment_size);
|
||||
EXPECT_EQ(5U, cie->augmentation_string.size());
|
||||
EXPECT_EQ('z', cie->augmentation_string[0]);
|
||||
EXPECT_EQ('L', cie->augmentation_string[1]);
|
||||
EXPECT_EQ('P', cie->augmentation_string[2]);
|
||||
EXPECT_EQ('R', cie->augmentation_string[3]);
|
||||
EXPECT_EQ('\0', cie->augmentation_string[4]);
|
||||
EXPECT_EQ(0x12345678U, cie->personality_handler);
|
||||
EXPECT_EQ(0x5021U, cie->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
|
||||
EXPECT_EQ(4U, cie->code_alignment_factor);
|
||||
EXPECT_EQ(8, cie->data_alignment_factor);
|
||||
EXPECT_EQ(0x10U, cie->return_address_register);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_3) {
|
||||
this->memory_.SetData32(0x5000, 0x100);
|
||||
this->memory_.SetData32(0x5004, 0xffffffff);
|
||||
this->memory_.SetData8(0x5008, 0x3);
|
||||
this->memory_.SetData8(0x5009, '\0');
|
||||
this->memory_.SetData8(0x500a, 4);
|
||||
this->memory_.SetData8(0x500b, 8);
|
||||
this->memory_.SetMemory(0x500c, std::vector<uint8_t>{0x81, 0x03});
|
||||
|
||||
EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
|
||||
|
||||
const DwarfCie* cie = this->section_->GetCie(0x5000);
|
||||
ASSERT_TRUE(cie != nullptr);
|
||||
EXPECT_EQ(3U, cie->version);
|
||||
EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
|
||||
EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
|
||||
EXPECT_EQ(0U, cie->segment_size);
|
||||
EXPECT_EQ(1U, cie->augmentation_string.size());
|
||||
EXPECT_EQ('\0', cie->augmentation_string[0]);
|
||||
EXPECT_EQ(0U, cie->personality_handler);
|
||||
EXPECT_EQ(0x500eU, cie->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
|
||||
EXPECT_EQ(4U, cie->code_alignment_factor);
|
||||
EXPECT_EQ(8, cie->data_alignment_factor);
|
||||
EXPECT_EQ(0x181U, cie->return_address_register);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetCie_version_4) {
|
||||
this->memory_.SetData32(0x5000, 0x100);
|
||||
this->memory_.SetData32(0x5004, 0xffffffff);
|
||||
this->memory_.SetData8(0x5008, 0x4);
|
||||
this->memory_.SetData8(0x5009, '\0');
|
||||
this->memory_.SetData8(0x500a, 4);
|
||||
this->memory_.SetData8(0x500b, 0x13);
|
||||
this->memory_.SetData8(0x500c, 4);
|
||||
this->memory_.SetData8(0x500d, 8);
|
||||
this->memory_.SetMemory(0x500e, std::vector<uint8_t>{0x81, 0x03});
|
||||
|
||||
EXPECT_CALL(*this->section_, IsCie32(0xffffffff)).WillRepeatedly(::testing::Return(true));
|
||||
|
||||
const DwarfCie* cie = this->section_->GetCie(0x5000);
|
||||
ASSERT_TRUE(cie != nullptr);
|
||||
EXPECT_EQ(4U, cie->version);
|
||||
EXPECT_EQ(DW_EH_PE_sdata4, cie->fde_address_encoding);
|
||||
EXPECT_EQ(DW_EH_PE_omit, cie->lsda_encoding);
|
||||
EXPECT_EQ(0x13U, cie->segment_size);
|
||||
EXPECT_EQ(1U, cie->augmentation_string.size());
|
||||
EXPECT_EQ('\0', cie->augmentation_string[0]);
|
||||
EXPECT_EQ(0U, cie->personality_handler);
|
||||
EXPECT_EQ(0x5010U, cie->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x5104U, cie->cfa_instructions_end);
|
||||
EXPECT_EQ(4U, cie->code_alignment_factor);
|
||||
EXPECT_EQ(8, cie->data_alignment_factor);
|
||||
EXPECT_EQ(0x181U, cie->return_address_register);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_fail_should_not_cache) {
|
||||
ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
|
||||
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
|
||||
this->section_->TestClearError();
|
||||
ASSERT_TRUE(this->section_->GetFdeFromOffset(0x4000) == nullptr);
|
||||
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment) {
|
||||
this->memory_.SetData32(0x4000, 0x20);
|
||||
this->memory_.SetData32(0x4004, 0x8000);
|
||||
this->memory_.SetData32(0x4008, 0x5000);
|
||||
this->memory_.SetData32(0x400c, 0x100);
|
||||
|
||||
EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
|
||||
EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
|
||||
DwarfCie cie{};
|
||||
cie.fde_address_encoding = DW_EH_PE_udata4;
|
||||
this->section_->TestSetCachedCieEntry(0x8000, cie);
|
||||
EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
|
||||
|
||||
const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
|
||||
ASSERT_TRUE(fde != nullptr);
|
||||
ASSERT_TRUE(fde->cie != nullptr);
|
||||
EXPECT_EQ(0x4010U, fde->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x4024U, fde->cfa_instructions_end);
|
||||
EXPECT_EQ(0x5000U, fde->pc_start);
|
||||
EXPECT_EQ(0x5100U, fde->pc_end);
|
||||
EXPECT_EQ(0x8000U, fde->cie_offset);
|
||||
EXPECT_EQ(0U, fde->lsda_address);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_no_augment_non_zero_segment_size) {
|
||||
this->memory_.SetData32(0x4000, 0x30);
|
||||
this->memory_.SetData32(0x4004, 0x8000);
|
||||
this->memory_.SetData32(0x4018, 0x5000);
|
||||
this->memory_.SetData32(0x401c, 0x100);
|
||||
|
||||
EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
|
||||
EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
|
||||
DwarfCie cie{};
|
||||
cie.fde_address_encoding = DW_EH_PE_udata4;
|
||||
cie.segment_size = 0x10;
|
||||
this->section_->TestSetCachedCieEntry(0x8000, cie);
|
||||
EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
|
||||
|
||||
const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
|
||||
ASSERT_TRUE(fde != nullptr);
|
||||
ASSERT_TRUE(fde->cie != nullptr);
|
||||
EXPECT_EQ(0x4020U, fde->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x4034U, fde->cfa_instructions_end);
|
||||
EXPECT_EQ(0x5000U, fde->pc_start);
|
||||
EXPECT_EQ(0x5100U, fde->pc_end);
|
||||
EXPECT_EQ(0x8000U, fde->cie_offset);
|
||||
EXPECT_EQ(0U, fde->lsda_address);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_32_augment) {
|
||||
this->memory_.SetData32(0x4000, 0x100);
|
||||
this->memory_.SetData32(0x4004, 0x8000);
|
||||
this->memory_.SetData32(0x4008, 0x5000);
|
||||
this->memory_.SetData32(0x400c, 0x100);
|
||||
this->memory_.SetMemory(0x4010, std::vector<uint8_t>{0x82, 0x01});
|
||||
this->memory_.SetData16(0x4012, 0x1234);
|
||||
|
||||
EXPECT_CALL(*this->section_, IsCie32(0x8000)).WillOnce(::testing::Return(false));
|
||||
EXPECT_CALL(*this->section_, GetCieOffsetFromFde32(0x8000)).WillOnce(::testing::Return(0x8000));
|
||||
DwarfCie cie{};
|
||||
cie.fde_address_encoding = DW_EH_PE_udata4;
|
||||
cie.augmentation_string.push_back('z');
|
||||
cie.lsda_encoding = DW_EH_PE_udata2;
|
||||
this->section_->TestSetCachedCieEntry(0x8000, cie);
|
||||
EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
|
||||
|
||||
const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
|
||||
ASSERT_TRUE(fde != nullptr);
|
||||
ASSERT_TRUE(fde->cie != nullptr);
|
||||
EXPECT_EQ(0x4094U, fde->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x4104U, fde->cfa_instructions_end);
|
||||
EXPECT_EQ(0x5000U, fde->pc_start);
|
||||
EXPECT_EQ(0x5100U, fde->pc_end);
|
||||
EXPECT_EQ(0x8000U, fde->cie_offset);
|
||||
EXPECT_EQ(0x1234U, fde->lsda_address);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_64_no_augment) {
|
||||
this->memory_.SetData32(0x4000, 0xffffffff);
|
||||
this->memory_.SetData64(0x4004, 0x100);
|
||||
this->memory_.SetData64(0x400c, 0x12345678);
|
||||
this->memory_.SetData32(0x4014, 0x5000);
|
||||
this->memory_.SetData32(0x4018, 0x100);
|
||||
|
||||
EXPECT_CALL(*this->section_, IsCie64(0x12345678)).WillOnce(::testing::Return(false));
|
||||
EXPECT_CALL(*this->section_, GetCieOffsetFromFde64(0x12345678))
|
||||
.WillOnce(::testing::Return(0x12345678));
|
||||
DwarfCie cie{};
|
||||
cie.fde_address_encoding = DW_EH_PE_udata4;
|
||||
this->section_->TestSetCachedCieEntry(0x12345678, cie);
|
||||
EXPECT_CALL(*this->section_, AdjustPcFromFde(0x5000)).WillOnce(::testing::Return(0x5000));
|
||||
|
||||
const DwarfFde* fde = this->section_->GetFdeFromOffset(0x4000);
|
||||
ASSERT_TRUE(fde != nullptr);
|
||||
ASSERT_TRUE(fde->cie != nullptr);
|
||||
EXPECT_EQ(0x401cU, fde->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x410cU, fde->cfa_instructions_end);
|
||||
EXPECT_EQ(0x5000U, fde->pc_start);
|
||||
EXPECT_EQ(0x5100U, fde->pc_end);
|
||||
EXPECT_EQ(0x12345678U, fde->cie_offset);
|
||||
EXPECT_EQ(0U, fde->lsda_address);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetFdeFromOffset_cached) {
|
||||
DwarfCie cie{};
|
||||
cie.fde_address_encoding = DW_EH_PE_udata4;
|
||||
cie.augmentation_string.push_back('z');
|
||||
cie.lsda_encoding = DW_EH_PE_udata2;
|
||||
|
||||
DwarfFde fde_cached{};
|
||||
fde_cached.cfa_instructions_offset = 0x1000;
|
||||
fde_cached.cfa_instructions_end = 0x1100;
|
||||
fde_cached.pc_start = 0x9000;
|
||||
fde_cached.pc_end = 0x9400;
|
||||
fde_cached.cie_offset = 0x30000;
|
||||
fde_cached.cie = &cie;
|
||||
this->section_->TestSetCachedFdeEntry(0x6000, fde_cached);
|
||||
|
||||
const DwarfFde* fde = this->section_->GetFdeFromOffset(0x6000);
|
||||
ASSERT_TRUE(fde != nullptr);
|
||||
ASSERT_EQ(&cie, fde->cie);
|
||||
EXPECT_EQ(0x1000U, fde->cfa_instructions_offset);
|
||||
EXPECT_EQ(0x1100U, fde->cfa_instructions_end);
|
||||
EXPECT_EQ(0x9000U, fde->pc_start);
|
||||
EXPECT_EQ(0x9400U, fde->pc_end);
|
||||
EXPECT_EQ(0x30000U, fde->cie_offset);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_not_cached) {
|
||||
DwarfCie cie{};
|
||||
cie.cfa_instructions_offset = 0x3000;
|
||||
cie.cfa_instructions_end = 0x3002;
|
||||
DwarfFde fde{};
|
||||
fde.cie = &cie;
|
||||
fde.cie_offset = 0x8000;
|
||||
fde.cfa_instructions_offset = 0x6000;
|
||||
fde.cfa_instructions_end = 0x6002;
|
||||
|
||||
this->memory_.SetMemory(0x3000, std::vector<uint8_t>{0x09, 0x02, 0x01});
|
||||
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_EQ(2U, loc_regs.size());
|
||||
|
||||
auto entry = loc_regs.find(2);
|
||||
ASSERT_NE(entry, loc_regs.end());
|
||||
ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
|
||||
ASSERT_EQ(1U, entry->second.values[0]);
|
||||
|
||||
entry = loc_regs.find(4);
|
||||
ASSERT_NE(entry, loc_regs.end());
|
||||
ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
|
||||
ASSERT_EQ(3U, entry->second.values[0]);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, GetCfaLocationInfo_cie_cached) {
|
||||
DwarfCie cie{};
|
||||
cie.cfa_instructions_offset = 0x3000;
|
||||
cie.cfa_instructions_end = 0x3002;
|
||||
DwarfFde fde{};
|
||||
fde.cie = &cie;
|
||||
fde.cie_offset = 0x8000;
|
||||
fde.cfa_instructions_offset = 0x6000;
|
||||
fde.cfa_instructions_end = 0x6002;
|
||||
|
||||
dwarf_loc_regs_t cie_loc_regs{{6, {DWARF_LOCATION_REGISTER, {4, 0}}}};
|
||||
this->section_->TestSetCachedCieLocRegs(0x8000, cie_loc_regs);
|
||||
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_EQ(2U, loc_regs.size());
|
||||
|
||||
auto entry = loc_regs.find(6);
|
||||
ASSERT_NE(entry, loc_regs.end());
|
||||
ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
|
||||
ASSERT_EQ(4U, entry->second.values[0]);
|
||||
|
||||
entry = loc_regs.find(4);
|
||||
ASSERT_NE(entry, loc_regs.end());
|
||||
ASSERT_EQ(DWARF_LOCATION_REGISTER, entry->second.type);
|
||||
ASSERT_EQ(3U, entry->second.values[0]);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(DwarfSectionImplTest, Log) {
|
||||
DwarfCie cie{};
|
||||
cie.cfa_instructions_offset = 0x5000;
|
||||
cie.cfa_instructions_end = 0x5001;
|
||||
DwarfFde fde{};
|
||||
fde.cie = &cie;
|
||||
fde.cfa_instructions_offset = 0x6000;
|
||||
fde.cfa_instructions_end = 0x6001;
|
||||
|
||||
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, 0x1000, &fde));
|
||||
|
||||
ASSERT_EQ(
|
||||
"4 unwind DW_CFA_nop\n"
|
||||
"4 unwind Raw Data: 0x00\n"
|
||||
"4 unwind DW_CFA_restore register(2)\n"
|
||||
"4 unwind Raw Data: 0xc2\n",
|
||||
GetFakeLogPrint());
|
||||
ASSERT_EQ("", GetFakeLogBuf());
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_CASE_P(
|
||||
DwarfSectionImplTest, Eval_cfa_expr_eval_fail, Eval_cfa_expr_no_stack,
|
||||
Eval_cfa_expr_is_register, Eval_cfa_expr, Eval_cfa_val_expr, Eval_bad_regs, Eval_no_cfa,
|
||||
Eval_cfa_bad, Eval_cfa_register_prev, Eval_cfa_register_from_value, Eval_double_indirection,
|
||||
Eval_invalid_register, Eval_different_reg_locations, Eval_return_address_undefined,
|
||||
Eval_return_address, Eval_ignore_large_reg_loc, Eval_reg_expr, Eval_reg_val_expr,
|
||||
Eval_same_cfa_same_pc, GetCie_fail_should_not_cache, GetCie_32_version_check,
|
||||
GetCie_negative_data_alignment_factor, GetCie_64_no_augment, GetCie_augment, GetCie_version_3,
|
||||
GetCie_version_4, GetFdeFromOffset_fail_should_not_cache, GetFdeFromOffset_32_no_augment,
|
||||
GetFdeFromOffset_32_no_augment_non_zero_segment_size, GetFdeFromOffset_32_augment,
|
||||
GetFdeFromOffset_64_no_augment, GetFdeFromOffset_cached, GetCfaLocationInfo_cie_not_cached,
|
||||
GetCfaLocationInfo_cie_cached, Log);
|
||||
|
||||
typedef ::testing::Types<uint32_t, uint64_t> DwarfSectionImplTestTypes;
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes);
|
160
libunwindstack/tests/DwarfSectionTest.cpp
Normal file
160
libunwindstack/tests/DwarfSectionTest.cpp
Normal file
|
@ -0,0 +1,160 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "DwarfSection.h"
|
||||
|
||||
#include "MemoryFake.h"
|
||||
|
||||
class MockDwarfSection : public DwarfSection {
|
||||
public:
|
||||
MockDwarfSection(Memory* memory) : DwarfSection(memory) {}
|
||||
virtual ~MockDwarfSection() = default;
|
||||
|
||||
MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
|
||||
|
||||
MOCK_METHOD4(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*));
|
||||
|
||||
MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
|
||||
|
||||
MOCK_METHOD2(Init, bool(uint64_t, uint64_t));
|
||||
|
||||
MOCK_METHOD2(GetFdeOffsetFromPc, bool(uint64_t, uint64_t*));
|
||||
|
||||
MOCK_METHOD1(GetFdeFromOffset, const DwarfFde*(uint64_t));
|
||||
|
||||
MOCK_METHOD1(GetFdeFromIndex, const DwarfFde*(size_t));
|
||||
|
||||
MOCK_METHOD1(IsCie32, bool(uint32_t));
|
||||
|
||||
MOCK_METHOD1(IsCie64, bool(uint64_t));
|
||||
|
||||
MOCK_METHOD1(GetCieOffsetFromFde32, uint64_t(uint32_t));
|
||||
|
||||
MOCK_METHOD1(GetCieOffsetFromFde64, uint64_t(uint64_t));
|
||||
|
||||
MOCK_METHOD1(AdjustPcFromFde, uint64_t(uint64_t));
|
||||
};
|
||||
|
||||
class DwarfSectionTest : public ::testing::Test {
|
||||
protected:
|
||||
MemoryFake memory_;
|
||||
};
|
||||
|
||||
TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_from_pc) {
|
||||
MockDwarfSection mock_section(&memory_);
|
||||
|
||||
EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
|
||||
.WillOnce(::testing::Return(false));
|
||||
|
||||
// Verify nullptr when GetFdeOffsetFromPc fails.
|
||||
ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_fail_fde_pc_end) {
|
||||
MockDwarfSection mock_section(&memory_);
|
||||
|
||||
DwarfFde fde{};
|
||||
fde.pc_end = 0x500;
|
||||
|
||||
EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
|
||||
.WillOnce(::testing::Return(true));
|
||||
EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
|
||||
|
||||
// Verify nullptr when GetFdeOffsetFromPc fails.
|
||||
ASSERT_TRUE(mock_section.GetFdeFromPc(0x1000) == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(DwarfSectionTest, GetFdeOffsetFromPc_pass) {
|
||||
MockDwarfSection mock_section(&memory_);
|
||||
|
||||
DwarfFde fde{};
|
||||
fde.pc_end = 0x2000;
|
||||
|
||||
EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
|
||||
.WillOnce(::testing::Return(true));
|
||||
EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
|
||||
|
||||
// Verify nullptr when GetFdeOffsetFromPc fails.
|
||||
ASSERT_EQ(&fde, mock_section.GetFdeFromPc(0x1000));
|
||||
}
|
||||
|
||||
TEST_F(DwarfSectionTest, Step_fail_fde) {
|
||||
MockDwarfSection mock_section(&memory_);
|
||||
|
||||
EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
|
||||
.WillOnce(::testing::Return(false));
|
||||
|
||||
ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DwarfSectionTest, Step_fail_cie_null) {
|
||||
MockDwarfSection mock_section(&memory_);
|
||||
|
||||
DwarfFde fde{};
|
||||
fde.pc_end = 0x2000;
|
||||
fde.cie = nullptr;
|
||||
|
||||
EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
|
||||
.WillOnce(::testing::Return(true));
|
||||
EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
|
||||
|
||||
ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
|
||||
MockDwarfSection mock_section(&memory_);
|
||||
|
||||
DwarfCie cie{};
|
||||
DwarfFde fde{};
|
||||
fde.pc_end = 0x2000;
|
||||
fde.cie = &cie;
|
||||
|
||||
EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
|
||||
.WillOnce(::testing::Return(true));
|
||||
EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
|
||||
|
||||
EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
|
||||
.WillOnce(::testing::Return(false));
|
||||
|
||||
ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
|
||||
}
|
||||
|
||||
TEST_F(DwarfSectionTest, Step_pass) {
|
||||
MockDwarfSection mock_section(&memory_);
|
||||
|
||||
DwarfCie cie{};
|
||||
DwarfFde fde{};
|
||||
fde.pc_end = 0x2000;
|
||||
fde.cie = &cie;
|
||||
|
||||
EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
|
||||
.WillOnce(::testing::Return(true));
|
||||
EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
|
||||
|
||||
EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
|
||||
.WillOnce(::testing::Return(true));
|
||||
|
||||
MemoryFake process;
|
||||
EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr))
|
||||
.WillOnce(::testing::Return(true));
|
||||
|
||||
ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process));
|
||||
}
|
36
libunwindstack/tests/RegsFake.h
Normal file
36
libunwindstack/tests/RegsFake.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2016 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
|
||||
#define _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "Regs.h"
|
||||
|
||||
template <typename TypeParam>
|
||||
class RegsFake : public RegsTmpl<TypeParam> {
|
||||
public:
|
||||
RegsFake(uint16_t total_regs, uint16_t sp_reg)
|
||||
: RegsTmpl<TypeParam>(total_regs, sp_reg, Regs::Location(Regs::LOCATION_UNKNOWN, 0)) {}
|
||||
virtual ~RegsFake() = default;
|
||||
|
||||
uint64_t GetRelPc(Elf*, const MapInfo*) override { return 0; }
|
||||
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
|
||||
bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
|
||||
};
|
||||
|
||||
#endif // _LIBUNWINDSTACK_TESTS_REGS_FAKE_H
|
Loading…
Reference in a new issue