blob: 57075962ac6e3c568b885cb65bd850dd51ee1cd1 [file] [log] [blame]
/*
* 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