317 lines
8.5 KiB
C++
317 lines
8.5 KiB
C++
/*
|
|
* 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
|