From 61d4097737c897847b7600d7d5ddbd384efb8540 Mon Sep 17 00:00:00 2001 From: Christopher Ferris Date: Mon, 12 Jun 2017 19:14:20 -0700 Subject: [PATCH] Add EH frame and debug frame support. Bug: 23762183 Test: Pass new unit tests. Change-Id: I1bfe900e068017ff31998f359bf98d4c5c9af2a5 --- libunwindstack/Android.bp | 4 + libunwindstack/DwarfDebugFrame.cpp | 311 +++++++++++++ libunwindstack/DwarfDebugFrame.h | 76 ++++ libunwindstack/DwarfEhFrame.cpp | 213 +++++++++ libunwindstack/DwarfEhFrame.h | 89 ++++ libunwindstack/ElfInterface.cpp | 26 ++ libunwindstack/ElfInterface.h | 19 +- libunwindstack/tests/DwarfDebugFrameTest.cpp | 456 +++++++++++++++++++ libunwindstack/tests/DwarfEhFrameTest.cpp | 409 +++++++++++++++++ libunwindstack/tests/ElfInterfaceTest.cpp | 147 ++++++ 10 files changed, 1746 insertions(+), 4 deletions(-) create mode 100644 libunwindstack/DwarfDebugFrame.cpp create mode 100644 libunwindstack/DwarfDebugFrame.h create mode 100644 libunwindstack/DwarfEhFrame.cpp create mode 100644 libunwindstack/DwarfEhFrame.h create mode 100644 libunwindstack/tests/DwarfDebugFrameTest.cpp create mode 100644 libunwindstack/tests/DwarfEhFrameTest.cpp diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index eb2b90229..d7e949bad 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -48,6 +48,8 @@ cc_defaults { srcs: [ "ArmExidx.cpp", "DwarfCfa.cpp", + "DwarfDebugFrame.cpp", + "DwarfEhFrame.cpp", "DwarfMemory.cpp", "DwarfOp.cpp", "DwarfSection.cpp", @@ -96,6 +98,8 @@ cc_defaults { "tests/ArmExidxExtractTest.cpp", "tests/DwarfCfaLogTest.cpp", "tests/DwarfCfaTest.cpp", + "tests/DwarfDebugFrameTest.cpp", + "tests/DwarfEhFrameTest.cpp", "tests/DwarfMemoryTest.cpp", "tests/DwarfOpLogTest.cpp", "tests/DwarfOpTest.cpp", diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp new file mode 100644 index 000000000..3ac02fccb --- /dev/null +++ b/libunwindstack/DwarfDebugFrame.cpp @@ -0,0 +1,311 @@ +/* + * 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 + +#include + +#include "DwarfDebugFrame.h" +#include "DwarfMemory.h" +#include "DwarfSection.h" +#include "DwarfStructs.h" +#include "Memory.h" + +template +bool DwarfDebugFrame::Init(uint64_t offset, uint64_t size) { + offset_ = offset; + end_offset_ = offset + size; + + memory_.clear_func_offset(); + memory_.clear_text_offset(); + memory_.set_data_offset(offset); + memory_.set_cur_offset(offset); + + return CreateSortedFdeList(); +} + +template +bool DwarfDebugFrame::GetCieInfo(uint8_t* segment_size, uint8_t* encoding) { + uint8_t version; + if (!memory_.ReadBytes(&version, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + // Read the augmentation string. + std::vector aug_string; + char aug_value; + bool get_encoding = false; + do { + if (!memory_.ReadBytes(&aug_value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + if (aug_value == 'R') { + get_encoding = true; + } + aug_string.push_back(aug_value); + } while (aug_value != '\0'); + + if (version == 4) { + // Skip the Address Size field. + memory_.set_cur_offset(memory_.cur_offset() + 1); + + // Read the segment size. + if (!memory_.ReadBytes(segment_size, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } else { + *segment_size = 0; + } + + if (aug_string[0] != 'z' || !get_encoding) { + // No encoding + return true; + } + + // Skip code alignment factor + uint8_t value; + do { + if (!memory_.ReadBytes(&value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } while (value & 0x80); + + // Skip data alignment factor + do { + if (!memory_.ReadBytes(&value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } while (value & 0x80); + + if (version == 1) { + // Skip return address register. + memory_.set_cur_offset(memory_.cur_offset() + 1); + } else { + // Skip return address register. + do { + if (!memory_.ReadBytes(&value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } while (value & 0x80); + } + + // Skip the augmentation length. + do { + if (!memory_.ReadBytes(&value, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } while (value & 0x80); + + for (size_t i = 1; i < aug_string.size(); i++) { + if (aug_string[i] == 'R') { + if (!memory_.ReadBytes(encoding, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + // Got the encoding, that's all we are looking for. + return true; + } else if (aug_string[i] == 'L') { + memory_.set_cur_offset(memory_.cur_offset() + 1); + } else if (aug_string[i] == 'P') { + uint8_t encoding; + if (!memory_.ReadBytes(&encoding, 1)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + uint64_t value; + if (!memory_.template ReadEncodedValue(encoding, &value)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + } + } + + // It should be impossible to get here. + abort(); +} + +template +bool DwarfDebugFrame::AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, + uint8_t encoding) { + if (segment_size != 0) { + memory_.set_cur_offset(memory_.cur_offset() + 1); + } + + uint64_t start; + if (!memory_.template ReadEncodedValue(encoding & 0xf, &start)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + uint64_t length; + if (!memory_.template ReadEncodedValue(encoding & 0xf, &length)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + if (length != 0) { + fdes_.emplace_back(entry_offset, start, length); + } + + return true; +} + +template +bool DwarfDebugFrame::CreateSortedFdeList() { + memory_.set_cur_offset(offset_); + + // Loop through all of the entries and read just enough to create + // a sorted list of pcs. + // This code assumes that first comes the cie, then the fdes that + // it applies to. + uint64_t cie_offset = 0; + uint8_t address_encoding; + uint8_t segment_size; + while (memory_.cur_offset() < end_offset_) { + uint64_t cur_entry_offset = memory_.cur_offset(); + + // Figure out the entry length and type. + uint32_t value32; + if (!memory_.ReadBytes(&value32, sizeof(value32))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + uint64_t next_entry_offset; + if (value32 == static_cast(-1)) { + uint64_t value64; + if (!memory_.ReadBytes(&value64, sizeof(value64))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + next_entry_offset = memory_.cur_offset() + value64; + + // Read the Cie Id of a Cie or the pointer of the Fde. + if (!memory_.ReadBytes(&value64, sizeof(value64))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + if (value64 == static_cast(-1)) { + // Cie 64 bit + address_encoding = DW_EH_PE_sdata8; + if (!GetCieInfo(&segment_size, &address_encoding)) { + return false; + } + cie_offset = cur_entry_offset; + } else { + if (offset_ + value64 != cie_offset) { + // This means that this Fde is not following the Cie. + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + + // Fde 64 bit + if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) { + return false; + } + } + } else { + next_entry_offset = memory_.cur_offset() + value32; + + // Read the Cie Id of a Cie or the pointer of the Fde. + if (!memory_.ReadBytes(&value32, sizeof(value32))) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + if (value32 == static_cast(-1)) { + // Cie 32 bit + address_encoding = DW_EH_PE_sdata4; + if (!GetCieInfo(&segment_size, &address_encoding)) { + return false; + } + cie_offset = cur_entry_offset; + } else { + if (offset_ + value32 != cie_offset) { + // This means that this Fde is not following the Cie. + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + + // Fde 32 bit + if (!AddFdeInfo(cur_entry_offset, segment_size, address_encoding)) { + return false; + } + } + } + + if (next_entry_offset < memory_.cur_offset()) { + // This indicates some kind of corruption, or malformed section data. + last_error_ = DWARF_ERROR_ILLEGAL_VALUE; + return false; + } + memory_.set_cur_offset(next_entry_offset); + } + + // Sort the entries. + std::sort(fdes_.begin(), fdes_.end(), [](const FdeInfo& a, const FdeInfo& b) { + if (a.start == b.start) return a.end < b.end; + return a.start < b.start; + }); + + fde_count_ = fdes_.size(); + + return true; +} + +template +bool DwarfDebugFrame::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) { + if (fde_count_ == 0) { + return false; + } + + size_t first = 0; + size_t last = fde_count_; + while (first < last) { + size_t current = (first + last) / 2; + const FdeInfo* info = &fdes_[current]; + if (pc >= info->start && pc <= info->end) { + *fde_offset = info->offset; + return true; + } + + if (pc < info->start) { + last = current; + } else { + first = current + 1; + } + } + return false; +} + +template +const DwarfFde* DwarfDebugFrame::GetFdeFromIndex(size_t index) { + if (index >= fdes_.size()) { + return nullptr; + } + return this->GetFdeFromOffset(fdes_[index].offset); +} + +// Explicitly instantiate DwarfDebugFrame. +template class DwarfDebugFrame; +template class DwarfDebugFrame; diff --git a/libunwindstack/DwarfDebugFrame.h b/libunwindstack/DwarfDebugFrame.h new file mode 100644 index 000000000..320368cac --- /dev/null +++ b/libunwindstack/DwarfDebugFrame.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#ifndef _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H +#define _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H + +#include + +#include + +#include "DwarfSection.h" + +template +class DwarfDebugFrame : public DwarfSectionImpl { + public: + // Add these so that the protected members of DwarfSectionImpl + // can be accessed without needing a this->. + using DwarfSectionImpl::memory_; + using DwarfSectionImpl::fde_count_; + using DwarfSectionImpl::last_error_; + + struct FdeInfo { + FdeInfo(uint64_t offset, uint64_t start, uint64_t length) + : offset(offset), start(start), end(start + length) {} + + uint64_t offset; + AddressType start; + AddressType end; + }; + + DwarfDebugFrame(Memory* memory) : DwarfSectionImpl(memory) {} + virtual ~DwarfDebugFrame() = default; + + bool Init(uint64_t offset, uint64_t size) override; + + bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override; + + const DwarfFde* GetFdeFromIndex(size_t index) override; + + bool IsCie32(uint32_t value32) override { return value32 == static_cast(-1); } + + bool IsCie64(uint64_t value64) override { return value64 == static_cast(-1); } + + uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { return offset_ + pointer; } + + uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { return offset_ + pointer; } + + uint64_t AdjustPcFromFde(uint64_t pc) override { return pc; } + + bool GetCieInfo(uint8_t* segment_size, uint8_t* encoding); + + bool AddFdeInfo(uint64_t entry_offset, uint8_t segment_size, uint8_t encoding); + + bool CreateSortedFdeList(); + + protected: + uint64_t offset_; + uint64_t end_offset_; + + std::vector fdes_; +}; + +#endif // _LIBUNWINDSTACK_DWARF_DEBUG_FRAME_H diff --git a/libunwindstack/DwarfEhFrame.cpp b/libunwindstack/DwarfEhFrame.cpp new file mode 100644 index 000000000..045fb363f --- /dev/null +++ b/libunwindstack/DwarfEhFrame.cpp @@ -0,0 +1,213 @@ +/* + * 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 + +#include "DwarfEhFrame.h" +#include "DwarfMemory.h" +#include "DwarfSection.h" +#include "DwarfStructs.h" +#include "Memory.h" + +template +bool DwarfEhFrame::Init(uint64_t offset, uint64_t size) { + uint8_t data[4]; + + memory_.clear_func_offset(); + memory_.clear_text_offset(); + memory_.set_data_offset(offset); + memory_.set_cur_offset(offset); + + // Read the first four bytes all at once. + if (!memory_.ReadBytes(data, 4)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + version_ = data[0]; + if (version_ != 1) { + // Unknown version. + last_error_ = DWARF_ERROR_UNSUPPORTED_VERSION; + return false; + } + + ptr_encoding_ = data[1]; + uint8_t fde_count_encoding = data[2]; + table_encoding_ = data[3]; + table_entry_size_ = memory_.template GetEncodedSize(table_encoding_); + + memory_.set_pc_offset(memory_.cur_offset()); + if (!memory_.template ReadEncodedValue(ptr_encoding_, &ptr_offset_)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + memory_.set_pc_offset(memory_.cur_offset()); + if (!memory_.template ReadEncodedValue(fde_count_encoding, &fde_count_)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + entries_offset_ = memory_.cur_offset(); + entries_end_ = offset + size; + entries_data_offset_ = offset; + cur_entries_offset_ = entries_offset_; + + return true; +} + +template +const DwarfFde* DwarfEhFrame::GetFdeFromIndex(size_t index) { + const FdeInfo* info = GetFdeInfoFromIndex(index); + if (info == nullptr) { + return nullptr; + } + return this->GetFdeFromOffset(info->offset); +} + +template +const typename DwarfEhFrame::FdeInfo* DwarfEhFrame::GetFdeInfoFromIndex( + size_t index) { + auto entry = fde_info_.find(index); + if (entry != fde_info_.end()) { + return &fde_info_[index]; + } + FdeInfo* info = &fde_info_[index]; + + memory_.set_data_offset(entries_data_offset_); + memory_.set_cur_offset(entries_offset_ + 2 * index * table_entry_size_); + memory_.set_pc_offset(memory_.cur_offset()); + uint64_t value; + if (!memory_.template ReadEncodedValue(table_encoding_, &value) || + !memory_.template ReadEncodedValue(table_encoding_, &info->offset)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + fde_info_.erase(index); + return nullptr; + } + info->pc = value; + return info; +} + +template +bool DwarfEhFrame::GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, + uint64_t total_entries) { + assert(fde_count_ > 0); + assert(total_entries <= fde_count_); + + size_t first = 0; + size_t last = total_entries; + while (first < last) { + size_t current = (first + last) / 2; + const FdeInfo* info = GetFdeInfoFromIndex(current); + if (pc == info->pc) { + *fde_offset = info->offset; + return true; + } + if (pc < info->pc) { + last = current; + } else { + first = current + 1; + } + } + if (last != 0) { + const FdeInfo* info = GetFdeInfoFromIndex(last - 1); + *fde_offset = info->offset; + return true; + } + return false; +} + +template +bool DwarfEhFrame::GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset) { + assert(fde_count_ != 0); + last_error_ = DWARF_ERROR_NONE; + // We can do a binary search if the pc is in the range of the elements + // that have already been cached. + if (!fde_info_.empty()) { + const FdeInfo* info = &fde_info_[fde_info_.size() - 1]; + if (pc >= info->pc) { + *fde_offset = info->offset; + return true; + } + if (pc < info->pc) { + return GetFdeOffsetBinary(pc, fde_offset, fde_info_.size()); + } + } + + if (cur_entries_offset_ == 0) { + // All entries read, or error encountered. + return false; + } + + memory_.set_data_offset(entries_data_offset_); + memory_.set_cur_offset(cur_entries_offset_); + cur_entries_offset_ = 0; + + FdeInfo* prev_info = nullptr; + for (size_t current = fde_info_.size(); + current < fde_count_ && memory_.cur_offset() < entries_end_; current++) { + memory_.set_pc_offset(memory_.cur_offset()); + uint64_t value; + if (!memory_.template ReadEncodedValue(table_encoding_, &value)) { + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + + FdeInfo* info = &fde_info_[current]; + if (!memory_.template ReadEncodedValue(table_encoding_, &info->offset)) { + fde_info_.erase(current); + last_error_ = DWARF_ERROR_MEMORY_INVALID; + return false; + } + info->pc = value; + + if (pc < info->pc) { + if (prev_info == nullptr) { + return false; + } + cur_entries_offset_ = memory_.cur_offset(); + *fde_offset = prev_info->offset; + return true; + } + prev_info = info; + } + + if (fde_count_ == fde_info_.size() && pc >= prev_info->pc) { + *fde_offset = prev_info->offset; + return true; + } + return false; +} + +template +bool DwarfEhFrame::GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) { + if (fde_count_ == 0) { + return false; + } + + if (table_entry_size_ > 0) { + // Do a binary search since the size of each table entry is fixed. + return GetFdeOffsetBinary(pc, fde_offset, fde_count_); + } else { + // Do a sequential search since each table entry size is variable. + return GetFdeOffsetSequential(pc, fde_offset); + } +} + +// Explicitly instantiate DwarfEhFrame. +template class DwarfEhFrame; +template class DwarfEhFrame; diff --git a/libunwindstack/DwarfEhFrame.h b/libunwindstack/DwarfEhFrame.h new file mode 100644 index 000000000..e6909edab --- /dev/null +++ b/libunwindstack/DwarfEhFrame.h @@ -0,0 +1,89 @@ +/* + * 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_EH_FRAME_H +#define _LIBUNWINDSTACK_DWARF_EH_FRAME_H + +#include + +#include "DwarfSection.h" + +// Forward declarations. +class Memory; + +template +class DwarfEhFrame : public DwarfSectionImpl { + public: + // Add these so that the protected members of DwarfSectionImpl + // can be accessed without needing a this->. + using DwarfSectionImpl::memory_; + using DwarfSectionImpl::fde_count_; + using DwarfSectionImpl::last_error_; + + struct FdeInfo { + AddressType pc; + uint64_t offset; + }; + + DwarfEhFrame(Memory* memory) : DwarfSectionImpl(memory) {} + virtual ~DwarfEhFrame() = default; + + bool Init(uint64_t offset, uint64_t size) override; + + bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) override; + + const DwarfFde* GetFdeFromIndex(size_t index) override; + + bool IsCie32(uint32_t value32) override { return value32 == 0; } + + bool IsCie64(uint64_t value64) override { return value64 == 0; } + + uint64_t GetCieOffsetFromFde32(uint32_t pointer) override { + return memory_.cur_offset() - pointer - 4; + } + + uint64_t GetCieOffsetFromFde64(uint64_t pointer) override { + return memory_.cur_offset() - pointer - 8; + } + + uint64_t AdjustPcFromFde(uint64_t pc) override { + // The eh_frame uses relative pcs. + return pc + memory_.cur_offset(); + } + + const FdeInfo* GetFdeInfoFromIndex(size_t index); + + bool GetFdeOffsetSequential(uint64_t pc, uint64_t* fde_offset); + + bool GetFdeOffsetBinary(uint64_t pc, uint64_t* fde_offset, uint64_t total_entries); + + protected: + uint8_t version_; + uint8_t ptr_encoding_; + uint8_t table_encoding_; + size_t table_entry_size_; + + uint64_t ptr_offset_; + + uint64_t entries_offset_; + uint64_t entries_end_; + uint64_t entries_data_offset_; + uint64_t cur_entries_offset_ = 0; + + std::unordered_map fde_info_; +}; + +#endif // _LIBUNWINDSTACK_DWARF_EH_FRAME_H diff --git a/libunwindstack/ElfInterface.cpp b/libunwindstack/ElfInterface.cpp index 087457c02..bfa794432 100644 --- a/libunwindstack/ElfInterface.cpp +++ b/libunwindstack/ElfInterface.cpp @@ -20,10 +20,33 @@ #include #include +#include "DwarfDebugFrame.h" +#include "DwarfEhFrame.h" #include "ElfInterface.h" #include "Memory.h" #include "Regs.h" +template +void ElfInterface::InitHeadersWithTemplate() { + if (eh_frame_offset_ != 0) { + eh_frame_.reset(new DwarfEhFrame(memory_)); + if (!eh_frame_->Init(eh_frame_offset_, eh_frame_size_)) { + eh_frame_.reset(nullptr); + eh_frame_offset_ = 0; + eh_frame_size_ = static_cast(-1); + } + } + + if (debug_frame_offset_ != 0) { + debug_frame_.reset(new DwarfDebugFrame(memory_)); + if (!debug_frame_->Init(debug_frame_offset_, debug_frame_size_)) { + debug_frame_.reset(nullptr); + debug_frame_offset_ = 0; + debug_frame_size_ = static_cast(-1); + } + } +} + template bool ElfInterface::ReadAllHeaders() { EhdrType ehdr; @@ -210,6 +233,9 @@ bool ElfInterface::Step(uint64_t, Regs*, Memory*) { } // Instantiate all of the needed template functions. +template void ElfInterface::InitHeadersWithTemplate(); +template void ElfInterface::InitHeadersWithTemplate(); + template bool ElfInterface::ReadAllHeaders(); template bool ElfInterface::ReadAllHeaders(); diff --git a/libunwindstack/ElfInterface.h b/libunwindstack/ElfInterface.h index 944146cdb..1cc8aa0ee 100644 --- a/libunwindstack/ElfInterface.h +++ b/libunwindstack/ElfInterface.h @@ -25,6 +25,8 @@ #include #include +#include "DwarfSection.h" + // Forward declarations. class Memory; class Regs; @@ -68,10 +70,18 @@ class ElfInterface { uint64_t dynamic_size() { return dynamic_size_; } uint64_t eh_frame_offset() { return eh_frame_offset_; } uint64_t eh_frame_size() { return eh_frame_size_; } + uint64_t debug_frame_offset() { return debug_frame_offset_; } + uint64_t debug_frame_size() { return debug_frame_size_; } uint64_t gnu_debugdata_offset() { return gnu_debugdata_offset_; } uint64_t gnu_debugdata_size() { return gnu_debugdata_size_; } + DwarfSection* eh_frame() { return eh_frame_.get(); } + DwarfSection* debug_frame() { return debug_frame_.get(); } + protected: + template + void InitHeadersWithTemplate(); + template bool ReadAllHeaders(); @@ -105,6 +115,9 @@ class ElfInterface { uint8_t soname_type_ = SONAME_UNKNOWN; std::string soname_; + + std::unique_ptr eh_frame_; + std::unique_ptr debug_frame_; }; class ElfInterface32 : public ElfInterface { @@ -116,8 +129,7 @@ class ElfInterface32 : public ElfInterface { return ElfInterface::ReadAllHeaders(); } - void InitHeaders() override { - } + void InitHeaders() override { ElfInterface::InitHeadersWithTemplate(); } bool GetSoname(std::string* soname) override { return ElfInterface::GetSonameWithTemplate(soname); @@ -137,8 +149,7 @@ class ElfInterface64 : public ElfInterface { return ElfInterface::ReadAllHeaders(); } - void InitHeaders() override { - } + void InitHeaders() override { ElfInterface::InitHeadersWithTemplate(); } bool GetSoname(std::string* soname) override { return ElfInterface::GetSonameWithTemplate(soname); diff --git a/libunwindstack/tests/DwarfDebugFrameTest.cpp b/libunwindstack/tests/DwarfDebugFrameTest.cpp new file mode 100644 index 000000000..94a997451 --- /dev/null +++ b/libunwindstack/tests/DwarfDebugFrameTest.cpp @@ -0,0 +1,456 @@ +/* + * 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 "DwarfDebugFrame.h" +#include "DwarfEncoding.h" + +#include "LogFake.h" +#include "MemoryFake.h" +#include "RegsFake.h" + +template +class MockDwarfDebugFrame : public DwarfDebugFrame { + public: + MockDwarfDebugFrame(Memory* memory) : DwarfDebugFrame(memory) {} + ~MockDwarfDebugFrame() = default; + + void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; } + void TestSetOffset(uint64_t offset) { this->offset_ = offset; } + void TestSetEndOffset(uint64_t offset) { this->end_offset_ = offset; } + void TestPushFdeInfo(const typename DwarfDebugFrame::FdeInfo& info) { + this->fdes_.push_back(info); + } + + uint64_t TestGetFdeCount() { return this->fde_count_; } + uint8_t TestGetOffset() { return this->offset_; } + uint8_t TestGetEndOffset() { return this->end_offset_; } + void TestGetFdeInfo(size_t index, typename DwarfDebugFrame::FdeInfo* info) { + *info = this->fdes_[index]; + } +}; + +template +class DwarfDebugFrameTest : public ::testing::Test { + protected: + void SetUp() override { + memory_.Clear(); + debug_frame_ = new MockDwarfDebugFrame(&memory_); + ResetLogs(); + } + + void TearDown() override { delete debug_frame_; } + + MemoryFake memory_; + MockDwarfDebugFrame* debug_frame_ = nullptr; +}; +TYPED_TEST_CASE_P(DwarfDebugFrameTest); + +// NOTE: All test class variables need to be referenced as this->. + +TYPED_TEST_P(DwarfDebugFrameTest, Init32) { + // CIE 32 information. + this->memory_.SetData32(0x5000, 0xfc); + this->memory_.SetData32(0x5004, 0xffffffff); + this->memory_.SetData8(0x5008, 1); + this->memory_.SetData8(0x5009, '\0'); + + // FDE 32 information. + this->memory_.SetData32(0x5100, 0xfc); + this->memory_.SetData32(0x5104, 0); + this->memory_.SetData32(0x5108, 0x1500); + this->memory_.SetData32(0x510c, 0x200); + + this->memory_.SetData32(0x5200, 0xfc); + this->memory_.SetData32(0x5204, 0); + this->memory_.SetData32(0x5208, 0x2500); + this->memory_.SetData32(0x520c, 0x300); + + // CIE 32 information. + this->memory_.SetData32(0x5300, 0xfc); + this->memory_.SetData32(0x5304, 0xffffffff); + this->memory_.SetData8(0x5308, 1); + this->memory_.SetData8(0x5309, '\0'); + + // FDE 32 information. + this->memory_.SetData32(0x5400, 0xfc); + this->memory_.SetData32(0x5404, 0x300); + this->memory_.SetData32(0x5408, 0x3500); + this->memory_.SetData32(0x540c, 0x400); + + this->memory_.SetData32(0x5500, 0xfc); + this->memory_.SetData32(0x5504, 0x300); + this->memory_.SetData32(0x5508, 0x4500); + this->memory_.SetData32(0x550c, 0x500); + + ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600)); + ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount()); + + typename DwarfDebugFrame::FdeInfo info(0, 0, 0); + + this->debug_frame_->TestGetFdeInfo(0, &info); + EXPECT_EQ(0x5100U, info.offset); + EXPECT_EQ(0x1500U, info.start); + EXPECT_EQ(0x1700U, info.end); + + this->debug_frame_->TestGetFdeInfo(1, &info); + EXPECT_EQ(0x5200U, info.offset); + EXPECT_EQ(0x2500U, info.start); + EXPECT_EQ(0x2800U, info.end); + + this->debug_frame_->TestGetFdeInfo(2, &info); + EXPECT_EQ(0x5400U, info.offset); + EXPECT_EQ(0x3500U, info.start); + EXPECT_EQ(0x3900U, info.end); + + this->debug_frame_->TestGetFdeInfo(3, &info); + EXPECT_EQ(0x5500U, info.offset); + EXPECT_EQ(0x4500U, info.start); + EXPECT_EQ(0x4a00U, info.end); +} + +TYPED_TEST_P(DwarfDebugFrameTest, Init32_fde_not_following_cie) { + // CIE 32 information. + this->memory_.SetData32(0x5000, 0xfc); + this->memory_.SetData32(0x5004, 0xffffffff); + this->memory_.SetData8(0x5008, 1); + this->memory_.SetData8(0x5009, '\0'); + + // FDE 32 information. + this->memory_.SetData32(0x5100, 0xfc); + this->memory_.SetData32(0x5104, 0x1000); + this->memory_.SetData32(0x5108, 0x1500); + this->memory_.SetData32(0x510c, 0x200); + + ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error()); +} + +TYPED_TEST_P(DwarfDebugFrameTest, Init64) { + // CIE 64 information. + this->memory_.SetData32(0x5000, 0xffffffff); + this->memory_.SetData64(0x5004, 0xf4); + this->memory_.SetData64(0x500c, 0xffffffffffffffffULL); + this->memory_.SetData8(0x5014, 1); + this->memory_.SetData8(0x5015, '\0'); + + // FDE 64 information. + this->memory_.SetData32(0x5100, 0xffffffff); + this->memory_.SetData64(0x5104, 0xf4); + this->memory_.SetData64(0x510c, 0); + this->memory_.SetData64(0x5114, 0x1500); + this->memory_.SetData64(0x511c, 0x200); + + this->memory_.SetData32(0x5200, 0xffffffff); + this->memory_.SetData64(0x5204, 0xf4); + this->memory_.SetData64(0x520c, 0); + this->memory_.SetData64(0x5214, 0x2500); + this->memory_.SetData64(0x521c, 0x300); + + // CIE 64 information. + this->memory_.SetData32(0x5300, 0xffffffff); + this->memory_.SetData64(0x5304, 0xf4); + this->memory_.SetData64(0x530c, 0xffffffffffffffffULL); + this->memory_.SetData8(0x5314, 1); + this->memory_.SetData8(0x5315, '\0'); + + // FDE 64 information. + this->memory_.SetData32(0x5400, 0xffffffff); + this->memory_.SetData64(0x5404, 0xf4); + this->memory_.SetData64(0x540c, 0x300); + this->memory_.SetData64(0x5414, 0x3500); + this->memory_.SetData64(0x541c, 0x400); + + this->memory_.SetData32(0x5500, 0xffffffff); + this->memory_.SetData64(0x5504, 0xf4); + this->memory_.SetData64(0x550c, 0x300); + this->memory_.SetData64(0x5514, 0x4500); + this->memory_.SetData64(0x551c, 0x500); + + ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x600)); + ASSERT_EQ(4U, this->debug_frame_->TestGetFdeCount()); + + typename DwarfDebugFrame::FdeInfo info(0, 0, 0); + + this->debug_frame_->TestGetFdeInfo(0, &info); + EXPECT_EQ(0x5100U, info.offset); + EXPECT_EQ(0x1500U, info.start); + EXPECT_EQ(0x1700U, info.end); + + this->debug_frame_->TestGetFdeInfo(1, &info); + EXPECT_EQ(0x5200U, info.offset); + EXPECT_EQ(0x2500U, info.start); + EXPECT_EQ(0x2800U, info.end); + + this->debug_frame_->TestGetFdeInfo(2, &info); + EXPECT_EQ(0x5400U, info.offset); + EXPECT_EQ(0x3500U, info.start); + EXPECT_EQ(0x3900U, info.end); + + this->debug_frame_->TestGetFdeInfo(3, &info); + EXPECT_EQ(0x5500U, info.offset); + EXPECT_EQ(0x4500U, info.start); + EXPECT_EQ(0x4a00U, info.end); +} + +TYPED_TEST_P(DwarfDebugFrameTest, Init64_fde_not_following_cie) { + // CIE 64 information. + this->memory_.SetData32(0x5000, 0xffffffff); + this->memory_.SetData64(0x5004, 0xf4); + this->memory_.SetData64(0x500c, 0xffffffffffffffffULL); + this->memory_.SetData8(0x5014, 1); + this->memory_.SetData8(0x5015, '\0'); + + // FDE 64 information. + this->memory_.SetData32(0x5100, 0xffffffff); + this->memory_.SetData64(0x5104, 0xf4); + this->memory_.SetData64(0x510c, 0x1000); + this->memory_.SetData64(0x5114, 0x1500); + this->memory_.SetData64(0x511c, 0x200); + + ASSERT_FALSE(this->debug_frame_->Init(0x5000, 0x600)); + ASSERT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->debug_frame_->last_error()); +} + +TYPED_TEST_P(DwarfDebugFrameTest, Init_version1) { + // CIE 32 information. + this->memory_.SetData32(0x5000, 0xfc); + this->memory_.SetData32(0x5004, 0xffffffff); + this->memory_.SetData8(0x5008, 1); + // Augment string. + this->memory_.SetMemory(0x5009, std::vector{'z', 'R', 'P', 'L', '\0'}); + // Code alignment factor. + this->memory_.SetMemory(0x500e, std::vector{0x80, 0x00}); + // Data alignment factor. + this->memory_.SetMemory(0x5010, std::vector{0x81, 0x80, 0x80, 0x00}); + // Return address register + this->memory_.SetData8(0x5014, 0x84); + // Augmentation length + this->memory_.SetMemory(0x5015, std::vector{0x84, 0x00}); + // R data. + this->memory_.SetData8(0x5017, DW_EH_PE_pcrel | DW_EH_PE_udata2); + + // FDE 32 information. + this->memory_.SetData32(0x5100, 0xfc); + this->memory_.SetData32(0x5104, 0); + this->memory_.SetData16(0x5108, 0x1500); + this->memory_.SetData16(0x510a, 0x200); + + ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200)); + ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount()); + + typename DwarfDebugFrame::FdeInfo info(0, 0, 0); + this->debug_frame_->TestGetFdeInfo(0, &info); + EXPECT_EQ(0x5100U, info.offset); + EXPECT_EQ(0x1500U, info.start); + EXPECT_EQ(0x1700U, info.end); +} + +TYPED_TEST_P(DwarfDebugFrameTest, Init_version4) { + // CIE 32 information. + this->memory_.SetData32(0x5000, 0xfc); + this->memory_.SetData32(0x5004, 0xffffffff); + this->memory_.SetData8(0x5008, 4); + // Augment string. + this->memory_.SetMemory(0x5009, std::vector{'z', 'L', 'P', 'R', '\0'}); + // Address size. + this->memory_.SetData8(0x500e, 4); + // Segment size. + this->memory_.SetData8(0x500f, 0); + // Code alignment factor. + this->memory_.SetMemory(0x5010, std::vector{0x80, 0x00}); + // Data alignment factor. + this->memory_.SetMemory(0x5012, std::vector{0x81, 0x80, 0x80, 0x00}); + // Return address register + this->memory_.SetMemory(0x5016, std::vector{0x85, 0x10}); + // Augmentation length + this->memory_.SetMemory(0x5018, std::vector{0x84, 0x00}); + // L data. + this->memory_.SetData8(0x501a, 0x10); + // P data. + this->memory_.SetData8(0x501b, DW_EH_PE_udata4); + this->memory_.SetData32(0x501c, 0x100); + // R data. + this->memory_.SetData8(0x5020, DW_EH_PE_pcrel | DW_EH_PE_udata2); + + // FDE 32 information. + this->memory_.SetData32(0x5100, 0xfc); + this->memory_.SetData32(0x5104, 0); + this->memory_.SetData16(0x5108, 0x1500); + this->memory_.SetData16(0x510a, 0x200); + + ASSERT_TRUE(this->debug_frame_->Init(0x5000, 0x200)); + ASSERT_EQ(1U, this->debug_frame_->TestGetFdeCount()); + + typename DwarfDebugFrame::FdeInfo info(0, 0, 0); + this->debug_frame_->TestGetFdeInfo(0, &info); + EXPECT_EQ(0x5100U, info.offset); + EXPECT_EQ(0x1500U, info.start); + EXPECT_EQ(0x1700U, info.end); +} + +TYPED_TEST_P(DwarfDebugFrameTest, GetFdeOffsetFromPc) { + typename DwarfDebugFrame::FdeInfo info(0, 0, 0); + for (size_t i = 0; i < 9; i++) { + info.start = 0x1000 * (i + 1); + info.end = 0x1000 * (i + 2) - 0x10; + info.offset = 0x5000 + i * 0x20; + this->debug_frame_->TestPushFdeInfo(info); + } + + this->debug_frame_->TestSetFdeCount(0); + uint64_t fde_offset; + ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x1000, &fde_offset)); + ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error()); + + this->debug_frame_->TestSetFdeCount(9); + ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(0x100, &fde_offset)); + ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error()); + // Odd number of elements. + for (size_t i = 0; i < 9; i++) { + TypeParam pc = 0x1000 * (i + 1); + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " + << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset)) + << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset)) + << "Failed at index " << i; + ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error()); + } + + // Even number of elements. + this->debug_frame_->TestSetFdeCount(10); + info.start = 0xa000; + info.end = 0xaff0; + info.offset = 0x5120; + this->debug_frame_->TestPushFdeInfo(info); + + for (size_t i = 0; i < 10; i++) { + TypeParam pc = 0x1000 * (i + 1); + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc, &fde_offset)) << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 1, &fde_offset)) << "Failed at index " + << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_TRUE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xeff, &fde_offset)) + << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + ASSERT_FALSE(this->debug_frame_->GetFdeOffsetFromPc(pc + 0xfff, &fde_offset)) + << "Failed at index " << i; + ASSERT_EQ(DWARF_ERROR_NONE, this->debug_frame_->last_error()); + } +} + +TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde32) { + this->debug_frame_->TestSetOffset(0x4000); + + // CIE 32 information. + this->memory_.SetData32(0xf000, 0x100); + this->memory_.SetData32(0xf004, 0xffffffff); + this->memory_.SetData8(0xf008, 0x1); + this->memory_.SetData8(0xf009, '\0'); + this->memory_.SetData8(0xf00a, 4); + this->memory_.SetData8(0xf00b, 8); + this->memory_.SetData8(0xf00c, 0x20); + + // FDE 32 information. + this->memory_.SetData32(0x14000, 0x20); + this->memory_.SetData32(0x14004, 0xb000); + this->memory_.SetData32(0x14008, 0x9000); + this->memory_.SetData32(0x1400c, 0x100); + + const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x14000); + ASSERT_TRUE(fde != nullptr); + EXPECT_EQ(0x14010U, fde->cfa_instructions_offset); + EXPECT_EQ(0x14024U, fde->cfa_instructions_end); + EXPECT_EQ(0x9000U, fde->pc_start); + EXPECT_EQ(0x9100U, fde->pc_end); + EXPECT_EQ(0xf000U, fde->cie_offset); + EXPECT_EQ(0U, fde->lsda_address); + + ASSERT_TRUE(fde->cie != nullptr); + EXPECT_EQ(1U, fde->cie->version); + EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding); + EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding); + EXPECT_EQ(0U, fde->cie->segment_size); + EXPECT_EQ(1U, fde->cie->augmentation_string.size()); + EXPECT_EQ('\0', fde->cie->augmentation_string[0]); + EXPECT_EQ(0U, fde->cie->personality_handler); + EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset); + EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end); + EXPECT_EQ(4U, fde->cie->code_alignment_factor); + EXPECT_EQ(8, fde->cie->data_alignment_factor); + EXPECT_EQ(0x20U, fde->cie->return_address_register); +} + +TYPED_TEST_P(DwarfDebugFrameTest, GetCieFde64) { + this->debug_frame_->TestSetOffset(0x2000); + + // CIE 64 information. + this->memory_.SetData32(0x6000, 0xffffffff); + this->memory_.SetData64(0x6004, 0x100); + this->memory_.SetData64(0x600c, 0xffffffffffffffffULL); + this->memory_.SetData8(0x6014, 0x1); + this->memory_.SetData8(0x6015, '\0'); + this->memory_.SetData8(0x6016, 4); + this->memory_.SetData8(0x6017, 8); + this->memory_.SetData8(0x6018, 0x20); + + // FDE 64 information. + this->memory_.SetData32(0x8000, 0xffffffff); + this->memory_.SetData64(0x8004, 0x200); + this->memory_.SetData64(0x800c, 0x4000); + this->memory_.SetData64(0x8014, 0x5000); + this->memory_.SetData64(0x801c, 0x300); + + const DwarfFde* fde = this->debug_frame_->GetFdeFromOffset(0x8000); + ASSERT_TRUE(fde != nullptr); + EXPECT_EQ(0x8024U, fde->cfa_instructions_offset); + EXPECT_EQ(0x820cU, fde->cfa_instructions_end); + EXPECT_EQ(0x5000U, fde->pc_start); + EXPECT_EQ(0x5300U, fde->pc_end); + EXPECT_EQ(0x6000U, fde->cie_offset); + EXPECT_EQ(0U, fde->lsda_address); + + ASSERT_TRUE(fde->cie != nullptr); + EXPECT_EQ(1U, fde->cie->version); + EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding); + EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding); + EXPECT_EQ(0U, fde->cie->segment_size); + EXPECT_EQ(1U, fde->cie->augmentation_string.size()); + EXPECT_EQ('\0', fde->cie->augmentation_string[0]); + EXPECT_EQ(0U, fde->cie->personality_handler); + EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset); + EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end); + EXPECT_EQ(4U, fde->cie->code_alignment_factor); + EXPECT_EQ(8, fde->cie->data_alignment_factor); + EXPECT_EQ(0x20U, fde->cie->return_address_register); +} + +REGISTER_TYPED_TEST_CASE_P(DwarfDebugFrameTest, Init32, Init32_fde_not_following_cie, Init64, + Init64_fde_not_following_cie, Init_version1, Init_version4, + GetFdeOffsetFromPc, GetCieFde32, GetCieFde64); + +typedef ::testing::Types DwarfDebugFrameTestTypes; +INSTANTIATE_TYPED_TEST_CASE_P(, DwarfDebugFrameTest, DwarfDebugFrameTestTypes); diff --git a/libunwindstack/tests/DwarfEhFrameTest.cpp b/libunwindstack/tests/DwarfEhFrameTest.cpp new file mode 100644 index 000000000..4e538d4ec --- /dev/null +++ b/libunwindstack/tests/DwarfEhFrameTest.cpp @@ -0,0 +1,409 @@ +/* + * 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 "DwarfEhFrame.h" +#include "DwarfEncoding.h" + +#include "LogFake.h" +#include "MemoryFake.h" +#include "RegsFake.h" + +template +class MockDwarfEhFrame : public DwarfEhFrame { + public: + MockDwarfEhFrame(Memory* memory) : DwarfEhFrame(memory) {} + ~MockDwarfEhFrame() = default; + + void TestSetTableEncoding(uint8_t encoding) { this->table_encoding_ = encoding; } + void TestSetEntriesOffset(uint64_t offset) { this->entries_offset_ = offset; } + void TestSetEntriesEnd(uint64_t end) { this->entries_end_ = end; } + void TestSetEntriesDataOffset(uint64_t offset) { this->entries_data_offset_ = offset; } + void TestSetCurEntriesOffset(uint64_t offset) { this->cur_entries_offset_ = offset; } + void TestSetTableEntrySize(size_t size) { this->table_entry_size_ = size; } + + void TestSetFdeCount(uint64_t count) { this->fde_count_ = count; } + void TestSetFdeInfo(uint64_t index, const typename DwarfEhFrame::FdeInfo& info) { + this->fde_info_[index] = info; + } + + uint8_t TestGetVersion() { return this->version_; } + uint8_t TestGetPtrEncoding() { return this->ptr_encoding_; } + uint64_t TestGetPtrOffset() { return this->ptr_offset_; } + uint8_t TestGetTableEncoding() { return this->table_encoding_; } + uint64_t TestGetTableEntrySize() { return this->table_entry_size_; } + uint64_t TestGetFdeCount() { return this->fde_count_; } + uint64_t TestGetEntriesOffset() { return this->entries_offset_; } + uint64_t TestGetEntriesEnd() { return this->entries_end_; } + uint64_t TestGetEntriesDataOffset() { return this->entries_data_offset_; } + uint64_t TestGetCurEntriesOffset() { return this->cur_entries_offset_; } +}; + +template +class DwarfEhFrameTest : public ::testing::Test { + protected: + void SetUp() override { + memory_.Clear(); + eh_frame_ = new MockDwarfEhFrame(&memory_); + ResetLogs(); + } + + void TearDown() override { delete eh_frame_; } + + MemoryFake memory_; + MockDwarfEhFrame* eh_frame_ = nullptr; +}; +TYPED_TEST_CASE_P(DwarfEhFrameTest); + +// NOTE: All test class variables need to be referenced as this->. + +TYPED_TEST_P(DwarfEhFrameTest, Init) { + this->memory_.SetMemory( + 0x1000, std::vector{0x1, DW_EH_PE_udata2, DW_EH_PE_udata4, DW_EH_PE_sdata4}); + this->memory_.SetData16(0x1004, 0x500); + this->memory_.SetData32(0x1006, 126); + + ASSERT_TRUE(this->eh_frame_->Init(0x1000, 0x100)); + EXPECT_EQ(1U, this->eh_frame_->TestGetVersion()); + EXPECT_EQ(DW_EH_PE_udata2, this->eh_frame_->TestGetPtrEncoding()); + EXPECT_EQ(DW_EH_PE_sdata4, this->eh_frame_->TestGetTableEncoding()); + EXPECT_EQ(4U, this->eh_frame_->TestGetTableEntrySize()); + EXPECT_EQ(126U, this->eh_frame_->TestGetFdeCount()); + EXPECT_EQ(0x500U, this->eh_frame_->TestGetPtrOffset()); + EXPECT_EQ(0x100aU, this->eh_frame_->TestGetEntriesOffset()); + EXPECT_EQ(0x1100U, this->eh_frame_->TestGetEntriesEnd()); + EXPECT_EQ(0x1000U, this->eh_frame_->TestGetEntriesDataOffset()); + EXPECT_EQ(0x100aU, this->eh_frame_->TestGetCurEntriesOffset()); + + // Verify an unexpected version will cause a fail. + this->memory_.SetData8(0x1000, 0); + ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100)); + ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error()); + this->memory_.SetData8(0x1000, 2); + ASSERT_FALSE(this->eh_frame_->Init(0x1000, 0x100)); + ASSERT_EQ(DWARF_ERROR_UNSUPPORTED_VERSION, this->eh_frame_->last_error()); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_expect_cache_fail) { + this->eh_frame_->TestSetTableEntrySize(0x10); + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4); + ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr); + ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error()); + ASSERT_TRUE(this->eh_frame_->GetFdeInfoFromIndex(0) == nullptr); + ASSERT_EQ(DWARF_ERROR_MEMORY_INVALID, this->eh_frame_->last_error()); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_pcrel) { + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_pcrel | DW_EH_PE_udata4); + this->eh_frame_->TestSetEntriesOffset(0x1000); + this->eh_frame_->TestSetEntriesDataOffset(0x3000); + this->eh_frame_->TestSetTableEntrySize(0x10); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + auto info = this->eh_frame_->GetFdeInfoFromIndex(2); + ASSERT_TRUE(info != nullptr); + EXPECT_EQ(0x1380U, info->pc); + EXPECT_EQ(0x1540U, info->offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_read_datarel) { + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_datarel | DW_EH_PE_udata4); + this->eh_frame_->TestSetEntriesOffset(0x1000); + this->eh_frame_->TestSetEntriesDataOffset(0x3000); + this->eh_frame_->TestSetTableEntrySize(0x10); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + auto info = this->eh_frame_->GetFdeInfoFromIndex(2); + ASSERT_TRUE(info != nullptr); + EXPECT_EQ(0x3340U, info->pc); + EXPECT_EQ(0x3500U, info->offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeInfoFromIndex_cached) { + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4); + this->eh_frame_->TestSetEntriesOffset(0x1000); + this->eh_frame_->TestSetTableEntrySize(0x10); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + auto info = this->eh_frame_->GetFdeInfoFromIndex(2); + ASSERT_TRUE(info != nullptr); + EXPECT_EQ(0x340U, info->pc); + EXPECT_EQ(0x500U, info->offset); + + // Clear the memory so that this will fail if it doesn't read cached data. + this->memory_.Clear(); + + info = this->eh_frame_->GetFdeInfoFromIndex(2); + ASSERT_TRUE(info != nullptr); + EXPECT_EQ(0x340U, info->pc); + EXPECT_EQ(0x500U, info->offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetBinary_verify) { + this->eh_frame_->TestSetTableEntrySize(0x10); + this->eh_frame_->TestSetFdeCount(10); + + typename DwarfEhFrame::FdeInfo info; + for (size_t i = 0; i < 10; i++) { + info.pc = 0x1000 * (i + 1); + info.offset = 0x5000 + i * 0x20; + this->eh_frame_->TestSetFdeInfo(i, info); + } + + uint64_t fde_offset; + EXPECT_FALSE(this->eh_frame_->GetFdeOffsetBinary(0x100, &fde_offset, 10)); + // Not an error, just not found. + ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error()); + // Even number of elements. + for (size_t i = 0; i < 10; i++) { + TypeParam pc = 0x1000 * (i + 1); + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 10)) << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 10)) << "Failed at index " + << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 10)) + << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + } + // Odd number of elements. + for (size_t i = 0; i < 9; i++) { + TypeParam pc = 0x1000 * (i + 1); + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc, &fde_offset, 9)) << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 1, &fde_offset, 9)) << "Failed at index " + << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + EXPECT_TRUE(this->eh_frame_->GetFdeOffsetBinary(pc + 0xfff, &fde_offset, 9)) + << "Failed at index " << i; + EXPECT_EQ(0x5000 + i * 0x20, fde_offset) << "Failed at index " << i; + } +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential) { + this->eh_frame_->TestSetFdeCount(10); + this->eh_frame_->TestSetEntriesDataOffset(0x100); + this->eh_frame_->TestSetEntriesEnd(0x2000); + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + this->memory_.SetData32(0x1048, 0x440); + this->memory_.SetData32(0x104c, 0x600); + + // Verify that if entries is zero, that it fails. + uint64_t fde_offset; + ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset)); + this->eh_frame_->TestSetCurEntriesOffset(0x1040); + + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x340, &fde_offset)); + EXPECT_EQ(0x500U, fde_offset); + + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset)); + EXPECT_EQ(0x600U, fde_offset); + + // Expect that the data is cached so no more memory reads will occur. + this->memory_.Clear(); + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x440, &fde_offset)); + EXPECT_EQ(0x600U, fde_offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_last_element) { + this->eh_frame_->TestSetFdeCount(2); + this->eh_frame_->TestSetEntriesDataOffset(0x100); + this->eh_frame_->TestSetEntriesEnd(0x2000); + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4); + this->eh_frame_->TestSetCurEntriesOffset(0x1040); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + this->memory_.SetData32(0x1048, 0x440); + this->memory_.SetData32(0x104c, 0x600); + + uint64_t fde_offset; + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset)); + EXPECT_EQ(0x600U, fde_offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetSequential_end_check) { + this->eh_frame_->TestSetFdeCount(2); + this->eh_frame_->TestSetEntriesDataOffset(0x100); + this->eh_frame_->TestSetEntriesEnd(0x1048); + this->eh_frame_->TestSetTableEncoding(DW_EH_PE_udata4); + + this->memory_.SetData32(0x1040, 0x340); + this->memory_.SetData32(0x1044, 0x500); + + this->memory_.SetData32(0x1048, 0x440); + this->memory_.SetData32(0x104c, 0x600); + + uint64_t fde_offset; + ASSERT_FALSE(this->eh_frame_->GetFdeOffsetSequential(0x540, &fde_offset)); + ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error()); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_fail_fde_count) { + this->eh_frame_->TestSetFdeCount(0); + + uint64_t fde_offset; + ASSERT_FALSE(this->eh_frame_->GetFdeOffsetFromPc(0x100, &fde_offset)); + ASSERT_EQ(DWARF_ERROR_NONE, this->eh_frame_->last_error()); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_binary_search) { + this->eh_frame_->TestSetTableEntrySize(16); + this->eh_frame_->TestSetFdeCount(10); + + typename DwarfEhFrame::FdeInfo info; + info.pc = 0x550; + info.offset = 0x10500; + this->eh_frame_->TestSetFdeInfo(5, info); + info.pc = 0x750; + info.offset = 0x10700; + this->eh_frame_->TestSetFdeInfo(7, info); + info.pc = 0x850; + info.offset = 0x10800; + this->eh_frame_->TestSetFdeInfo(8, info); + + uint64_t fde_offset; + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x800, &fde_offset)); + EXPECT_EQ(0x10700U, fde_offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetFdeOffsetFromPc_sequential_search) { + this->eh_frame_->TestSetFdeCount(10); + this->eh_frame_->TestSetTableEntrySize(0); + + typename DwarfEhFrame::FdeInfo info; + info.pc = 0x50; + info.offset = 0x10000; + this->eh_frame_->TestSetFdeInfo(0, info); + info.pc = 0x150; + info.offset = 0x10100; + this->eh_frame_->TestSetFdeInfo(1, info); + info.pc = 0x250; + info.offset = 0x10200; + this->eh_frame_->TestSetFdeInfo(2, info); + + uint64_t fde_offset; + ASSERT_TRUE(this->eh_frame_->GetFdeOffsetFromPc(0x200, &fde_offset)); + EXPECT_EQ(0x10100U, fde_offset); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetCieFde32) { + // CIE 32 information. + this->memory_.SetData32(0xf000, 0x100); + this->memory_.SetData32(0xf004, 0); + this->memory_.SetData8(0xf008, 0x1); + this->memory_.SetData8(0xf009, '\0'); + this->memory_.SetData8(0xf00a, 4); + this->memory_.SetData8(0xf00b, 8); + this->memory_.SetData8(0xf00c, 0x20); + + // FDE 32 information. + this->memory_.SetData32(0x14000, 0x20); + this->memory_.SetData32(0x14004, 0x5004); + this->memory_.SetData32(0x14008, 0x9000); + this->memory_.SetData32(0x1400c, 0x100); + + const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x14000); + ASSERT_TRUE(fde != nullptr); + EXPECT_EQ(0x14010U, fde->cfa_instructions_offset); + EXPECT_EQ(0x14024U, fde->cfa_instructions_end); + EXPECT_EQ(0x1d00cU, fde->pc_start); + EXPECT_EQ(0x1d10cU, fde->pc_end); + EXPECT_EQ(0xf000U, fde->cie_offset); + EXPECT_EQ(0U, fde->lsda_address); + + ASSERT_TRUE(fde->cie != nullptr); + EXPECT_EQ(1U, fde->cie->version); + EXPECT_EQ(DW_EH_PE_sdata4, fde->cie->fde_address_encoding); + EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding); + EXPECT_EQ(0U, fde->cie->segment_size); + EXPECT_EQ(1U, fde->cie->augmentation_string.size()); + EXPECT_EQ('\0', fde->cie->augmentation_string[0]); + EXPECT_EQ(0U, fde->cie->personality_handler); + EXPECT_EQ(0xf00dU, fde->cie->cfa_instructions_offset); + EXPECT_EQ(0xf104U, fde->cie->cfa_instructions_end); + EXPECT_EQ(4U, fde->cie->code_alignment_factor); + EXPECT_EQ(8, fde->cie->data_alignment_factor); + EXPECT_EQ(0x20U, fde->cie->return_address_register); +} + +TYPED_TEST_P(DwarfEhFrameTest, GetCieFde64) { + // CIE 64 information. + this->memory_.SetData32(0x6000, 0xffffffff); + this->memory_.SetData64(0x6004, 0x100); + this->memory_.SetData64(0x600c, 0); + this->memory_.SetData8(0x6014, 0x1); + this->memory_.SetData8(0x6015, '\0'); + this->memory_.SetData8(0x6016, 4); + this->memory_.SetData8(0x6017, 8); + this->memory_.SetData8(0x6018, 0x20); + + // FDE 64 information. + this->memory_.SetData32(0x8000, 0xffffffff); + this->memory_.SetData64(0x8004, 0x200); + this->memory_.SetData64(0x800c, 0x200c); + this->memory_.SetData64(0x8014, 0x5000); + this->memory_.SetData64(0x801c, 0x300); + + const DwarfFde* fde = this->eh_frame_->GetFdeFromOffset(0x8000); + ASSERT_TRUE(fde != nullptr); + EXPECT_EQ(0x8024U, fde->cfa_instructions_offset); + EXPECT_EQ(0x820cU, fde->cfa_instructions_end); + EXPECT_EQ(0xd01cU, fde->pc_start); + EXPECT_EQ(0xd31cU, fde->pc_end); + EXPECT_EQ(0x6000U, fde->cie_offset); + EXPECT_EQ(0U, fde->lsda_address); + + ASSERT_TRUE(fde->cie != nullptr); + EXPECT_EQ(1U, fde->cie->version); + EXPECT_EQ(DW_EH_PE_sdata8, fde->cie->fde_address_encoding); + EXPECT_EQ(DW_EH_PE_omit, fde->cie->lsda_encoding); + EXPECT_EQ(0U, fde->cie->segment_size); + EXPECT_EQ(1U, fde->cie->augmentation_string.size()); + EXPECT_EQ('\0', fde->cie->augmentation_string[0]); + EXPECT_EQ(0U, fde->cie->personality_handler); + EXPECT_EQ(0x6019U, fde->cie->cfa_instructions_offset); + EXPECT_EQ(0x610cU, fde->cie->cfa_instructions_end); + EXPECT_EQ(4U, fde->cie->code_alignment_factor); + EXPECT_EQ(8, fde->cie->data_alignment_factor); + EXPECT_EQ(0x20U, fde->cie->return_address_register); +} + +REGISTER_TYPED_TEST_CASE_P(DwarfEhFrameTest, Init, GetFdeInfoFromIndex_expect_cache_fail, + GetFdeInfoFromIndex_read_pcrel, GetFdeInfoFromIndex_read_datarel, + GetFdeInfoFromIndex_cached, GetFdeOffsetBinary_verify, + GetFdeOffsetSequential, GetFdeOffsetSequential_last_element, + GetFdeOffsetSequential_end_check, GetFdeOffsetFromPc_fail_fde_count, + GetFdeOffsetFromPc_binary_search, GetFdeOffsetFromPc_sequential_search, + GetCieFde32, GetCieFde64); + +typedef ::testing::Types DwarfEhFrameTestTypes; +INSTANTIATE_TYPED_TEST_CASE_P(, DwarfEhFrameTest, DwarfEhFrameTestTypes); diff --git a/libunwindstack/tests/ElfInterfaceTest.cpp b/libunwindstack/tests/ElfInterfaceTest.cpp index c31903dbb..81cdaf5f0 100644 --- a/libunwindstack/tests/ElfInterfaceTest.cpp +++ b/libunwindstack/tests/ElfInterfaceTest.cpp @@ -67,6 +67,18 @@ class ElfInterfaceTest : public ::testing::Test { template void SonameSize(); + template + void InitHeadersEhFrameTest(); + + template + void InitHeadersDebugFrame(); + + template + void InitHeadersEhFrameFail(); + + template + void InitHeadersDebugFrameFail(); + MemoryFake memory_; }; @@ -571,3 +583,138 @@ TEST_F(ElfInterfaceTest, elf32_soname_size) { TEST_F(ElfInterfaceTest, elf64_soname_size) { SonameSize(); } + +class MockElfInterface32 : public ElfInterface32 { + public: + MockElfInterface32(Memory* memory) : ElfInterface32(memory) {} + virtual ~MockElfInterface32() = default; + + void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; } + void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; } + + void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; } + void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; } +}; + +class MockElfInterface64 : public ElfInterface64 { + public: + MockElfInterface64(Memory* memory) : ElfInterface64(memory) {} + virtual ~MockElfInterface64() = default; + + void TestSetEhFrameOffset(uint64_t offset) { eh_frame_offset_ = offset; } + void TestSetEhFrameSize(uint64_t size) { eh_frame_size_ = size; } + + void TestSetDebugFrameOffset(uint64_t offset) { debug_frame_offset_ = offset; } + void TestSetDebugFrameSize(uint64_t size) { debug_frame_size_ = size; } +}; + +template +void ElfInterfaceTest::InitHeadersEhFrameTest() { + ElfType elf(&memory_); + + elf.TestSetEhFrameOffset(0x10000); + elf.TestSetEhFrameSize(0); + elf.TestSetDebugFrameOffset(0); + elf.TestSetDebugFrameSize(0); + + memory_.SetMemory(0x10000, + std::vector{0x1, DW_EH_PE_udata2, DW_EH_PE_udata2, DW_EH_PE_udata2}); + memory_.SetData32(0x10004, 0x500); + memory_.SetData32(0x10008, 250); + + elf.InitHeaders(); + + EXPECT_FALSE(elf.eh_frame() == nullptr); + EXPECT_TRUE(elf.debug_frame() == nullptr); +} + +TEST_F(ElfInterfaceTest, init_headers_eh_frame32) { + InitHeadersEhFrameTest(); +} + +TEST_F(ElfInterfaceTest, init_headers_eh_frame64) { + InitHeadersEhFrameTest(); +} + +template +void ElfInterfaceTest::InitHeadersDebugFrame() { + ElfType elf(&memory_); + + elf.TestSetEhFrameOffset(0); + elf.TestSetEhFrameSize(0); + elf.TestSetDebugFrameOffset(0x5000); + elf.TestSetDebugFrameSize(0x200); + + memory_.SetData32(0x5000, 0xfc); + memory_.SetData32(0x5004, 0xffffffff); + memory_.SetData8(0x5008, 1); + memory_.SetData8(0x5009, '\0'); + + memory_.SetData32(0x5100, 0xfc); + memory_.SetData32(0x5104, 0); + memory_.SetData32(0x5108, 0x1500); + memory_.SetData32(0x510c, 0x200); + + elf.InitHeaders(); + + EXPECT_TRUE(elf.eh_frame() == nullptr); + EXPECT_FALSE(elf.debug_frame() == nullptr); +} + +TEST_F(ElfInterfaceTest, init_headers_debug_frame32) { + InitHeadersDebugFrame(); +} + +TEST_F(ElfInterfaceTest, init_headers_debug_frame64) { + InitHeadersDebugFrame(); +} + +template +void ElfInterfaceTest::InitHeadersEhFrameFail() { + ElfType elf(&memory_); + + elf.TestSetEhFrameOffset(0x1000); + elf.TestSetEhFrameSize(0x100); + elf.TestSetDebugFrameOffset(0); + elf.TestSetDebugFrameSize(0); + + elf.InitHeaders(); + + EXPECT_TRUE(elf.eh_frame() == nullptr); + EXPECT_EQ(0U, elf.eh_frame_offset()); + EXPECT_EQ(static_cast(-1), elf.eh_frame_size()); + EXPECT_TRUE(elf.debug_frame() == nullptr); +} + +TEST_F(ElfInterfaceTest, init_headers_eh_frame32_fail) { + InitHeadersEhFrameFail(); +} + +TEST_F(ElfInterfaceTest, init_headers_eh_frame64_fail) { + InitHeadersEhFrameFail(); +} + +template +void ElfInterfaceTest::InitHeadersDebugFrameFail() { + ElfType elf(&memory_); + + elf.TestSetEhFrameOffset(0); + elf.TestSetEhFrameSize(0); + elf.TestSetDebugFrameOffset(0x1000); + elf.TestSetDebugFrameSize(0x100); + + elf.InitHeaders(); + + EXPECT_TRUE(elf.eh_frame() == nullptr); + EXPECT_TRUE(elf.debug_frame() == nullptr); + EXPECT_EQ(0U, elf.debug_frame_offset()); + EXPECT_EQ(static_cast(-1), elf.debug_frame_size()); +} + +TEST_F(ElfInterfaceTest, init_headers_debug_frame32_fail) { + InitHeadersDebugFrameFail(); +} + +TEST_F(ElfInterfaceTest, init_headers_debug_frame64_fail) { + InitHeadersDebugFrameFail(); +}