Add ability to read jit gdb data.
Changes: - New JitDebug class to handle all of the jit gdb interface. - Add unit tests for all, along with new offline test using debug data. - Add new Memory type called MemoryOfflineParts that has multiple MemoryOffline objects to support the offline test. - Update the tools to use the JitDebug object. - Modify libbacktrace to use the JitDebug, but only looking in libart.so and libartd.so. - Change the Format32Bits to Is32Bit since it's more accurate and I use it in a different context where original name didn't make sense. - Add a new function to find global variables in an elf file (GetGlobalVariable). - Add a new function to determine if a pc is valid for this elf (IsValidPc). Bug: 68396769 Test: Ran new unit tests. Added new offline test that uses jit debug data. Test: Ran art test that generates jit data and verified a crash unwinds Test: through the jit data. Change-Id: I6e7ee2f5bab2242028a06feece156dff21c0a974
This commit is contained in:
parent
55feb241b1
commit
150db124f3
|
@ -50,6 +50,7 @@ bool Backtrace::Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
|||
auto process_memory = stack_map->process_memory();
|
||||
unwindstack::Unwinder unwinder(MAX_BACKTRACE_FRAMES + num_ignore_frames, stack_map->stack_maps(),
|
||||
regs, stack_map->process_memory());
|
||||
unwinder.SetJitDebug(stack_map->GetJitDebug(), regs->Arch());
|
||||
unwinder.Unwind(skip_names, &stack_map->GetSuffixesToIgnore());
|
||||
|
||||
if (num_ignore_frames >= unwinder.NumFrames()) {
|
||||
|
|
|
@ -18,6 +18,9 @@
|
|||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
|
@ -39,6 +42,10 @@ bool UnwindStackMap::Build() {
|
|||
// Create the process memory object.
|
||||
process_memory_ = unwindstack::Memory::CreateProcessMemory(pid_);
|
||||
|
||||
// Create a JitDebug object for getting jit unwind information.
|
||||
std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
|
||||
jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
|
||||
|
||||
if (!stack_maps_->Parse()) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include <backtrace/BacktraceMap.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
|
||||
class UnwindStackMap : public BacktraceMap {
|
||||
|
@ -41,11 +42,14 @@ class UnwindStackMap : public BacktraceMap {
|
|||
|
||||
const std::shared_ptr<unwindstack::Memory>& process_memory() { return process_memory_; }
|
||||
|
||||
unwindstack::JitDebug* GetJitDebug() { return jit_debug_.get(); }
|
||||
|
||||
protected:
|
||||
uint64_t GetLoadBias(size_t index) override;
|
||||
|
||||
std::unique_ptr<unwindstack::Maps> stack_maps_;
|
||||
std::shared_ptr<unwindstack::Memory> process_memory_;
|
||||
std::unique_ptr<unwindstack::JitDebug> jit_debug_;
|
||||
};
|
||||
|
||||
#endif // _LIBBACKTRACE_UNWINDSTACK_MAP_H
|
||||
|
|
|
@ -55,6 +55,7 @@ cc_library {
|
|||
"Elf.cpp",
|
||||
"ElfInterface.cpp",
|
||||
"ElfInterfaceArm.cpp",
|
||||
"JitDebug.cpp",
|
||||
"Log.cpp",
|
||||
"MapInfo.cpp",
|
||||
"Maps.cpp",
|
||||
|
@ -128,6 +129,7 @@ cc_test {
|
|||
"tests/ElfInterfaceTest.cpp",
|
||||
"tests/ElfTest.cpp",
|
||||
"tests/ElfTestUtils.cpp",
|
||||
"tests/JitDebugTest.cpp",
|
||||
"tests/LogFake.cpp",
|
||||
"tests/MapInfoGetElfTest.cpp",
|
||||
"tests/MapInfoGetLoadBiasTest.cpp",
|
||||
|
@ -168,6 +170,7 @@ cc_test {
|
|||
data: [
|
||||
"tests/files/elf32.xz",
|
||||
"tests/files/elf64.xz",
|
||||
"tests/files/offline/jit_debug_x86_32/*",
|
||||
"tests/files/offline/gnu_debugdata_arm32/*",
|
||||
"tests/files/offline/straddle_arm32/*",
|
||||
"tests/files/offline/straddle_arm64/*",
|
||||
|
|
|
@ -103,6 +103,37 @@ bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offse
|
|||
addr, load_bias_, name, func_offset)));
|
||||
}
|
||||
|
||||
bool Elf::GetGlobalVariable(const std::string& name, uint64_t* memory_address) {
|
||||
if (!valid_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!interface_->GetGlobalVariable(name, memory_address) &&
|
||||
(gnu_debugdata_interface_ == nullptr ||
|
||||
!gnu_debugdata_interface_->GetGlobalVariable(name, memory_address))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adjust by the load bias.
|
||||
if (*memory_address < load_bias_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*memory_address -= load_bias_;
|
||||
|
||||
// If this winds up in the dynamic section, then we might need to adjust
|
||||
// the address.
|
||||
uint64_t dynamic_end = interface_->dynamic_vaddr() + interface_->dynamic_size();
|
||||
if (*memory_address >= interface_->dynamic_vaddr() && *memory_address < dynamic_end) {
|
||||
if (interface_->dynamic_vaddr() > interface_->dynamic_offset()) {
|
||||
*memory_address -= interface_->dynamic_vaddr() - interface_->dynamic_offset();
|
||||
} else {
|
||||
*memory_address += interface_->dynamic_offset() - interface_->dynamic_vaddr();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// The relative pc is always relative to the start of the map from which it comes.
|
||||
bool Elf::Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
|
||||
Memory* process_memory, bool* finished) {
|
||||
|
@ -160,6 +191,23 @@ void Elf::GetInfo(Memory* memory, bool* valid, uint64_t* size) {
|
|||
}
|
||||
}
|
||||
|
||||
bool Elf::IsValidPc(uint64_t pc) {
|
||||
if (!valid_ || pc < load_bias_) {
|
||||
return false;
|
||||
}
|
||||
pc -= load_bias_;
|
||||
|
||||
if (interface_->IsValidPc(pc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gnu_debugdata_interface_ != nullptr && gnu_debugdata_interface_->IsValidPc(pc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
ElfInterface* Elf::CreateInterfaceFromMemory(Memory* memory) {
|
||||
if (!IsValidElf(memory)) {
|
||||
return nullptr;
|
||||
|
|
|
@ -43,6 +43,30 @@ ElfInterface::~ElfInterface() {
|
|||
}
|
||||
}
|
||||
|
||||
bool ElfInterface::IsValidPc(uint64_t pc) {
|
||||
if (!pt_loads_.empty()) {
|
||||
for (auto& entry : pt_loads_) {
|
||||
uint64_t start = entry.second.table_offset;
|
||||
uint64_t end = start + entry.second.table_size;
|
||||
if (pc >= start && pc < end) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// No PT_LOAD data, look for a fde for this pc in the section data.
|
||||
if (debug_frame_ != nullptr && debug_frame_->GetFdeFromPc(pc) != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (eh_frame_ != nullptr && eh_frame_->GetFdeFromPc(pc) != nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Memory* ElfInterface::CreateGnuDebugdataMemory() {
|
||||
if (gnu_debugdata_offset_ == 0 || gnu_debugdata_size_ == 0) {
|
||||
return nullptr;
|
||||
|
@ -225,6 +249,10 @@ bool ElfInterface::ReadProgramHeaders(const EhdrType& ehdr, uint64_t* load_bias)
|
|||
return false;
|
||||
}
|
||||
dynamic_offset_ = phdr.p_offset;
|
||||
if (!memory_->ReadField(offset, &phdr, &phdr.p_vaddr, sizeof(phdr.p_vaddr))) {
|
||||
return false;
|
||||
}
|
||||
dynamic_vaddr_ = phdr.p_vaddr;
|
||||
if (!memory_->ReadField(offset, &phdr, &phdr.p_memsz, sizeof(phdr.p_memsz))) {
|
||||
return false;
|
||||
}
|
||||
|
@ -386,6 +414,20 @@ bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias
|
|||
return false;
|
||||
}
|
||||
|
||||
template <typename SymType>
|
||||
bool ElfInterface::GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address) {
|
||||
if (symbols_.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto symbol : symbols_) {
|
||||
if (symbol->GetGlobal<SymType>(memory_, name, memory_address)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ElfInterface::Step(uint64_t pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
|
||||
bool* finished) {
|
||||
// Adjust the load bias to get the real relative pc.
|
||||
|
@ -451,6 +493,9 @@ template bool ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(uint64_t, uin
|
|||
template bool ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(uint64_t, uint64_t, std::string*,
|
||||
uint64_t*);
|
||||
|
||||
template bool ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(const std::string&, uint64_t*);
|
||||
template bool ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(const std::string&, uint64_t*);
|
||||
|
||||
template void ElfInterface::GetMaxSizeWithTemplate<Elf32_Ehdr>(Memory*, uint64_t*);
|
||||
template void ElfInterface::GetMaxSizeWithTemplate<Elf64_Ehdr>(Memory*, uint64_t*);
|
||||
|
||||
|
|
|
@ -0,0 +1,248 @@
|
|||
/*
|
||||
* 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 <sys/mman.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
// This implements the JIT Compilation Interface.
|
||||
// See https://sourceware.org/gdb/onlinedocs/gdb/JIT-Interface.html
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
struct JITCodeEntry32Pack {
|
||||
uint32_t next;
|
||||
uint32_t prev;
|
||||
uint32_t symfile_addr;
|
||||
uint64_t symfile_size;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct JITCodeEntry32Pad {
|
||||
uint32_t next;
|
||||
uint32_t prev;
|
||||
uint32_t symfile_addr;
|
||||
uint32_t pad;
|
||||
uint64_t symfile_size;
|
||||
};
|
||||
|
||||
struct JITCodeEntry64 {
|
||||
uint64_t next;
|
||||
uint64_t prev;
|
||||
uint64_t symfile_addr;
|
||||
uint64_t symfile_size;
|
||||
};
|
||||
|
||||
struct JITDescriptorHeader {
|
||||
uint32_t version;
|
||||
uint32_t action_flag;
|
||||
};
|
||||
|
||||
struct JITDescriptor32 {
|
||||
JITDescriptorHeader header;
|
||||
uint32_t relevant_entry;
|
||||
uint32_t first_entry;
|
||||
};
|
||||
|
||||
struct JITDescriptor64 {
|
||||
JITDescriptorHeader header;
|
||||
uint64_t relevant_entry;
|
||||
uint64_t first_entry;
|
||||
};
|
||||
|
||||
JitDebug::JitDebug(std::shared_ptr<Memory>& memory) : memory_(memory) {}
|
||||
|
||||
JitDebug::JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
|
||||
: memory_(memory), search_libs_(search_libs) {}
|
||||
|
||||
JitDebug::~JitDebug() {
|
||||
for (auto* elf : elf_list_) {
|
||||
delete elf;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t JitDebug::ReadDescriptor32(uint64_t addr) {
|
||||
JITDescriptor32 desc;
|
||||
if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (desc.header.version != 1 || desc.first_entry == 0) {
|
||||
// Either unknown version, or no jit entries.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return desc.first_entry;
|
||||
}
|
||||
|
||||
uint64_t JitDebug::ReadDescriptor64(uint64_t addr) {
|
||||
JITDescriptor64 desc;
|
||||
if (!memory_->ReadFully(addr, &desc, sizeof(desc))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (desc.header.version != 1 || desc.first_entry == 0) {
|
||||
// Either unknown version, or no jit entries.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return desc.first_entry;
|
||||
}
|
||||
|
||||
uint64_t JitDebug::ReadEntry32Pack(uint64_t* start, uint64_t* size) {
|
||||
JITCodeEntry32Pack code;
|
||||
if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = code.symfile_addr;
|
||||
*size = code.symfile_size;
|
||||
return code.next;
|
||||
}
|
||||
|
||||
uint64_t JitDebug::ReadEntry32Pad(uint64_t* start, uint64_t* size) {
|
||||
JITCodeEntry32Pad code;
|
||||
if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = code.symfile_addr;
|
||||
*size = code.symfile_size;
|
||||
return code.next;
|
||||
}
|
||||
|
||||
uint64_t JitDebug::ReadEntry64(uint64_t* start, uint64_t* size) {
|
||||
JITCodeEntry64 code;
|
||||
if (!memory_->ReadFully(entry_addr_, &code, sizeof(code))) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*start = code.symfile_addr;
|
||||
*size = code.symfile_size;
|
||||
return code.next;
|
||||
}
|
||||
|
||||
void JitDebug::SetArch(ArchEnum arch) {
|
||||
switch (arch) {
|
||||
case ARCH_X86:
|
||||
read_descriptor_func_ = &JitDebug::ReadDescriptor32;
|
||||
read_entry_func_ = &JitDebug::ReadEntry32Pack;
|
||||
break;
|
||||
|
||||
case ARCH_ARM:
|
||||
case ARCH_MIPS:
|
||||
read_descriptor_func_ = &JitDebug::ReadDescriptor32;
|
||||
read_entry_func_ = &JitDebug::ReadEntry32Pad;
|
||||
break;
|
||||
|
||||
case ARCH_ARM64:
|
||||
case ARCH_X86_64:
|
||||
case ARCH_MIPS64:
|
||||
read_descriptor_func_ = &JitDebug::ReadDescriptor64;
|
||||
read_entry_func_ = &JitDebug::ReadEntry64;
|
||||
break;
|
||||
case ARCH_UNKNOWN:
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void JitDebug::Init(Maps* maps) {
|
||||
if (initialized_) {
|
||||
return;
|
||||
}
|
||||
// Regardless of what happens below, consider the init finished.
|
||||
initialized_ = true;
|
||||
|
||||
std::string descriptor_name("__jit_debug_descriptor");
|
||||
uint64_t descriptor_addr = 0;
|
||||
for (MapInfo* info : *maps) {
|
||||
if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!search_libs_.empty()) {
|
||||
bool found = false;
|
||||
const char* lib = basename(info->name.c_str());
|
||||
for (std::string& name : search_libs_) {
|
||||
if (strcmp(name.c_str(), lib) == 0) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
Elf* elf = info->GetElf(memory_, true);
|
||||
if (elf->GetGlobalVariable(descriptor_name, &descriptor_addr)) {
|
||||
descriptor_addr += info->start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (descriptor_addr == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
entry_addr_ = (this->*read_descriptor_func_)(descriptor_addr);
|
||||
}
|
||||
|
||||
Elf* JitDebug::GetElf(Maps* maps, uint64_t pc) {
|
||||
// Use a single lock, this object should be used so infrequently that
|
||||
// a fine grain lock is unnecessary.
|
||||
std::lock_guard<std::mutex> guard(lock_);
|
||||
if (!initialized_) {
|
||||
Init(maps);
|
||||
}
|
||||
|
||||
// Search the existing elf object first.
|
||||
for (Elf* elf : elf_list_) {
|
||||
if (elf->IsValidPc(pc)) {
|
||||
return elf;
|
||||
}
|
||||
}
|
||||
|
||||
while (entry_addr_ != 0) {
|
||||
uint64_t start;
|
||||
uint64_t size;
|
||||
entry_addr_ = (this->*read_entry_func_)(&start, &size);
|
||||
|
||||
Elf* elf = new Elf(new MemoryRange(memory_, start, size, 0));
|
||||
elf->Init(true);
|
||||
if (!elf->valid()) {
|
||||
// The data is not formatted in a way we understand, do not attempt
|
||||
// to process any other entries.
|
||||
entry_addr_ = 0;
|
||||
delete elf;
|
||||
return nullptr;
|
||||
}
|
||||
elf_list_.push_back(elf);
|
||||
|
||||
if (elf->IsValidPc(pc)) {
|
||||
return elf;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
|
@ -345,4 +345,26 @@ size_t MemoryOffline::Read(uint64_t addr, void* dst, size_t size) {
|
|||
return memory_->Read(addr, dst, size);
|
||||
}
|
||||
|
||||
MemoryOfflineParts::~MemoryOfflineParts() {
|
||||
for (auto memory : memories_) {
|
||||
delete memory;
|
||||
}
|
||||
}
|
||||
|
||||
size_t MemoryOfflineParts::Read(uint64_t addr, void* dst, size_t size) {
|
||||
if (memories_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Do a read on each memory object, no support for reading across the
|
||||
// different memory objects.
|
||||
for (MemoryOffline* memory : memories_) {
|
||||
size_t bytes = memory->Read(addr, dst, size);
|
||||
if (bytes != 0) {
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -37,10 +37,6 @@ ArchEnum RegsArm::Arch() {
|
|||
}
|
||||
|
||||
uint64_t RegsArm::GetAdjustedPc(uint64_t rel_pc, Elf* elf) {
|
||||
if (!elf->valid()) {
|
||||
return rel_pc;
|
||||
}
|
||||
|
||||
uint64_t load_bias = elf->GetLoadBias();
|
||||
if (rel_pc < load_bias) {
|
||||
return rel_pc;
|
||||
|
|
|
@ -108,8 +108,35 @@ bool Symbols::GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std
|
|||
return return_value;
|
||||
}
|
||||
|
||||
template <typename SymType>
|
||||
bool Symbols::GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address) {
|
||||
uint64_t cur_offset = offset_;
|
||||
while (cur_offset + entry_size_ <= end_) {
|
||||
SymType entry;
|
||||
if (!elf_memory->ReadFully(cur_offset, &entry, sizeof(entry))) {
|
||||
return false;
|
||||
}
|
||||
cur_offset += entry_size_;
|
||||
|
||||
if (entry.st_shndx != SHN_UNDEF && ELF32_ST_TYPE(entry.st_info) == STT_OBJECT &&
|
||||
ELF32_ST_BIND(entry.st_info) == STB_GLOBAL) {
|
||||
uint64_t str_offset = str_offset_ + entry.st_name;
|
||||
if (str_offset < str_end_) {
|
||||
std::string symbol;
|
||||
if (elf_memory->ReadString(str_offset, &symbol, str_end_ - str_offset) && symbol == name) {
|
||||
*memory_address = entry.st_value;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Instantiate all of the needed template functions.
|
||||
template bool Symbols::GetName<Elf32_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
|
||||
template bool Symbols::GetName<Elf64_Sym>(uint64_t, uint64_t, Memory*, std::string*, uint64_t*);
|
||||
|
||||
template bool Symbols::GetGlobal<Elf32_Sym>(Memory*, const std::string&, uint64_t*);
|
||||
template bool Symbols::GetGlobal<Elf64_Sym>(Memory*, const std::string&, uint64_t*);
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -47,6 +47,9 @@ class Symbols {
|
|||
bool GetName(uint64_t addr, uint64_t load_bias, Memory* elf_memory, std::string* name,
|
||||
uint64_t* func_offset);
|
||||
|
||||
template <typename SymType>
|
||||
bool GetGlobal(Memory* elf_memory, const std::string& name, uint64_t* memory_address);
|
||||
|
||||
void ClearCache() {
|
||||
symbols_.clear();
|
||||
cur_offset_ = offset_;
|
||||
|
|
|
@ -27,12 +27,13 @@
|
|||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc) {
|
||||
void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t func_pc) {
|
||||
size_t frame_num = frames_.size();
|
||||
frames_.resize(frame_num + 1);
|
||||
FrameData* frame = &frames_.at(frame_num);
|
||||
|
@ -53,7 +54,7 @@ void Unwinder::FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc
|
|||
frame->map_flags = map_info->flags;
|
||||
frame->map_load_bias = elf->GetLoadBias();
|
||||
|
||||
if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) {
|
||||
if (!elf->GetFunctionName(func_pc, &frame->function_name, &frame->function_offset)) {
|
||||
frame->function_name = "";
|
||||
frame->function_offset = 0;
|
||||
}
|
||||
|
@ -79,17 +80,20 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
|
|||
|
||||
bool return_address_attempt = false;
|
||||
bool adjust_pc = false;
|
||||
std::unique_ptr<JitDebug> jit_debug;
|
||||
for (; frames_.size() < max_frames_;) {
|
||||
uint64_t cur_pc = regs_->pc();
|
||||
uint64_t cur_sp = regs_->sp();
|
||||
|
||||
MapInfo* map_info = maps_->Find(regs_->pc());
|
||||
uint64_t rel_pc;
|
||||
uint64_t adjusted_pc;
|
||||
uint64_t adjusted_rel_pc;
|
||||
Elf* elf;
|
||||
if (map_info == nullptr) {
|
||||
rel_pc = regs_->pc();
|
||||
adjusted_rel_pc = rel_pc;
|
||||
adjusted_pc = rel_pc;
|
||||
} else {
|
||||
if (ShouldStop(map_suffixes_to_ignore, map_info->name)) {
|
||||
break;
|
||||
|
@ -97,16 +101,30 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
|
|||
elf = map_info->GetElf(process_memory_, true);
|
||||
rel_pc = elf->GetRelPc(regs_->pc(), map_info);
|
||||
if (adjust_pc) {
|
||||
adjusted_rel_pc = regs_->GetAdjustedPc(rel_pc, elf);
|
||||
adjusted_pc = regs_->GetAdjustedPc(rel_pc, elf);
|
||||
} else {
|
||||
adjusted_rel_pc = rel_pc;
|
||||
adjusted_pc = rel_pc;
|
||||
}
|
||||
adjusted_rel_pc = adjusted_pc;
|
||||
|
||||
// If the pc is in an invalid elf file, try and get an Elf object
|
||||
// using the jit debug information.
|
||||
if (!elf->valid() && jit_debug_ != nullptr) {
|
||||
uint64_t adjusted_jit_pc = regs_->pc() - (rel_pc - adjusted_pc);
|
||||
Elf* jit_elf = jit_debug_->GetElf(maps_, adjusted_jit_pc);
|
||||
if (jit_elf != nullptr) {
|
||||
// The jit debug information requires a non relative adjusted pc.
|
||||
adjusted_pc = adjusted_jit_pc;
|
||||
adjusted_rel_pc = adjusted_pc - map_info->start;
|
||||
elf = jit_elf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (map_info == nullptr || initial_map_names_to_skip == nullptr ||
|
||||
std::find(initial_map_names_to_skip->begin(), initial_map_names_to_skip->end(),
|
||||
basename(map_info->name.c_str())) == initial_map_names_to_skip->end()) {
|
||||
FillInFrame(map_info, elf, adjusted_rel_pc);
|
||||
FillInFrame(map_info, elf, adjusted_rel_pc, adjusted_pc);
|
||||
|
||||
// Once a frame is added, stop skipping frames.
|
||||
initial_map_names_to_skip = nullptr;
|
||||
|
@ -134,7 +152,7 @@ void Unwinder::Unwind(const std::vector<std::string>* initial_map_names_to_skip,
|
|||
in_device_map = true;
|
||||
} else {
|
||||
bool finished;
|
||||
stepped = elf->Step(rel_pc, adjusted_rel_pc, map_info->elf_offset, regs_,
|
||||
stepped = elf->Step(rel_pc, adjusted_pc, map_info->elf_offset, regs_,
|
||||
process_memory_.get(), &finished);
|
||||
if (stepped && finished) {
|
||||
break;
|
||||
|
@ -174,13 +192,13 @@ std::string Unwinder::FormatFrame(size_t frame_num) {
|
|||
if (frame_num >= frames_.size()) {
|
||||
return "";
|
||||
}
|
||||
return FormatFrame(frames_[frame_num], regs_->Format32Bit());
|
||||
return FormatFrame(frames_[frame_num], regs_->Is32Bit());
|
||||
}
|
||||
|
||||
std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) {
|
||||
std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) {
|
||||
std::string data;
|
||||
|
||||
if (bits32) {
|
||||
if (is32bit) {
|
||||
data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
|
||||
} else {
|
||||
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
|
||||
|
@ -208,4 +226,9 @@ std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) {
|
|||
return data;
|
||||
}
|
||||
|
||||
void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) {
|
||||
jit_debug->SetArch(arch);
|
||||
jit_debug_ = jit_debug;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -59,6 +59,8 @@ class Elf {
|
|||
|
||||
bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offset);
|
||||
|
||||
bool GetGlobalVariable(const std::string& name, uint64_t* memory_address);
|
||||
|
||||
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
|
||||
|
||||
bool Step(uint64_t rel_pc, uint64_t adjusted_rel_pc, uint64_t elf_offset, Regs* regs,
|
||||
|
@ -68,6 +70,8 @@ class Elf {
|
|||
|
||||
uint64_t GetLoadBias() { return load_bias_; }
|
||||
|
||||
bool IsValidPc(uint64_t pc);
|
||||
|
||||
bool valid() { return valid_; }
|
||||
|
||||
uint32_t machine_type() { return machine_type_; }
|
||||
|
|
|
@ -60,9 +60,13 @@ class ElfInterface {
|
|||
virtual bool GetFunctionName(uint64_t addr, uint64_t load_bias, std::string* name,
|
||||
uint64_t* offset) = 0;
|
||||
|
||||
virtual bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) = 0;
|
||||
|
||||
virtual bool Step(uint64_t rel_pc, uint64_t load_bias, Regs* regs, Memory* process_memory,
|
||||
bool* finished);
|
||||
|
||||
virtual bool IsValidPc(uint64_t pc);
|
||||
|
||||
Memory* CreateGnuDebugdataMemory();
|
||||
|
||||
Memory* memory() { return memory_; }
|
||||
|
@ -72,6 +76,7 @@ class ElfInterface {
|
|||
void SetGnuDebugdataInterface(ElfInterface* interface) { gnu_debugdata_interface_ = interface; }
|
||||
|
||||
uint64_t dynamic_offset() { return dynamic_offset_; }
|
||||
uint64_t dynamic_vaddr() { return dynamic_vaddr_; }
|
||||
uint64_t dynamic_size() { return dynamic_size_; }
|
||||
uint64_t eh_frame_hdr_offset() { return eh_frame_hdr_offset_; }
|
||||
uint64_t eh_frame_hdr_size() { return eh_frame_hdr_size_; }
|
||||
|
@ -108,6 +113,9 @@ class ElfInterface {
|
|||
bool GetFunctionNameWithTemplate(uint64_t addr, uint64_t load_bias, std::string* name,
|
||||
uint64_t* func_offset);
|
||||
|
||||
template <typename SymType>
|
||||
bool GetGlobalVariableWithTemplate(const std::string& name, uint64_t* memory_address);
|
||||
|
||||
virtual bool HandleType(uint64_t, uint32_t, uint64_t) { return false; }
|
||||
|
||||
template <typename EhdrType>
|
||||
|
@ -118,6 +126,7 @@ class ElfInterface {
|
|||
|
||||
// Stored elf data.
|
||||
uint64_t dynamic_offset_ = 0;
|
||||
uint64_t dynamic_vaddr_ = 0;
|
||||
uint64_t dynamic_size_ = 0;
|
||||
|
||||
uint64_t eh_frame_hdr_offset_ = 0;
|
||||
|
@ -163,6 +172,10 @@ class ElfInterface32 : public ElfInterface {
|
|||
return ElfInterface::GetFunctionNameWithTemplate<Elf32_Sym>(addr, load_bias, name, func_offset);
|
||||
}
|
||||
|
||||
bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
|
||||
return ElfInterface::GetGlobalVariableWithTemplate<Elf32_Sym>(name, memory_address);
|
||||
}
|
||||
|
||||
static void GetMaxSize(Memory* memory, uint64_t* size) {
|
||||
GetMaxSizeWithTemplate<Elf32_Ehdr>(memory, size);
|
||||
}
|
||||
|
@ -188,6 +201,10 @@ class ElfInterface64 : public ElfInterface {
|
|||
return ElfInterface::GetFunctionNameWithTemplate<Elf64_Sym>(addr, load_bias, name, func_offset);
|
||||
}
|
||||
|
||||
bool GetGlobalVariable(const std::string& name, uint64_t* memory_address) override {
|
||||
return ElfInterface::GetGlobalVariableWithTemplate<Elf64_Sym>(name, memory_address);
|
||||
}
|
||||
|
||||
static void GetMaxSize(Memory* memory, uint64_t* size) {
|
||||
GetMaxSizeWithTemplate<Elf64_Ehdr>(memory, size);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_JIT_DEBUG_H
|
||||
#define _LIBUNWINDSTACK_JIT_DEBUG_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
// Forward declarations.
|
||||
class Elf;
|
||||
class Maps;
|
||||
class Memory;
|
||||
enum ArchEnum : uint8_t;
|
||||
|
||||
class JitDebug {
|
||||
public:
|
||||
explicit JitDebug(std::shared_ptr<Memory>& memory);
|
||||
JitDebug(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
|
||||
~JitDebug();
|
||||
|
||||
Elf* GetElf(Maps* maps, uint64_t pc);
|
||||
|
||||
void SetArch(ArchEnum arch);
|
||||
|
||||
private:
|
||||
void Init(Maps* maps);
|
||||
|
||||
std::shared_ptr<Memory> memory_;
|
||||
uint64_t entry_addr_ = 0;
|
||||
bool initialized_ = false;
|
||||
std::vector<Elf*> elf_list_;
|
||||
std::vector<std::string> search_libs_;
|
||||
|
||||
std::mutex lock_;
|
||||
|
||||
uint64_t (JitDebug::*read_descriptor_func_)(uint64_t) = nullptr;
|
||||
uint64_t (JitDebug::*read_entry_func_)(uint64_t*, uint64_t*) = nullptr;
|
||||
|
||||
uint64_t ReadDescriptor32(uint64_t);
|
||||
uint64_t ReadDescriptor64(uint64_t);
|
||||
|
||||
uint64_t ReadEntry32Pack(uint64_t* start, uint64_t* size);
|
||||
uint64_t ReadEntry32Pad(uint64_t* start, uint64_t* size);
|
||||
uint64_t ReadEntry64(uint64_t* start, uint64_t* size);
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_JIT_DEBUG_H
|
|
@ -151,6 +151,19 @@ class MemoryOffline : public Memory {
|
|||
std::unique_ptr<MemoryRange> memory_;
|
||||
};
|
||||
|
||||
class MemoryOfflineParts : public Memory {
|
||||
public:
|
||||
MemoryOfflineParts() = default;
|
||||
virtual ~MemoryOfflineParts();
|
||||
|
||||
void Add(MemoryOffline* memory) { memories_.push_back(memory); }
|
||||
|
||||
size_t Read(uint64_t addr, void* dst, size_t size) override;
|
||||
|
||||
private:
|
||||
std::vector<MemoryOffline*> memories_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_MEMORY_H
|
||||
|
|
|
@ -51,7 +51,7 @@ class Regs {
|
|||
|
||||
virtual ArchEnum Arch() = 0;
|
||||
|
||||
virtual bool Format32Bit() = 0;
|
||||
virtual bool Is32Bit() = 0;
|
||||
|
||||
virtual void* RawData() = 0;
|
||||
virtual uint64_t pc() = 0;
|
||||
|
@ -94,7 +94,7 @@ class RegsImpl : public Regs {
|
|||
void set_pc(AddressType pc) { pc_ = pc; }
|
||||
void set_sp(AddressType sp) { sp_ = sp; }
|
||||
|
||||
bool Format32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
|
||||
bool Is32Bit() override { return sizeof(AddressType) == sizeof(uint32_t); }
|
||||
|
||||
inline AddressType& operator[](size_t reg) { return regs_[reg]; }
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ namespace unwindstack {
|
|||
|
||||
// Forward declarations.
|
||||
class Elf;
|
||||
class JitDebug;
|
||||
enum ArchEnum : uint8_t;
|
||||
|
||||
struct FrameData {
|
||||
size_t num;
|
||||
|
@ -67,16 +69,19 @@ class Unwinder {
|
|||
const std::vector<FrameData>& frames() { return frames_; }
|
||||
|
||||
std::string FormatFrame(size_t frame_num);
|
||||
static std::string FormatFrame(const FrameData& frame, bool bits32);
|
||||
static std::string FormatFrame(const FrameData& frame, bool is32bit);
|
||||
|
||||
void SetJitDebug(JitDebug* jit_debug, ArchEnum arch);
|
||||
|
||||
private:
|
||||
void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc);
|
||||
void FillInFrame(MapInfo* map_info, Elf* elf, uint64_t adjusted_rel_pc, uint64_t adjusted_pc);
|
||||
|
||||
size_t max_frames_;
|
||||
Maps* maps_;
|
||||
Regs* regs_;
|
||||
std::vector<FrameData> frames_;
|
||||
std::shared_ptr<Memory> process_memory_;
|
||||
JitDebug* jit_debug_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -43,6 +43,15 @@ bool ElfInterfaceFake::GetFunctionName(uint64_t, uint64_t, std::string* name, ui
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ElfInterfaceFake::GetGlobalVariable(const std::string& global, uint64_t* offset) {
|
||||
auto entry = globals_.find(global);
|
||||
if (entry == globals_.end()) {
|
||||
return false;
|
||||
}
|
||||
*offset = entry->second;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ElfInterfaceFake::Step(uint64_t, uint64_t, Regs* regs, Memory*, bool* finished) {
|
||||
if (steps_.empty()) {
|
||||
return false;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/ElfInterface.h>
|
||||
|
@ -55,6 +56,9 @@ class ElfFake : public Elf {
|
|||
void FakeSetLoadBias(uint64_t load_bias) { load_bias_ = load_bias; }
|
||||
|
||||
void FakeSetInterface(ElfInterface* interface) { interface_.reset(interface); }
|
||||
void FakeSetGnuDebugdataInterface(ElfInterface* interface) {
|
||||
gnu_debugdata_interface_.reset(interface);
|
||||
}
|
||||
};
|
||||
|
||||
class ElfInterfaceFake : public ElfInterface {
|
||||
|
@ -67,9 +71,14 @@ class ElfInterfaceFake : public ElfInterface {
|
|||
bool GetSoname(std::string*) override { return false; }
|
||||
|
||||
bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override;
|
||||
bool GetGlobalVariable(const std::string&, uint64_t*) override;
|
||||
|
||||
bool Step(uint64_t, uint64_t, Regs*, Memory*, bool*) override;
|
||||
|
||||
void FakeSetGlobalVariable(const std::string& global, uint64_t offset) {
|
||||
globals_[global] = offset;
|
||||
}
|
||||
|
||||
static void FakePushFunctionData(const FunctionData data) { functions_.push_back(data); }
|
||||
static void FakePushStepData(const StepData data) { steps_.push_back(data); }
|
||||
|
||||
|
@ -79,6 +88,8 @@ class ElfInterfaceFake : public ElfInterface {
|
|||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, uint64_t> globals_;
|
||||
|
||||
static std::deque<FunctionData> functions_;
|
||||
static std::deque<StepData> steps_;
|
||||
};
|
||||
|
|
|
@ -958,4 +958,189 @@ TEST_F(ElfInterfaceTest, init_section_headers_offsets64) {
|
|||
InitSectionHeadersOffsets<Elf64_Ehdr, Elf64_Shdr, ElfInterface64>();
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load) {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
|
||||
|
||||
Elf32_Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 1;
|
||||
ehdr.e_phentsize = sizeof(Elf32_Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Elf32_Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
phdr.p_type = PT_LOAD;
|
||||
phdr.p_vaddr = 0;
|
||||
phdr.p_memsz = 0x10000;
|
||||
phdr.p_flags = PF_R | PF_X;
|
||||
phdr.p_align = 0x1000;
|
||||
memory_.SetMemory(0x100, &phdr, sizeof(phdr));
|
||||
|
||||
uint64_t load_bias = 0;
|
||||
ASSERT_TRUE(elf->Init(&load_bias));
|
||||
EXPECT_EQ(0U, load_bias);
|
||||
EXPECT_TRUE(elf->IsValidPc(0));
|
||||
EXPECT_TRUE(elf->IsValidPc(0x5000));
|
||||
EXPECT_TRUE(elf->IsValidPc(0xffff));
|
||||
EXPECT_FALSE(elf->IsValidPc(0x10000));
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, is_valid_pc_from_pt_load_non_zero_load_bias) {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
|
||||
|
||||
Elf32_Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
ehdr.e_phoff = 0x100;
|
||||
ehdr.e_phnum = 1;
|
||||
ehdr.e_phentsize = sizeof(Elf32_Phdr);
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Elf32_Phdr phdr;
|
||||
memset(&phdr, 0, sizeof(phdr));
|
||||
phdr.p_type = PT_LOAD;
|
||||
phdr.p_vaddr = 0x2000;
|
||||
phdr.p_memsz = 0x10000;
|
||||
phdr.p_flags = PF_R | PF_X;
|
||||
phdr.p_align = 0x1000;
|
||||
memory_.SetMemory(0x100, &phdr, sizeof(phdr));
|
||||
|
||||
uint64_t load_bias = 0;
|
||||
ASSERT_TRUE(elf->Init(&load_bias));
|
||||
EXPECT_EQ(0x2000U, load_bias);
|
||||
EXPECT_FALSE(elf->IsValidPc(0));
|
||||
EXPECT_FALSE(elf->IsValidPc(0x1000));
|
||||
EXPECT_FALSE(elf->IsValidPc(0x1fff));
|
||||
EXPECT_TRUE(elf->IsValidPc(0x2000));
|
||||
EXPECT_TRUE(elf->IsValidPc(0x5000));
|
||||
EXPECT_TRUE(elf->IsValidPc(0x11fff));
|
||||
EXPECT_FALSE(elf->IsValidPc(0x12000));
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, is_valid_pc_from_debug_frame) {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
|
||||
|
||||
uint64_t sh_offset = 0x100;
|
||||
|
||||
Elf32_Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
ehdr.e_shstrndx = 1;
|
||||
ehdr.e_shoff = sh_offset;
|
||||
ehdr.e_shentsize = sizeof(Elf32_Shdr);
|
||||
ehdr.e_shnum = 3;
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Elf32_Shdr shdr;
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
shdr.sh_type = SHT_NULL;
|
||||
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
|
||||
|
||||
sh_offset += sizeof(shdr);
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
shdr.sh_type = SHT_STRTAB;
|
||||
shdr.sh_name = 1;
|
||||
shdr.sh_offset = 0x500;
|
||||
shdr.sh_size = 0x100;
|
||||
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
|
||||
memory_.SetMemory(0x500, ".debug_frame");
|
||||
|
||||
sh_offset += sizeof(shdr);
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
shdr.sh_type = SHT_PROGBITS;
|
||||
shdr.sh_name = 0;
|
||||
shdr.sh_addr = 0x600;
|
||||
shdr.sh_offset = 0x600;
|
||||
shdr.sh_size = 0x200;
|
||||
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
|
||||
|
||||
// CIE 32.
|
||||
memory_.SetData32(0x600, 0xfc);
|
||||
memory_.SetData32(0x604, 0xffffffff);
|
||||
memory_.SetData8(0x608, 1);
|
||||
memory_.SetData8(0x609, '\0');
|
||||
memory_.SetData8(0x60a, 0x4);
|
||||
memory_.SetData8(0x60b, 0x4);
|
||||
memory_.SetData8(0x60c, 0x1);
|
||||
|
||||
// FDE 32.
|
||||
memory_.SetData32(0x700, 0xfc);
|
||||
memory_.SetData32(0x704, 0);
|
||||
memory_.SetData32(0x708, 0x2100);
|
||||
memory_.SetData32(0x70c, 0x200);
|
||||
|
||||
uint64_t load_bias = 0;
|
||||
ASSERT_TRUE(elf->Init(&load_bias));
|
||||
elf->InitHeaders();
|
||||
EXPECT_EQ(0U, load_bias);
|
||||
EXPECT_FALSE(elf->IsValidPc(0));
|
||||
EXPECT_FALSE(elf->IsValidPc(0x20ff));
|
||||
EXPECT_TRUE(elf->IsValidPc(0x2100));
|
||||
EXPECT_TRUE(elf->IsValidPc(0x2200));
|
||||
EXPECT_TRUE(elf->IsValidPc(0x22ff));
|
||||
EXPECT_FALSE(elf->IsValidPc(0x2300));
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceTest, is_valid_pc_from_eh_frame) {
|
||||
std::unique_ptr<ElfInterface> elf(new ElfInterface32(&memory_));
|
||||
|
||||
uint64_t sh_offset = 0x100;
|
||||
|
||||
Elf32_Ehdr ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
ehdr.e_shstrndx = 1;
|
||||
ehdr.e_shoff = sh_offset;
|
||||
ehdr.e_shentsize = sizeof(Elf32_Shdr);
|
||||
ehdr.e_shnum = 3;
|
||||
memory_.SetMemory(0, &ehdr, sizeof(ehdr));
|
||||
|
||||
Elf32_Shdr shdr;
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
shdr.sh_type = SHT_NULL;
|
||||
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
|
||||
|
||||
sh_offset += sizeof(shdr);
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
shdr.sh_type = SHT_STRTAB;
|
||||
shdr.sh_name = 1;
|
||||
shdr.sh_offset = 0x500;
|
||||
shdr.sh_size = 0x100;
|
||||
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
|
||||
memory_.SetMemory(0x500, ".eh_frame");
|
||||
|
||||
sh_offset += sizeof(shdr);
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
shdr.sh_type = SHT_PROGBITS;
|
||||
shdr.sh_name = 0;
|
||||
shdr.sh_addr = 0x600;
|
||||
shdr.sh_offset = 0x600;
|
||||
shdr.sh_size = 0x200;
|
||||
memory_.SetMemory(sh_offset, &shdr, sizeof(shdr));
|
||||
|
||||
// CIE 32.
|
||||
memory_.SetData32(0x600, 0xfc);
|
||||
memory_.SetData32(0x604, 0);
|
||||
memory_.SetData8(0x608, 1);
|
||||
memory_.SetData8(0x609, '\0');
|
||||
memory_.SetData8(0x60a, 0x4);
|
||||
memory_.SetData8(0x60b, 0x4);
|
||||
memory_.SetData8(0x60c, 0x1);
|
||||
|
||||
// FDE 32.
|
||||
memory_.SetData32(0x700, 0xfc);
|
||||
memory_.SetData32(0x704, 0x104);
|
||||
memory_.SetData32(0x708, 0x20f8);
|
||||
memory_.SetData32(0x70c, 0x200);
|
||||
|
||||
uint64_t load_bias = 0;
|
||||
ASSERT_TRUE(elf->Init(&load_bias));
|
||||
elf->InitHeaders();
|
||||
EXPECT_EQ(0U, load_bias);
|
||||
EXPECT_FALSE(elf->IsValidPc(0));
|
||||
EXPECT_FALSE(elf->IsValidPc(0x27ff));
|
||||
EXPECT_TRUE(elf->IsValidPc(0x2800));
|
||||
EXPECT_TRUE(elf->IsValidPc(0x2900));
|
||||
EXPECT_TRUE(elf->IsValidPc(0x29ff));
|
||||
EXPECT_FALSE(elf->IsValidPc(0x2a00));
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -346,7 +346,14 @@ class ElfInterfaceMock : public ElfInterface {
|
|||
void InitHeaders() override {}
|
||||
bool GetSoname(std::string*) override { return false; }
|
||||
bool GetFunctionName(uint64_t, uint64_t, std::string*, uint64_t*) override { return false; }
|
||||
|
||||
MOCK_METHOD5(Step, bool(uint64_t, uint64_t, Regs*, Memory*, bool*));
|
||||
MOCK_METHOD2(GetGlobalVariable, bool(const std::string&, uint64_t*));
|
||||
MOCK_METHOD1(IsValidPc, bool(uint64_t));
|
||||
|
||||
void MockSetDynamicOffset(uint64_t offset) { dynamic_offset_ = offset; }
|
||||
void MockSetDynamicVaddr(uint64_t vaddr) { dynamic_vaddr_ = vaddr; }
|
||||
void MockSetDynamicSize(uint64_t size) { dynamic_size_ = size; }
|
||||
};
|
||||
|
||||
TEST_F(ElfTest, step_in_interface) {
|
||||
|
@ -378,14 +385,200 @@ TEST_F(ElfTest, step_in_interface_non_zero_load_bias) {
|
|||
elf.FakeSetInterface(interface);
|
||||
MemoryFake process_memory;
|
||||
|
||||
// Invalid relative pc given load_bias.
|
||||
bool finished;
|
||||
ASSERT_FALSE(elf.Step(0x1004, 0x1000, 0x2000, ®s, &process_memory, &finished));
|
||||
|
||||
EXPECT_CALL(*interface, Step(0x7300, 0x4000, ®s, &process_memory, &finished))
|
||||
.WillOnce(::testing::Return(true));
|
||||
|
||||
ASSERT_TRUE(elf.Step(0x7304, 0x7300, 0x2000, ®s, &process_memory, &finished));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, get_global_invalid_elf) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(false);
|
||||
|
||||
std::string global("something");
|
||||
uint64_t offset;
|
||||
ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, get_global_valid_not_in_interface) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0);
|
||||
|
||||
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
|
||||
elf.FakeSetInterface(interface);
|
||||
|
||||
uint64_t offset;
|
||||
std::string global("something");
|
||||
EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
|
||||
|
||||
ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, get_global_valid_below_load_bias) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0x1000);
|
||||
|
||||
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
|
||||
elf.FakeSetInterface(interface);
|
||||
|
||||
uint64_t offset;
|
||||
std::string global("something");
|
||||
EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
|
||||
.WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
|
||||
|
||||
ASSERT_FALSE(elf.GetGlobalVariable(global, &offset));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, get_global_valid_dynamic_zero) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0);
|
||||
|
||||
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
|
||||
elf.FakeSetInterface(interface);
|
||||
|
||||
ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
|
||||
elf.FakeSetGnuDebugdataInterface(gnu_interface);
|
||||
|
||||
uint64_t offset;
|
||||
std::string global("something");
|
||||
EXPECT_CALL(*interface, GetGlobalVariable(global, &offset)).WillOnce(::testing::Return(false));
|
||||
|
||||
EXPECT_CALL(*gnu_interface, GetGlobalVariable(global, &offset))
|
||||
.WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x500), ::testing::Return(true)));
|
||||
|
||||
ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
|
||||
EXPECT_EQ(0x500U, offset);
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, get_global_valid_in_gnu_debugdata_dynamic_zero) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0);
|
||||
|
||||
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
|
||||
elf.FakeSetInterface(interface);
|
||||
|
||||
uint64_t offset;
|
||||
std::string global("something");
|
||||
EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
|
||||
.WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
|
||||
|
||||
ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
|
||||
EXPECT_EQ(0x300U, offset);
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, get_global_valid_dynamic_zero_non_zero_load_bias) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0x100);
|
||||
|
||||
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
|
||||
elf.FakeSetInterface(interface);
|
||||
|
||||
uint64_t offset;
|
||||
std::string global("something");
|
||||
EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
|
||||
.WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x300), ::testing::Return(true)));
|
||||
|
||||
ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
|
||||
EXPECT_EQ(0x200U, offset);
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, get_global_valid_dynamic_adjust_negative) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0);
|
||||
|
||||
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
|
||||
interface->MockSetDynamicOffset(0x400);
|
||||
interface->MockSetDynamicVaddr(0x800);
|
||||
interface->MockSetDynamicSize(0x100);
|
||||
elf.FakeSetInterface(interface);
|
||||
|
||||
uint64_t offset;
|
||||
std::string global("something");
|
||||
EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
|
||||
.WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
|
||||
|
||||
ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
|
||||
EXPECT_EQ(0x450U, offset);
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, get_global_valid_dynamic_adjust_positive) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0);
|
||||
|
||||
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
|
||||
interface->MockSetDynamicOffset(0x1000);
|
||||
interface->MockSetDynamicVaddr(0x800);
|
||||
interface->MockSetDynamicSize(0x100);
|
||||
elf.FakeSetInterface(interface);
|
||||
|
||||
uint64_t offset;
|
||||
std::string global("something");
|
||||
EXPECT_CALL(*interface, GetGlobalVariable(global, &offset))
|
||||
.WillOnce(::testing::DoAll(::testing::SetArgPointee<1>(0x850), ::testing::Return(true)));
|
||||
|
||||
ASSERT_TRUE(elf.GetGlobalVariable(global, &offset));
|
||||
EXPECT_EQ(0x1050U, offset);
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, is_valid_pc_elf_invalid) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(false);
|
||||
elf.FakeSetLoadBias(0);
|
||||
|
||||
EXPECT_FALSE(elf.IsValidPc(0x100));
|
||||
EXPECT_FALSE(elf.IsValidPc(0x200));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, is_valid_pc_interface) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0);
|
||||
|
||||
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
|
||||
elf.FakeSetInterface(interface);
|
||||
|
||||
EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
|
||||
|
||||
EXPECT_TRUE(elf.IsValidPc(0x1500));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, is_valid_pc_non_zero_load_bias) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0x1000);
|
||||
|
||||
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
|
||||
elf.FakeSetInterface(interface);
|
||||
|
||||
EXPECT_CALL(*interface, IsValidPc(0x500)).WillOnce(::testing::Return(true));
|
||||
|
||||
EXPECT_FALSE(elf.IsValidPc(0x100));
|
||||
EXPECT_FALSE(elf.IsValidPc(0x200));
|
||||
EXPECT_TRUE(elf.IsValidPc(0x1500));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, is_valid_pc_from_gnu_debugdata) {
|
||||
ElfFake elf(memory_);
|
||||
elf.FakeSetValid(true);
|
||||
elf.FakeSetLoadBias(0);
|
||||
|
||||
ElfInterfaceMock* interface = new ElfInterfaceMock(memory_);
|
||||
elf.FakeSetInterface(interface);
|
||||
ElfInterfaceMock* gnu_interface = new ElfInterfaceMock(memory_);
|
||||
elf.FakeSetGnuDebugdataInterface(gnu_interface);
|
||||
|
||||
EXPECT_CALL(*interface, IsValidPc(0x1500)).WillOnce(::testing::Return(false));
|
||||
EXPECT_CALL(*gnu_interface, IsValidPc(0x1500)).WillOnce(::testing::Return(true));
|
||||
|
||||
EXPECT_TRUE(elf.IsValidPc(0x1500));
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* Copyright (C) 2018 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 <elf.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
|
||||
#include "ElfFake.h"
|
||||
#include "MemoryFake.h"
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
class JitDebugTest : public ::testing::Test {
|
||||
protected:
|
||||
void SetUp() override {
|
||||
memory_ = new MemoryFake;
|
||||
process_memory_.reset(memory_);
|
||||
|
||||
jit_debug_.reset(new JitDebug(process_memory_));
|
||||
jit_debug_->SetArch(ARCH_ARM);
|
||||
|
||||
maps_.reset(
|
||||
new BufferMaps("1000-4000 ---s 00000000 00:00 0\n"
|
||||
"4000-6000 r--s 00000000 00:00 0\n"
|
||||
"6000-8000 -w-s 00000000 00:00 0\n"
|
||||
"a000-c000 --xp 00000000 00:00 0\n"
|
||||
"c000-f000 rwxp 00000000 00:00 0\n"
|
||||
"f000-11000 r-xp 00000000 00:00 0\n"
|
||||
"100000-110000 rw-p 0000000 00:00 0\n"
|
||||
"200000-210000 rw-p 0000000 00:00 0\n"));
|
||||
ASSERT_TRUE(maps_->Parse());
|
||||
|
||||
MapInfo* map_info = maps_->Get(3);
|
||||
ASSERT_TRUE(map_info != nullptr);
|
||||
elf_memories_.push_back(new MemoryFake);
|
||||
ElfFake* elf = new ElfFake(elf_memories_.back());
|
||||
elf->FakeSetValid(true);
|
||||
ElfInterfaceFake* interface = new ElfInterfaceFake(elf_memories_.back());
|
||||
elf->FakeSetInterface(interface);
|
||||
interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
|
||||
map_info->elf = elf;
|
||||
|
||||
map_info = maps_->Get(5);
|
||||
ASSERT_TRUE(map_info != nullptr);
|
||||
elf_memories_.push_back(new MemoryFake);
|
||||
elf = new ElfFake(elf_memories_.back());
|
||||
elf->FakeSetValid(true);
|
||||
interface = new ElfInterfaceFake(elf_memories_.back());
|
||||
elf->FakeSetInterface(interface);
|
||||
interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
|
||||
map_info->elf = elf;
|
||||
}
|
||||
|
||||
template <typename EhdrType, typename ShdrType>
|
||||
void CreateElf(uint64_t offset, uint8_t class_type, uint8_t machine_type, uint32_t pc,
|
||||
uint32_t size) {
|
||||
EhdrType ehdr;
|
||||
memset(&ehdr, 0, sizeof(ehdr));
|
||||
uint64_t sh_offset = sizeof(ehdr);
|
||||
memcpy(ehdr.e_ident, ELFMAG, SELFMAG);
|
||||
ehdr.e_ident[EI_CLASS] = class_type;
|
||||
ehdr.e_machine = machine_type;
|
||||
ehdr.e_shstrndx = 1;
|
||||
ehdr.e_shoff = sh_offset;
|
||||
ehdr.e_shentsize = sizeof(ShdrType);
|
||||
ehdr.e_shnum = 3;
|
||||
memory_->SetMemory(offset, &ehdr, sizeof(ehdr));
|
||||
|
||||
ShdrType shdr;
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
shdr.sh_type = SHT_NULL;
|
||||
memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
|
||||
|
||||
sh_offset += sizeof(shdr);
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
shdr.sh_type = SHT_STRTAB;
|
||||
shdr.sh_name = 1;
|
||||
shdr.sh_offset = 0x500;
|
||||
shdr.sh_size = 0x100;
|
||||
memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
|
||||
memory_->SetMemory(offset + 0x500, ".debug_frame");
|
||||
|
||||
sh_offset += sizeof(shdr);
|
||||
memset(&shdr, 0, sizeof(shdr));
|
||||
shdr.sh_type = SHT_PROGBITS;
|
||||
shdr.sh_name = 0;
|
||||
shdr.sh_addr = 0x600;
|
||||
shdr.sh_offset = 0x600;
|
||||
shdr.sh_size = 0x200;
|
||||
memory_->SetMemory(offset + sh_offset, &shdr, sizeof(shdr));
|
||||
|
||||
// Now add a single cie/fde.
|
||||
uint64_t dwarf_offset = offset + 0x600;
|
||||
if (class_type == ELFCLASS32) {
|
||||
// CIE 32 information.
|
||||
memory_->SetData32(dwarf_offset, 0xfc);
|
||||
memory_->SetData32(dwarf_offset + 0x4, 0xffffffff);
|
||||
memory_->SetData8(dwarf_offset + 0x8, 1);
|
||||
memory_->SetData8(dwarf_offset + 0x9, '\0');
|
||||
memory_->SetData8(dwarf_offset + 0xa, 0x4);
|
||||
memory_->SetData8(dwarf_offset + 0xb, 0x4);
|
||||
memory_->SetData8(dwarf_offset + 0xc, 0x1);
|
||||
|
||||
// FDE 32 information.
|
||||
memory_->SetData32(dwarf_offset + 0x100, 0xfc);
|
||||
memory_->SetData32(dwarf_offset + 0x104, 0);
|
||||
memory_->SetData32(dwarf_offset + 0x108, pc);
|
||||
memory_->SetData32(dwarf_offset + 0x10c, size);
|
||||
} else {
|
||||
// CIE 64 information.
|
||||
memory_->SetData32(dwarf_offset, 0xffffffff);
|
||||
memory_->SetData64(dwarf_offset + 4, 0xf4);
|
||||
memory_->SetData64(dwarf_offset + 0xc, 0xffffffffffffffffULL);
|
||||
memory_->SetData8(dwarf_offset + 0x14, 1);
|
||||
memory_->SetData8(dwarf_offset + 0x15, '\0');
|
||||
memory_->SetData8(dwarf_offset + 0x16, 0x4);
|
||||
memory_->SetData8(dwarf_offset + 0x17, 0x4);
|
||||
memory_->SetData8(dwarf_offset + 0x18, 0x1);
|
||||
|
||||
// FDE 64 information.
|
||||
memory_->SetData32(dwarf_offset + 0x100, 0xffffffff);
|
||||
memory_->SetData64(dwarf_offset + 0x104, 0xf4);
|
||||
memory_->SetData64(dwarf_offset + 0x10c, 0);
|
||||
memory_->SetData64(dwarf_offset + 0x114, pc);
|
||||
memory_->SetData64(dwarf_offset + 0x11c, size);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteDescriptor32(uint64_t addr, uint32_t entry);
|
||||
void WriteDescriptor64(uint64_t addr, uint64_t entry);
|
||||
void WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
|
||||
uint64_t elf_size);
|
||||
void WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
|
||||
uint64_t elf_size);
|
||||
void WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
|
||||
uint64_t elf_size);
|
||||
|
||||
std::shared_ptr<Memory> process_memory_;
|
||||
MemoryFake* memory_;
|
||||
std::vector<MemoryFake*> elf_memories_;
|
||||
std::unique_ptr<JitDebug> jit_debug_;
|
||||
std::unique_ptr<BufferMaps> maps_;
|
||||
};
|
||||
|
||||
void JitDebugTest::WriteDescriptor32(uint64_t addr, uint32_t entry) {
|
||||
// Format of the 32 bit JITDescriptor structure:
|
||||
// uint32_t version
|
||||
memory_->SetData32(addr, 1);
|
||||
// uint32_t action_flag
|
||||
memory_->SetData32(addr + 4, 0);
|
||||
// uint32_t relevant_entry
|
||||
memory_->SetData32(addr + 8, 0);
|
||||
// uint32_t first_entry
|
||||
memory_->SetData32(addr + 12, entry);
|
||||
}
|
||||
|
||||
void JitDebugTest::WriteDescriptor64(uint64_t addr, uint64_t entry) {
|
||||
// Format of the 64 bit JITDescriptor structure:
|
||||
// uint32_t version
|
||||
memory_->SetData32(addr, 1);
|
||||
// uint32_t action_flag
|
||||
memory_->SetData32(addr + 4, 0);
|
||||
// uint64_t relevant_entry
|
||||
memory_->SetData64(addr + 8, 0);
|
||||
// uint64_t first_entry
|
||||
memory_->SetData64(addr + 16, entry);
|
||||
}
|
||||
|
||||
void JitDebugTest::WriteEntry32Pack(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
|
||||
uint64_t elf_size) {
|
||||
// Format of the 32 bit JITCodeEntry structure:
|
||||
// uint32_t next
|
||||
memory_->SetData32(addr, next);
|
||||
// uint32_t prev
|
||||
memory_->SetData32(addr + 4, prev);
|
||||
// uint32_t symfile_addr
|
||||
memory_->SetData32(addr + 8, elf_addr);
|
||||
// uint64_t symfile_size
|
||||
memory_->SetData64(addr + 12, elf_size);
|
||||
}
|
||||
|
||||
void JitDebugTest::WriteEntry32Pad(uint64_t addr, uint32_t prev, uint32_t next, uint32_t elf_addr,
|
||||
uint64_t elf_size) {
|
||||
// Format of the 32 bit JITCodeEntry structure:
|
||||
// uint32_t next
|
||||
memory_->SetData32(addr, next);
|
||||
// uint32_t prev
|
||||
memory_->SetData32(addr + 4, prev);
|
||||
// uint32_t symfile_addr
|
||||
memory_->SetData32(addr + 8, elf_addr);
|
||||
// uint32_t pad
|
||||
memory_->SetData32(addr + 12, 0);
|
||||
// uint64_t symfile_size
|
||||
memory_->SetData64(addr + 16, elf_size);
|
||||
}
|
||||
|
||||
void JitDebugTest::WriteEntry64(uint64_t addr, uint64_t prev, uint64_t next, uint64_t elf_addr,
|
||||
uint64_t elf_size) {
|
||||
// Format of the 64 bit JITCodeEntry structure:
|
||||
// uint64_t next
|
||||
memory_->SetData64(addr, next);
|
||||
// uint64_t prev
|
||||
memory_->SetData64(addr + 8, prev);
|
||||
// uint64_t symfile_addr
|
||||
memory_->SetData64(addr + 16, elf_addr);
|
||||
// uint64_t symfile_size
|
||||
memory_->SetData64(addr + 24, elf_size);
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_invalid) {
|
||||
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_no_global_variable) {
|
||||
maps_.reset(new BufferMaps(""));
|
||||
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_no_valid_descriptor_in_memory) {
|
||||
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
|
||||
|
||||
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_no_valid_code_entry) {
|
||||
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
|
||||
|
||||
WriteDescriptor32(0xf800, 0x200000);
|
||||
|
||||
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_invalid_descriptor_first_entry) {
|
||||
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
|
||||
|
||||
WriteDescriptor32(0xf800, 0);
|
||||
|
||||
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_invalid_descriptor_version) {
|
||||
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
|
||||
|
||||
WriteDescriptor32(0xf800, 0x20000);
|
||||
// Set the version to an invalid value.
|
||||
memory_->SetData32(0xf800, 2);
|
||||
|
||||
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf == nullptr);
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_32) {
|
||||
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
|
||||
|
||||
WriteDescriptor32(0xf800, 0x200000);
|
||||
WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
|
||||
|
||||
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf != nullptr);
|
||||
|
||||
// Clear the memory and verify all of the data is cached.
|
||||
memory_->Clear();
|
||||
Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf2 != nullptr);
|
||||
EXPECT_EQ(elf, elf2);
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_x86) {
|
||||
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
|
||||
|
||||
WriteDescriptor32(0xf800, 0x200000);
|
||||
WriteEntry32Pack(0x200000, 0, 0, 0x4000, 0x1000);
|
||||
|
||||
jit_debug_->SetArch(ARCH_X86);
|
||||
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf != nullptr);
|
||||
|
||||
// Clear the memory and verify all of the data is cached.
|
||||
memory_->Clear();
|
||||
Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf2 != nullptr);
|
||||
EXPECT_EQ(elf, elf2);
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_64) {
|
||||
CreateElf<Elf64_Ehdr, Elf64_Shdr>(0x4000, ELFCLASS64, EM_AARCH64, 0x1500, 0x200);
|
||||
|
||||
WriteDescriptor64(0xf800, 0x200000);
|
||||
WriteEntry64(0x200000, 0, 0, 0x4000, 0x1000);
|
||||
|
||||
jit_debug_->SetArch(ARCH_ARM64);
|
||||
Elf* elf = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf != nullptr);
|
||||
|
||||
// Clear the memory and verify all of the data is cached.
|
||||
memory_->Clear();
|
||||
Elf* elf2 = jit_debug_->GetElf(maps_.get(), 0x1500);
|
||||
ASSERT_TRUE(elf2 != nullptr);
|
||||
EXPECT_EQ(elf, elf2);
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_multiple_entries) {
|
||||
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
|
||||
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x5000, ELFCLASS32, EM_ARM, 0x2300, 0x400);
|
||||
|
||||
WriteDescriptor32(0xf800, 0x200000);
|
||||
WriteEntry32Pad(0x200000, 0, 0x200100, 0x4000, 0x1000);
|
||||
WriteEntry32Pad(0x200100, 0x200100, 0, 0x5000, 0x1000);
|
||||
|
||||
Elf* elf_2 = jit_debug_->GetElf(maps_.get(), 0x2400);
|
||||
ASSERT_TRUE(elf_2 != nullptr);
|
||||
|
||||
Elf* elf_1 = jit_debug_->GetElf(maps_.get(), 0x1600);
|
||||
ASSERT_TRUE(elf_1 != nullptr);
|
||||
|
||||
// Clear the memory and verify all of the data is cached.
|
||||
memory_->Clear();
|
||||
EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x1500));
|
||||
EXPECT_EQ(elf_1, jit_debug_->GetElf(maps_.get(), 0x16ff));
|
||||
EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x2300));
|
||||
EXPECT_EQ(elf_2, jit_debug_->GetElf(maps_.get(), 0x26ff));
|
||||
EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x1700));
|
||||
EXPECT_EQ(nullptr, jit_debug_->GetElf(maps_.get(), 0x2700));
|
||||
}
|
||||
|
||||
TEST_F(JitDebugTest, get_elf_search_libs) {
|
||||
CreateElf<Elf32_Ehdr, Elf32_Shdr>(0x4000, ELFCLASS32, EM_ARM, 0x1500, 0x200);
|
||||
|
||||
WriteDescriptor32(0xf800, 0x200000);
|
||||
WriteEntry32Pad(0x200000, 0, 0, 0x4000, 0x1000);
|
||||
|
||||
// Only search a given named list of libs.
|
||||
std::vector<std::string> libs{"libart.so"};
|
||||
jit_debug_.reset(new JitDebug(process_memory_, libs));
|
||||
jit_debug_->SetArch(ARCH_ARM);
|
||||
EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) == nullptr);
|
||||
|
||||
// Change the name of the map that includes the value and verify this works.
|
||||
MapInfo* map_info = maps_->Get(5);
|
||||
map_info->name = "/system/lib/libart.so";
|
||||
jit_debug_.reset(new JitDebug(process_memory_, libs));
|
||||
// Make sure that clearing our copy of the libs doesn't affect the
|
||||
// JitDebug object.
|
||||
libs.clear();
|
||||
jit_debug_->SetArch(ARCH_ARM);
|
||||
EXPECT_TRUE(jit_debug_->GetElf(maps_.get(), 0x1500) != nullptr);
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
|
@ -45,7 +45,7 @@ class RegsFake : public Regs {
|
|||
|
||||
void IterateRegisters(std::function<void(const char*, uint64_t)>) override {}
|
||||
|
||||
bool Format32Bit() { return false; }
|
||||
bool Is32Bit() { return false; }
|
||||
|
||||
uint64_t GetAdjustedPc(uint64_t rel_pc, Elf*) override { return rel_pc - 2; }
|
||||
|
||||
|
|
|
@ -188,7 +188,7 @@ TEST_F(RegsTest, elf_invalid) {
|
|||
|
||||
regs_arm.set_pc(0x1500);
|
||||
EXPECT_EQ(0x500U, invalid_elf->GetRelPc(regs_arm.pc(), &map_info));
|
||||
EXPECT_EQ(0x500U, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
|
||||
EXPECT_EQ(0x4fcU, regs_arm.GetAdjustedPc(0x500U, invalid_elf));
|
||||
|
||||
regs_arm64.set_pc(0x1600);
|
||||
EXPECT_EQ(0x600U, invalid_elf->GetRelPc(regs_arm64.pc(), &map_info));
|
||||
|
|
|
@ -330,9 +330,69 @@ TYPED_TEST_P(SymbolsTest, symtab_read_cached) {
|
|||
ASSERT_EQ(3U, func_offset);
|
||||
}
|
||||
|
||||
TYPED_TEST_P(SymbolsTest, get_global) {
|
||||
uint64_t start_offset = 0x1000;
|
||||
uint64_t str_offset = 0xa000;
|
||||
Symbols symbols(start_offset, 4 * sizeof(TypeParam), sizeof(TypeParam), str_offset, 0x1000);
|
||||
|
||||
TypeParam sym;
|
||||
memset(&sym, 0, sizeof(sym));
|
||||
sym.st_shndx = SHN_COMMON;
|
||||
sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
|
||||
sym.st_name = 0x100;
|
||||
this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
|
||||
this->memory_.SetMemory(str_offset + 0x100, "global_0");
|
||||
|
||||
start_offset += sizeof(sym);
|
||||
memset(&sym, 0, sizeof(sym));
|
||||
sym.st_shndx = SHN_COMMON;
|
||||
sym.st_info = STT_FUNC;
|
||||
sym.st_name = 0x200;
|
||||
sym.st_value = 0x10000;
|
||||
sym.st_size = 0x100;
|
||||
this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
|
||||
this->memory_.SetMemory(str_offset + 0x200, "function_0");
|
||||
|
||||
start_offset += sizeof(sym);
|
||||
memset(&sym, 0, sizeof(sym));
|
||||
sym.st_shndx = SHN_COMMON;
|
||||
sym.st_info = STT_OBJECT | (STB_GLOBAL << 4);
|
||||
sym.st_name = 0x300;
|
||||
this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
|
||||
this->memory_.SetMemory(str_offset + 0x300, "global_1");
|
||||
|
||||
start_offset += sizeof(sym);
|
||||
memset(&sym, 0, sizeof(sym));
|
||||
sym.st_shndx = SHN_COMMON;
|
||||
sym.st_info = STT_FUNC;
|
||||
sym.st_name = 0x400;
|
||||
sym.st_value = 0x12000;
|
||||
sym.st_size = 0x100;
|
||||
this->memory_.SetMemory(start_offset, &sym, sizeof(sym));
|
||||
this->memory_.SetMemory(str_offset + 0x400, "function_1");
|
||||
|
||||
uint64_t offset;
|
||||
EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
|
||||
EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
|
||||
EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_0", &offset));
|
||||
EXPECT_TRUE(symbols.GetGlobal<TypeParam>(&this->memory_, "global_1", &offset));
|
||||
|
||||
EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_0", &offset));
|
||||
EXPECT_FALSE(symbols.GetGlobal<TypeParam>(&this->memory_, "function_1", &offset));
|
||||
|
||||
std::string name;
|
||||
EXPECT_TRUE(symbols.GetName<TypeParam>(0x10002, 0, &this->memory_, &name, &offset));
|
||||
EXPECT_EQ("function_0", name);
|
||||
EXPECT_EQ(2U, offset);
|
||||
|
||||
EXPECT_TRUE(symbols.GetName<TypeParam>(0x12004, 0, &this->memory_, &name, &offset));
|
||||
EXPECT_EQ("function_1", name);
|
||||
EXPECT_EQ(4U, offset);
|
||||
}
|
||||
|
||||
REGISTER_TYPED_TEST_CASE_P(SymbolsTest, function_bounds_check, no_symbol, multiple_entries,
|
||||
multiple_entries_nonstandard_size, load_bias, symtab_value_out_of_bounds,
|
||||
symtab_read_cached);
|
||||
symtab_read_cached, get_global);
|
||||
|
||||
typedef ::testing::Types<Elf32_Sym, Elf64_Sym> SymbolsTestTypes;
|
||||
INSTANTIATE_TYPED_TEST_CASE_P(, SymbolsTest, SymbolsTestTypes);
|
||||
|
|
|
@ -25,14 +25,17 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/RegsArm.h>
|
||||
#include <unwindstack/RegsArm64.h>
|
||||
#include <unwindstack/RegsX86.h>
|
||||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
#include "MachineArm.h"
|
||||
#include "MachineArm64.h"
|
||||
#include "MachineX86.h"
|
||||
|
||||
#include "ElfTestUtils.h"
|
||||
|
||||
|
@ -92,7 +95,7 @@ TEST(UnwindOfflineTest, pc_straddle_arm32) {
|
|||
" #00 pc 0001a9f8 libc.so (abort+63)\n"
|
||||
" #01 pc 00006a1b libbase.so (_ZN7android4base14DefaultAborterEPKc+6)\n"
|
||||
" #02 pc 00007441 libbase.so (_ZN7android4base10LogMessageD2Ev+748)\n"
|
||||
" #03 pc 00015149 /does/not/exist/libhidlbase.so\n",
|
||||
" #03 pc 00015147 /does/not/exist/libhidlbase.so\n",
|
||||
frame_info);
|
||||
}
|
||||
|
||||
|
@ -200,4 +203,207 @@ TEST(UnwindOfflineTest, pc_straddle_arm64) {
|
|||
frame_info);
|
||||
}
|
||||
|
||||
static void AddMemory(std::string file_name, MemoryOfflineParts* parts) {
|
||||
MemoryOffline* memory = new MemoryOffline;
|
||||
ASSERT_TRUE(memory->Init(file_name.c_str(), 0));
|
||||
parts->Add(memory);
|
||||
}
|
||||
|
||||
TEST(UnwindOfflineTest, jit_debug_x86_32) {
|
||||
std::string dir(TestGetFileDirectory() + "offline/jit_debug_x86_32/");
|
||||
|
||||
MemoryOfflineParts* memory = new MemoryOfflineParts;
|
||||
AddMemory(dir + "descriptor.data", memory);
|
||||
AddMemory(dir + "stack.data", memory);
|
||||
for (size_t i = 0; i < 7; i++) {
|
||||
AddMemory(dir + "entry" + std::to_string(i) + ".data", memory);
|
||||
AddMemory(dir + "jit" + std::to_string(i) + ".data", memory);
|
||||
}
|
||||
|
||||
FILE* fp = fopen((dir + "regs.txt").c_str(), "r");
|
||||
ASSERT_TRUE(fp != nullptr);
|
||||
RegsX86 regs;
|
||||
uint64_t reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "eax: %" SCNx64 "\n", ®_value));
|
||||
regs[X86_REG_EAX] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "ebx: %" SCNx64 "\n", ®_value));
|
||||
regs[X86_REG_EBX] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "ecx: %" SCNx64 "\n", ®_value));
|
||||
regs[X86_REG_ECX] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "edx: %" SCNx64 "\n", ®_value));
|
||||
regs[X86_REG_EDX] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "ebp: %" SCNx64 "\n", ®_value));
|
||||
regs[X86_REG_EBP] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "edi: %" SCNx64 "\n", ®_value));
|
||||
regs[X86_REG_EDI] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "esi: %" SCNx64 "\n", ®_value));
|
||||
regs[X86_REG_ESI] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "esp: %" SCNx64 "\n", ®_value));
|
||||
regs[X86_REG_ESP] = reg_value;
|
||||
ASSERT_EQ(1, fscanf(fp, "eip: %" SCNx64 "\n", ®_value));
|
||||
regs[X86_REG_EIP] = reg_value;
|
||||
regs.SetFromRaw();
|
||||
fclose(fp);
|
||||
|
||||
fp = fopen((dir + "maps.txt").c_str(), "r");
|
||||
ASSERT_TRUE(fp != nullptr);
|
||||
// The file is guaranteed to be less than 4096 bytes.
|
||||
std::vector<char> buffer(4096);
|
||||
ASSERT_NE(0U, fread(buffer.data(), 1, buffer.size(), fp));
|
||||
fclose(fp);
|
||||
|
||||
BufferMaps maps(buffer.data());
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
|
||||
ASSERT_EQ(ARCH_X86, regs.Arch());
|
||||
|
||||
std::shared_ptr<Memory> process_memory(memory);
|
||||
|
||||
char* cwd = getcwd(nullptr, 0);
|
||||
ASSERT_EQ(0, chdir(dir.c_str()));
|
||||
JitDebug jit_debug(process_memory);
|
||||
Unwinder unwinder(128, &maps, ®s, process_memory);
|
||||
unwinder.SetJitDebug(&jit_debug, regs.Arch());
|
||||
unwinder.Unwind();
|
||||
ASSERT_EQ(0, chdir(cwd));
|
||||
free(cwd);
|
||||
|
||||
std::string frame_info(DumpFrames(unwinder));
|
||||
ASSERT_EQ(69U, unwinder.NumFrames()) << "Unwind:\n" << frame_info;
|
||||
EXPECT_EQ(
|
||||
" #00 pc 00068fb8 libarttestd.so (_ZN3artL13CauseSegfaultEv+72)\n"
|
||||
" #01 pc 00067f00 libarttestd.so (Java_Main_unwindInProcess+10032)\n"
|
||||
" #02 pc 000021a8 (offset 0x2000) 137-cfi.odex (boolean Main.unwindInProcess(boolean, int, "
|
||||
"boolean)+136)\n"
|
||||
" #03 pc 0000fe81 anonymous:ee74c000 (boolean Main.bar(boolean)+65)\n"
|
||||
" #04 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
|
||||
" #05 pc 00146ab5 libartd.so "
|
||||
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
|
||||
" #06 pc 0039cf0d libartd.so "
|
||||
"(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
|
||||
"11ShadowFrameEtPNS_6JValueE+653)\n"
|
||||
" #07 pc 00392552 libartd.so "
|
||||
"(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
|
||||
"6JValueEb+354)\n"
|
||||
" #08 pc 0039399a libartd.so "
|
||||
"(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
|
||||
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
|
||||
" #09 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
|
||||
" #10 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
|
||||
" #11 pc 0000fe04 anonymous:ee74c000 (int Main.compare(Main, Main)+52)\n"
|
||||
" #12 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
|
||||
" #13 pc 00146ab5 libartd.so "
|
||||
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
|
||||
" #14 pc 0039cf0d libartd.so "
|
||||
"(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
|
||||
"11ShadowFrameEtPNS_6JValueE+653)\n"
|
||||
" #15 pc 00392552 libartd.so "
|
||||
"(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
|
||||
"6JValueEb+354)\n"
|
||||
" #16 pc 0039399a libartd.so "
|
||||
"(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
|
||||
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
|
||||
" #17 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
|
||||
" #18 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
|
||||
" #19 pc 0000fd3c anonymous:ee74c000 (int Main.compare(java.lang.Object, "
|
||||
"java.lang.Object)+108)\n"
|
||||
" #20 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
|
||||
" #21 pc 00146ab5 libartd.so "
|
||||
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
|
||||
" #22 pc 0039cf0d libartd.so "
|
||||
"(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
|
||||
"11ShadowFrameEtPNS_6JValueE+653)\n"
|
||||
" #23 pc 00392552 libartd.so "
|
||||
"(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
|
||||
"6JValueEb+354)\n"
|
||||
" #24 pc 0039399a libartd.so "
|
||||
"(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
|
||||
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
|
||||
" #25 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
|
||||
" #26 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
|
||||
" #27 pc 0000fbdc anonymous:ee74c000 (int "
|
||||
"java.util.Arrays.binarySearch0(java.lang.Object[], int, int, java.lang.Object, "
|
||||
"java.util.Comparator)+332)\n"
|
||||
" #28 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
|
||||
" #29 pc 00146acb libartd.so "
|
||||
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
|
||||
" #30 pc 0039cf0d libartd.so "
|
||||
"(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
|
||||
"11ShadowFrameEtPNS_6JValueE+653)\n"
|
||||
" #31 pc 00392552 libartd.so "
|
||||
"(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
|
||||
"6JValueEb+354)\n"
|
||||
" #32 pc 0039399a libartd.so "
|
||||
"(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
|
||||
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
|
||||
" #33 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
|
||||
" #34 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
|
||||
" #35 pc 0000f625 anonymous:ee74c000 (boolean Main.foo()+165)\n"
|
||||
" #36 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
|
||||
" #37 pc 00146ab5 libartd.so "
|
||||
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
|
||||
" #38 pc 0039cf0d libartd.so "
|
||||
"(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
|
||||
"11ShadowFrameEtPNS_6JValueE+653)\n"
|
||||
" #39 pc 00392552 libartd.so "
|
||||
"(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
|
||||
"6JValueEb+354)\n"
|
||||
" #40 pc 0039399a libartd.so "
|
||||
"(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
|
||||
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
|
||||
" #41 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
|
||||
" #42 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
|
||||
" #43 pc 0000eedc anonymous:ee74c000 (void Main.runPrimary()+60)\n"
|
||||
" #44 pc 006ad4d2 libartd.so (art_quick_invoke_stub+338)\n"
|
||||
" #45 pc 00146ab5 libartd.so "
|
||||
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+885)\n"
|
||||
" #46 pc 0039cf0d libartd.so "
|
||||
"(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
|
||||
"11ShadowFrameEtPNS_6JValueE+653)\n"
|
||||
" #47 pc 00392552 libartd.so "
|
||||
"(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
|
||||
"6JValueEb+354)\n"
|
||||
" #48 pc 0039399a libartd.so "
|
||||
"(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
|
||||
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
|
||||
" #49 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
|
||||
" #50 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
|
||||
" #51 pc 0000ac22 anonymous:ee74c000 (void Main.main(java.lang.String[])+98)\n"
|
||||
" #52 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
|
||||
" #53 pc 00146acb libartd.so "
|
||||
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
|
||||
" #54 pc 0039cf0d libartd.so "
|
||||
"(_ZN3art11interpreter34ArtInterpreterToCompiledCodeBridgeEPNS_6ThreadEPNS_9ArtMethodEPNS_"
|
||||
"11ShadowFrameEtPNS_6JValueE+653)\n"
|
||||
" #55 pc 00392552 libartd.so "
|
||||
"(_ZN3art11interpreterL7ExecuteEPNS_6ThreadERKNS_20CodeItemDataAccessorERNS_11ShadowFrameENS_"
|
||||
"6JValueEb+354)\n"
|
||||
" #56 pc 0039399a libartd.so "
|
||||
"(_ZN3art11interpreter30EnterInterpreterFromEntryPointEPNS_6ThreadERKNS_"
|
||||
"20CodeItemDataAccessorEPNS_11ShadowFrameE+234)\n"
|
||||
" #57 pc 00684362 libartd.so (artQuickToInterpreterBridge+1058)\n"
|
||||
" #58 pc 006b35bd libartd.so (art_quick_to_interpreter_bridge+77)\n"
|
||||
" #59 pc 006ad6a2 libartd.so (art_quick_invoke_static_stub+418)\n"
|
||||
" #60 pc 00146acb libartd.so "
|
||||
"(_ZN3art9ArtMethod6InvokeEPNS_6ThreadEPjjPNS_6JValueEPKc+907)\n"
|
||||
" #61 pc 005aac95 libartd.so "
|
||||
"(_ZN3artL18InvokeWithArgArrayERKNS_33ScopedObjectAccessAlreadyRunnableEPNS_9ArtMethodEPNS_"
|
||||
"8ArgArrayEPNS_6JValueEPKc+85)\n"
|
||||
" #62 pc 005aab5a libartd.so "
|
||||
"(_ZN3art17InvokeWithVarArgsERKNS_33ScopedObjectAccessAlreadyRunnableEP8_jobjectP10_"
|
||||
"jmethodIDPc+362)\n"
|
||||
" #63 pc 0048a3dd libartd.so "
|
||||
"(_ZN3art3JNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+125)\n"
|
||||
" #64 pc 0018448c libartd.so "
|
||||
"(_ZN3art8CheckJNI11CallMethodVEPKcP7_JNIEnvP8_jobjectP7_jclassP10_jmethodIDPcNS_"
|
||||
"9Primitive4TypeENS_10InvokeTypeE+1964)\n"
|
||||
" #65 pc 0017cf06 libartd.so "
|
||||
"(_ZN3art8CheckJNI21CallStaticVoidMethodVEP7_JNIEnvP7_jclassP10_jmethodIDPc+70)\n"
|
||||
" #66 pc 00001d8c dalvikvm32 "
|
||||
"(_ZN7_JNIEnv20CallStaticVoidMethodEP7_jclassP10_jmethodIDz+60)\n"
|
||||
" #67 pc 00001a80 dalvikvm32 (main+1312)\n"
|
||||
" #68 pc 00018275 libc.so\n",
|
||||
frame_info);
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,6 @@
|
|||
56573000-56577000 r-xp 0 00:00 0 dalvikvm32
|
||||
eb833000-eb8cc000 r-xp 0 00:00 0 libarttestd.so
|
||||
ec606000-ec607000 r-xp 2000 00:00 0 137-cfi.odex
|
||||
ee74c000-f074c000 r-xp 0 00:00 0 anonymous:ee74c000
|
||||
f6be1000-f732b000 r-xp 0 00:00 0 libartd.so
|
||||
f734b000-f74fc000 r-xp 0 00:00 0 libc.so
|
|
@ -0,0 +1,9 @@
|
|||
eax: eb8cccd0
|
||||
ebx: eb8cccd0
|
||||
ecx: ff
|
||||
edx: ffeb2ca8
|
||||
ebp: ffeb5298
|
||||
edi: ffeb5c08
|
||||
esi: ffeb5c00
|
||||
esp: ffeb5280
|
||||
eip: eb89bfb8
|
Binary file not shown.
|
@ -27,6 +27,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
@ -90,6 +91,8 @@ void DoUnwind(pid_t pid) {
|
|||
|
||||
auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
|
||||
unwindstack::Unwinder unwinder(128, &remote_maps, regs, process_memory);
|
||||
unwindstack::JitDebug jit_debug(process_memory);
|
||||
unwinder.SetJitDebug(&jit_debug, regs->Arch());
|
||||
unwinder.Unwind();
|
||||
|
||||
// Print the frames.
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/JitDebug.h>
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
@ -191,7 +192,9 @@ int SaveData(pid_t pid) {
|
|||
// elf files are involved.
|
||||
uint64_t sp = regs->sp();
|
||||
auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
|
||||
unwindstack::JitDebug jit_debug(process_memory);
|
||||
unwindstack::Unwinder unwinder(1024, &maps, regs, process_memory);
|
||||
unwinder.SetJitDebug(&jit_debug, regs->Arch());
|
||||
unwinder.Unwind();
|
||||
|
||||
std::unordered_map<uint64_t, map_info_t> maps_by_start;
|
||||
|
@ -214,6 +217,10 @@ int SaveData(pid_t pid) {
|
|||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
|
||||
printf("%s\n", unwinder.FormatFrame(i).c_str());
|
||||
}
|
||||
|
||||
if (!SaveStack(pid, sp, last_sp)) {
|
||||
return 1;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue