You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
316 lines
8.5 KiB
316 lines
8.5 KiB
/* |
|
* 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
|
|
|