Add EH frame and debug frame support.
Bug: 23762183
Test: Pass new unit tests.
Change-Id: I1bfe900e068017ff31998f359bf98d4c5c9af2a5
diff --git a/libunwindstack/DwarfDebugFrame.cpp b/libunwindstack/DwarfDebugFrame.cpp
new file mode 100644
index 0000000..3ac02fc
--- /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 <stdint.h>
+#include <stdlib.h>
+
+#include <algorithm>
+
+#include "DwarfDebugFrame.h"
+#include "DwarfMemory.h"
+#include "DwarfSection.h"
+#include "DwarfStructs.h"
+#include "Memory.h"
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::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 <typename AddressType>
+bool DwarfDebugFrame<AddressType>::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<char> 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<AddressType>(encoding, &value)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ }
+ }
+
+ // It should be impossible to get here.
+ abort();
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::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<AddressType>(encoding & 0xf, &start)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+
+ uint64_t length;
+ if (!memory_.template ReadEncodedValue<AddressType>(encoding & 0xf, &length)) {
+ last_error_ = DWARF_ERROR_MEMORY_INVALID;
+ return false;
+ }
+ if (length != 0) {
+ fdes_.emplace_back(entry_offset, start, length);
+ }
+
+ return true;
+}
+
+template <typename AddressType>
+bool DwarfDebugFrame<AddressType>::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<uint32_t>(-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<uint64_t>(-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<uint32_t>(-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 <typename AddressType>
+bool DwarfDebugFrame<AddressType>::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 <typename AddressType>
+const DwarfFde* DwarfDebugFrame<AddressType>::GetFdeFromIndex(size_t index) {
+ if (index >= fdes_.size()) {
+ return nullptr;
+ }
+ return this->GetFdeFromOffset(fdes_[index].offset);
+}
+
+// Explicitly instantiate DwarfDebugFrame.
+template class DwarfDebugFrame<uint32_t>;
+template class DwarfDebugFrame<uint64_t>;