From 53a3c9b4ab57f04b46bc3abf42e36bdfa821f7f3 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Wed, 10 May 2017 18:34:15 -0700 Subject: [PATCH] Add DwarfSection classes. Bug: 23762183 Test: Ran new unit tests. Change-Id: Icca2a73c50d467718ba4ac41e1c8f541488620dd --- libunwindstack/Android.bp | 7 + libunwindstack/DwarfCfa.h | 4 +- libunwindstack/DwarfError.h | 2 + libunwindstack/DwarfSection.cpp | 543 ++++++++++++ libunwindstack/DwarfSection.h | 137 +++ libunwindstack/DwarfStructs.h | 6 +- libunwindstack/tests/DwarfCfaLogTest.cpp | 4 +- libunwindstack/tests/DwarfCfaTest.cpp | 4 +- libunwindstack/tests/DwarfOpTest.cpp | 14 +- libunwindstack/tests/DwarfSectionImplTest.cpp | 832 ++++++++++++++++++ libunwindstack/tests/DwarfSectionTest.cpp | 160 ++++ libunwindstack/tests/RegsFake.h | 36 + 12 files changed, 1727 insertions(+), 22 deletions(-) create mode 100644 libunwindstack/DwarfSection.cpp create mode 100644 libunwindstack/DwarfSection.h create mode 100644 libunwindstack/tests/DwarfSectionImplTest.cpp create mode 100644 libunwindstack/tests/DwarfSectionTest.cpp create mode 100644 libunwindstack/tests/RegsFake.h diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 32ed6c303..eb2b90229 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -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: [ diff --git a/libunwindstack/DwarfCfa.h b/libunwindstack/DwarfCfa.h index ce7da4a12..42ebae1ef 100644 --- a/libunwindstack/DwarfCfa.h +++ b/libunwindstack/DwarfCfa.h @@ -63,7 +63,7 @@ class DwarfCfa { typedef typename std::make_signed::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; diff --git a/libunwindstack/DwarfError.h b/libunwindstack/DwarfError.h index 824c30725..c00f17d65 100644 --- a/libunwindstack/DwarfError.h +++ b/libunwindstack/DwarfError.h @@ -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 diff --git a/libunwindstack/DwarfSection.cpp b/libunwindstack/DwarfSection.cpp new file mode 100644 index 000000000..0148ffb1d --- /dev/null +++ b/libunwindstack/DwarfSection.cpp @@ -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 + +#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 +bool DwarfSectionImpl::EvalExpression(const DwarfLocation& loc, uint8_t version, + Memory* regular_memory, AddressType* value) { + DwarfOp 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 +bool DwarfSectionImpl::Eval(const DwarfCie* cie, Memory* regular_memory, + const dwarf_loc_regs_t& loc_regs, Regs* regs) { + RegsTmpl* cur_regs = reinterpret_cast*>(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 +const DwarfCie* DwarfSectionImpl::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 +bool DwarfSectionImpl::FillInCie(DwarfCie* cie) { + uint32_t length32; + if (!memory_.ReadBytes(&length32, sizeof(length32))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + if (length32 == static_cast(-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(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 +const DwarfFde* DwarfSectionImpl::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 +bool DwarfSectionImpl::FillInFde(DwarfFde* fde) { + uint32_t length32; + if (!memory_.ReadBytes(&length32, sizeof(length32))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + if (length32 == static_cast(-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(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(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(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 +bool DwarfSectionImpl::GetCfaLocationInfo(uint64_t pc, const DwarfFde* fde, + dwarf_loc_regs_t* loc_regs) { + DwarfCfa 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 +bool DwarfSectionImpl::Log(uint8_t indent, uint64_t pc, uint64_t load_bias, + const DwarfFde* fde) { + DwarfCfa 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; +template class DwarfSectionImpl; diff --git a/libunwindstack/DwarfSection.h b/libunwindstack/DwarfSection.h new file mode 100644 index 000000000..e61760112 --- /dev/null +++ b/libunwindstack/DwarfSection.h @@ -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 + +#include +#include + +#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 { + 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 fde_entries_; + std::unordered_map cie_entries_; + std::unordered_map cie_loc_regs_; +}; + +template +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 diff --git a/libunwindstack/DwarfStructs.h b/libunwindstack/DwarfStructs.h index 57aac88b2..87182c93c 100644 --- a/libunwindstack/DwarfStructs.h +++ b/libunwindstack/DwarfStructs.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(-1); diff --git a/libunwindstack/tests/DwarfCfaLogTest.cpp b/libunwindstack/tests/DwarfCfaLogTest.cpp index 3185bc3bd..967c2c25f 100644 --- a/libunwindstack/tests/DwarfCfaLogTest.cpp +++ b/libunwindstack/tests/DwarfCfaLogTest.cpp @@ -60,8 +60,8 @@ class DwarfCfaLogTest : public ::testing::Test { MemoryFake memory_; std::unique_ptr dmem_; std::unique_ptr> cfa_; - DwarfCIE cie_; - DwarfFDE fde_; + DwarfCie cie_; + DwarfFde fde_; }; TYPED_TEST_CASE_P(DwarfCfaLogTest); diff --git a/libunwindstack/tests/DwarfCfaTest.cpp b/libunwindstack/tests/DwarfCfaTest.cpp index 6cf028a22..687af7d20 100644 --- a/libunwindstack/tests/DwarfCfaTest.cpp +++ b/libunwindstack/tests/DwarfCfaTest.cpp @@ -57,8 +57,8 @@ class DwarfCfaTest : public ::testing::Test { MemoryFake memory_; std::unique_ptr dmem_; std::unique_ptr> cfa_; - DwarfCIE cie_; - DwarfFDE fde_; + DwarfCie cie_; + DwarfFde fde_; }; TYPED_TEST_CASE_P(DwarfCfaTest); diff --git a/libunwindstack/tests/DwarfOpTest.cpp b/libunwindstack/tests/DwarfOpTest.cpp index 520c545d6..20c488a73 100644 --- a/libunwindstack/tests/DwarfOpTest.cpp +++ b/libunwindstack/tests/DwarfOpTest.cpp @@ -25,21 +25,9 @@ #include "DwarfMemory.h" #include "DwarfOp.h" #include "Log.h" -#include "Regs.h" #include "MemoryFake.h" - -template -class RegsFake : public RegsTmpl { - public: - RegsFake(uint16_t total_regs, uint16_t sp_reg) - : RegsTmpl(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 class DwarfOpTest : public ::testing::Test { diff --git a/libunwindstack/tests/DwarfSectionImplTest.cpp b/libunwindstack/tests/DwarfSectionImplTest.cpp new file mode 100644 index 000000000..71b114ad9 --- /dev/null +++ b/libunwindstack/tests/DwarfSectionImplTest.cpp @@ -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 + +#include +#include + +#include "DwarfSection.h" + +#include "LogFake.h" +#include "MemoryFake.h" +#include "RegsFake.h" + +template +class MockDwarfSectionImpl : public DwarfSectionImpl { + public: + MockDwarfSectionImpl(Memory* memory) : DwarfSectionImpl(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 +class DwarfSectionImplTest : public ::testing::Test { + protected: + void SetUp() override { + memory_.Clear(); + section_ = new MockDwarfSectionImpl(&memory_); + ResetLogs(); + } + + void TearDown() override { delete section_; } + + MemoryFake memory_; + MockDwarfSectionImpl* 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 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 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{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 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{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 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{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 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{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 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 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 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 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 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 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 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 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 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 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 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 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{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 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{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 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{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{'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{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{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{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{0x09, 0x02, 0x01}); + this->memory_.SetMemory(0x6000, std::vector{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{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{0x00}); + this->memory_.SetMemory(0x6000, std::vector{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 DwarfSectionImplTestTypes; +INSTANTIATE_TYPED_TEST_CASE_P(, DwarfSectionImplTest, DwarfSectionImplTestTypes); diff --git a/libunwindstack/tests/DwarfSectionTest.cpp b/libunwindstack/tests/DwarfSectionTest.cpp new file mode 100644 index 000000000..20f0e2a49 --- /dev/null +++ b/libunwindstack/tests/DwarfSectionTest.cpp @@ -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 + +#include +#include + +#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)); +} diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h new file mode 100644 index 000000000..dbc06a495 --- /dev/null +++ b/libunwindstack/tests/RegsFake.h @@ -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 + +#include "Regs.h" + +template +class RegsFake : public RegsTmpl { + public: + RegsFake(uint16_t total_regs, uint16_t sp_reg) + : RegsTmpl(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