| /* |
| * 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 <unwindstack/DwarfStructs.h> |
| #include <unwindstack/Memory.h> |
| |
| #include "DwarfDebugFrame.h" |
| #include "DwarfEncoding.h" |
| #include "DwarfError.h" |
| |
| namespace unwindstack { |
| |
| 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>; |
| |
| } // namespace unwindstack |