Merge "Load dex files from ART-specific data structure."

This commit is contained in:
David Srbecky 2018-02-03 11:29:38 +00:00 committed by Gerrit Code Review
commit 7f5615e8d4
13 changed files with 608 additions and 101 deletions

View file

@ -46,7 +46,7 @@ bool UnwindStackMap::Build() {
std::vector<std::string> search_libs_{"libart.so", "libartd.so"};
jit_debug_.reset(new unwindstack::JitDebug(process_memory_, search_libs_));
#if !defined(NO_LIBDEXFILE_SUPPORT)
dex_files_.reset(new unwindstack::DexFiles(process_memory_));
dex_files_.reset(new unwindstack::DexFiles(process_memory_, search_libs_));
#endif
if (!stack_maps_->Parse()) {

View file

@ -128,6 +128,12 @@ cc_library_static {
"DexFile.cpp",
"DexFiles.cpp",
],
target: {
// Always disable optimizations for host to make it easier to debug.
host: {
cflags: ["-O0", "-g"],
},
},
shared_libs: [
"libbase",
@ -151,8 +157,13 @@ cc_test_library {
vendor_available: false,
defaults: ["libunwindstack_flags"],
shared: {
enabled: false,
},
srcs: [
"tests/DexFileTest.cpp",
"tests/DexFilesTest.cpp",
],
local_include_dirs: ["include"],
allow_undefined_symbols: true,

View file

@ -52,10 +52,20 @@ DexFile* DexFile::Create(uint64_t dex_file_offset_in_memory, Memory* memory, Map
return nullptr;
}
void DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
DexFileFromFile::~DexFileFromFile() {
if (size_ != 0) {
munmap(mapped_memory_, size_);
}
}
bool DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name,
uint64_t* method_offset) {
if (dex_file_ == nullptr) {
return;
return false;
}
if (!dex_file_->IsInDataSection(dex_file_->Begin() + dex_offset)) {
return false; // The DEX offset is not within the bytecode of this dex file.
}
for (uint32_t i = 0; i < dex_file_->NumClassDefs(); ++i) {
@ -82,16 +92,11 @@ void DexFile::GetMethodInformation(uint64_t dex_offset, std::string* method_name
if (offset <= dex_offset && dex_offset < offset + size) {
*method_name = dex_file_->PrettyMethod(it.GetMemberIndex(), false);
*method_offset = dex_offset - offset;
return;
return true;
}
}
}
}
DexFileFromFile::~DexFileFromFile() {
if (size_ != 0) {
munmap(mapped_memory_, size_);
}
return false;
}
bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string& file) {
@ -139,25 +144,41 @@ bool DexFileFromFile::Open(uint64_t dex_file_offset_in_file, const std::string&
}
bool DexFileFromMemory::Open(uint64_t dex_file_offset_in_memory, Memory* memory) {
art::DexFile::Header header;
if (!memory->ReadFully(dex_file_offset_in_memory, &header, sizeof(header))) {
memory_.resize(sizeof(art::DexFile::Header));
if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
return false;
}
if (!art::StandardDexFile::IsMagicValid(header.magic_) &&
!art::CompactDexFile::IsMagicValid(header.magic_)) {
art::DexFile::Header* header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
bool modify_data_off = false;
uint32_t file_size = header->file_size_;
if (art::CompactDexFile::IsMagicValid(header->magic_)) {
uint32_t computed_file_size;
if (__builtin_add_overflow(header->data_off_, header->data_size_, &computed_file_size)) {
return false;
}
if (computed_file_size > file_size) {
file_size = computed_file_size;
modify_data_off = true;
}
} else if (!art::StandardDexFile::IsMagicValid(header->magic_)) {
return false;
}
memory_.resize(header.file_size_);
if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), header.file_size_)) {
memory_.resize(file_size);
if (!memory->ReadFully(dex_file_offset_in_memory, memory_.data(), memory_.size())) {
return false;
}
header = reinterpret_cast<art::DexFile::Header*>(memory_.data());
if (modify_data_off) {
header->data_off_ = header->file_size_;
}
art::DexFileLoader loader;
std::string error_msg;
auto dex =
loader.Open(memory_.data(), header.file_size_, "", 0, nullptr, false, false, &error_msg);
loader.Open(memory_.data(), header->file_size_, "", 0, nullptr, false, false, &error_msg);
dex_file_.reset(dex.release());
return dex_file_ != nullptr;
}

View file

@ -32,7 +32,7 @@ class DexFile {
DexFile() = default;
virtual ~DexFile() = default;
void GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
bool GetMethodInformation(uint64_t dex_offset, std::string* method_name, uint64_t* method_offset);
static DexFile* Create(uint64_t dex_file_offset_in_memory, Memory* memory, MapInfo* info);

View file

@ -24,23 +24,138 @@
#include <unwindstack/DexFiles.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include "DexFile.h"
namespace unwindstack {
struct DEXFileEntry32 {
uint32_t next;
uint32_t prev;
uint32_t dex_file;
};
struct DEXFileEntry64 {
uint64_t next;
uint64_t prev;
uint64_t dex_file;
};
DexFiles::DexFiles(std::shared_ptr<Memory>& memory) : memory_(memory) {}
DexFiles::DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs)
: memory_(memory), search_libs_(search_libs) {}
DexFiles::~DexFiles() {
for (auto& entry : files_) {
delete entry.second;
}
}
void DexFiles::SetArch(ArchEnum arch) {
switch (arch) {
case ARCH_ARM:
case ARCH_MIPS:
case ARCH_X86:
read_entry_ptr_func_ = &DexFiles::ReadEntryPtr32;
read_entry_func_ = &DexFiles::ReadEntry32;
break;
case ARCH_ARM64:
case ARCH_MIPS64:
case ARCH_X86_64:
read_entry_ptr_func_ = &DexFiles::ReadEntryPtr64;
read_entry_func_ = &DexFiles::ReadEntry64;
break;
case ARCH_UNKNOWN:
abort();
}
}
uint64_t DexFiles::ReadEntryPtr32(uint64_t addr) {
uint32_t entry;
if (!memory_->ReadFully(addr, &entry, sizeof(entry))) {
return 0;
}
return entry;
}
uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) {
uint64_t entry;
if (!memory_->ReadFully(addr, &entry, sizeof(entry))) {
return 0;
}
return entry;
}
bool DexFiles::ReadEntry32() {
DEXFileEntry32 entry;
if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
entry_addr_ = 0;
return false;
}
addrs_.push_back(entry.dex_file);
entry_addr_ = entry.next;
return true;
}
bool DexFiles::ReadEntry64() {
DEXFileEntry64 entry;
if (!memory_->ReadFully(entry_addr_, &entry, sizeof(entry)) || entry.dex_file == 0) {
entry_addr_ = 0;
return false;
}
addrs_.push_back(entry.dex_file);
entry_addr_ = entry.next;
return true;
}
void DexFiles::Init(Maps* maps) {
if (initialized_) {
return;
}
initialized_ = true;
entry_addr_ = 0;
const std::string dex_debug_name("__art_debug_dexfiles");
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 (const std::string& name : search_libs_) {
if (name == lib) {
found = true;
break;
}
}
if (!found) {
continue;
}
}
Elf* elf = info->GetElf(memory_, true);
uint64_t ptr;
// Find first non-empty list (libart might be loaded multiple times).
if (elf->GetGlobalVariable(dex_debug_name, &ptr) && ptr != 0) {
entry_addr_ = (this->*read_entry_ptr_func_)(ptr + info->start);
if (entry_addr_ != 0) {
break;
}
}
}
}
DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
// Lock while processing the data.
std::lock_guard<std::mutex> guard(files_lock_);
DexFile* dex_file;
auto entry = files_.find(dex_file_offset);
if (entry == files_.end()) {
@ -52,14 +167,38 @@ DexFile* DexFiles::GetDexFile(uint64_t dex_file_offset, MapInfo* info) {
return dex_file;
}
void DexFiles::GetMethodInformation(uint64_t dex_file_offset, uint64_t dex_offset, MapInfo* info,
bool DexFiles::GetAddr(size_t index, uint64_t* addr) {
if (index < addrs_.size()) {
*addr = addrs_[index];
return true;
}
if (entry_addr_ != 0 && (this->*read_entry_func_)()) {
*addr = addrs_.back();
return true;
}
return false;
}
void DexFiles::GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc,
std::string* method_name, uint64_t* method_offset) {
DexFile* dex_file = GetDexFile(dex_file_offset, info);
if (dex_file != nullptr) {
dex_file->GetMethodInformation(dex_offset, method_name, method_offset);
std::lock_guard<std::mutex> guard(lock_);
if (!initialized_) {
Init(maps);
}
size_t index = 0;
uint64_t addr;
while (GetAddr(index++, &addr)) {
if (addr < info->start || addr >= info->end) {
continue;
}
DexFile* dex_file = GetDexFile(addr, info);
if (dex_file != nullptr &&
dex_file->GetMethodInformation(dex_pc - addr, method_name, method_offset)) {
break;
}
}
}
void DexFiles::SetArch(ArchEnum) {}
} // namespace unwindstack

View file

@ -172,7 +172,7 @@ void JitDebug::Init(Maps* maps) {
// Regardless of what happens below, consider the init finished.
initialized_ = true;
std::string descriptor_name("__jit_debug_descriptor");
const std::string descriptor_name("__jit_debug_descriptor");
for (MapInfo* info : *maps) {
if (!(info->flags & PROT_EXEC) || !(info->flags & PROT_READ) || info->offset != 0) {
continue;

View file

@ -53,49 +53,22 @@ void Unwinder::FillInDexFrame() {
frame->pc = dex_pc;
frame->sp = regs_->sp();
auto it = maps_->begin();
uint64_t rel_dex_pc;
MapInfo* info;
for (; it != maps_->end(); ++it) {
auto entry = *it;
if (dex_pc >= entry->start && dex_pc < entry->end) {
info = entry;
rel_dex_pc = dex_pc - entry->start;
frame->map_start = entry->start;
frame->map_end = entry->end;
frame->map_offset = entry->offset;
frame->map_load_bias = entry->load_bias;
frame->map_flags = entry->flags;
frame->map_name = entry->name;
frame->rel_pc = rel_dex_pc;
break;
}
}
if (it == maps_->end() || ++it == maps_->end()) {
return;
}
auto entry = *it;
unwindstack::Elf* elf = entry->GetElf(process_memory_, true);
if (!elf->valid()) {
return;
}
// Adjust the relative dex by the offset.
rel_dex_pc += entry->elf_offset;
uint64_t dex_offset;
if (!elf->GetFunctionName(rel_dex_pc, &frame->function_name, &dex_offset)) {
return;
}
frame->function_offset = dex_offset;
if (frame->function_name != "$dexfile") {
return;
}
MapInfo* info = maps_->Find(dex_pc);
frame->map_start = info->start;
frame->map_end = info->end;
frame->map_offset = info->offset;
frame->map_load_bias = info->load_bias;
frame->map_flags = info->flags;
frame->map_name = info->name;
frame->rel_pc = dex_pc - info->start;
#if !defined(NO_LIBDEXFILE_SUPPORT)
dex_files_->GetMethodInformation(dex_pc - dex_offset, dex_offset, info, &frame->function_name,
if (dex_files_ == nullptr) {
return;
}
// dex_files_->GetMethodInformation(dex_pc - dex_offset, dex_offset, info, &frame->function_name,
dex_files_->GetMethodInformation(maps_, info, dex_pc, &frame->function_name,
&frame->function_offset);
#endif
}

View file

@ -23,6 +23,7 @@
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
namespace unwindstack {
@ -36,19 +37,40 @@ enum ArchEnum : uint8_t;
class DexFiles {
public:
explicit DexFiles(std::shared_ptr<Memory>& memory);
DexFiles(std::shared_ptr<Memory>& memory, std::vector<std::string>& search_libs);
~DexFiles();
DexFile* GetDexFile(uint64_t dex_file_offset, MapInfo* info);
void GetMethodInformation(uint64_t dex_file_offset, uint64_t dex_offset, MapInfo* info,
std::string* method_name, uint64_t* method_offset);
void GetMethodInformation(Maps* maps, MapInfo* info, uint64_t dex_pc, std::string* method_name,
uint64_t* method_offset);
void SetArch(ArchEnum arch);
private:
void Init(Maps* maps);
bool GetAddr(size_t index, uint64_t* addr);
uint64_t ReadEntryPtr32(uint64_t addr);
uint64_t ReadEntryPtr64(uint64_t addr);
bool ReadEntry32();
bool ReadEntry64();
std::shared_ptr<Memory> memory_;
std::mutex files_lock_;
std::vector<std::string> search_libs_;
std::mutex lock_;
bool initialized_ = false;
std::unordered_map<uint64_t, DexFile*> files_;
uint64_t entry_addr_ = 0;
uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr;
bool (DexFiles::*read_entry_func_)() = nullptr;
std::vector<uint64_t> addrs_;
};
} // namespace unwindstack

View file

@ -0,0 +1,45 @@
/*
* 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.
*/
#ifndef _LIBUNWINDSTACK_DEXFILESDATA_H
#define _LIBUNWINDSTACK_DEXFILESDATA_H
namespace unwindstack {
// Borrowed from art/dex/dex_file_test.cc.
static constexpr uint32_t kDexData[] = {
0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
};
} // namespace unwindstack
#endif // _LIBUNWINDSTACK_DEXFILESDATA_H

View file

@ -32,31 +32,11 @@
#include "DexFile.h"
#include "DexFileData.h"
#include "MemoryFake.h"
namespace unwindstack {
// Borrowed from art/dex/dex_file_test.cc.
static constexpr uint32_t kDexData[] = {
0x0a786564, 0x00383330, 0xc98b3ab8, 0xf3749d94, 0xaecca4d8, 0xffc7b09a, 0xdca9ca7f, 0x5be5deab,
0x00000220, 0x00000070, 0x12345678, 0x00000000, 0x00000000, 0x0000018c, 0x00000008, 0x00000070,
0x00000004, 0x00000090, 0x00000002, 0x000000a0, 0x00000000, 0x00000000, 0x00000003, 0x000000b8,
0x00000001, 0x000000d0, 0x00000130, 0x000000f0, 0x00000122, 0x0000012a, 0x00000132, 0x00000146,
0x00000151, 0x00000154, 0x00000158, 0x0000016d, 0x00000001, 0x00000002, 0x00000004, 0x00000006,
0x00000004, 0x00000002, 0x00000000, 0x00000005, 0x00000002, 0x0000011c, 0x00000000, 0x00000000,
0x00010000, 0x00000007, 0x00000001, 0x00000000, 0x00000000, 0x00000001, 0x00000001, 0x00000000,
0x00000003, 0x00000000, 0x0000017e, 0x00000000, 0x00010001, 0x00000001, 0x00000173, 0x00000004,
0x00021070, 0x000e0000, 0x00010001, 0x00000000, 0x00000178, 0x00000001, 0x0000000e, 0x00000001,
0x3c060003, 0x74696e69, 0x4c06003e, 0x6e69614d, 0x4c12003b, 0x6176616a, 0x6e616c2f, 0x624f2f67,
0x7463656a, 0x4d09003b, 0x2e6e6961, 0x6176616a, 0x00560100, 0x004c5602, 0x6a4c5b13, 0x2f617661,
0x676e616c, 0x7274532f, 0x3b676e69, 0x616d0400, 0x01006e69, 0x000e0700, 0x07000103, 0x0000000e,
0x81000002, 0x01f00480, 0x02880901, 0x0000000c, 0x00000000, 0x00000001, 0x00000000, 0x00000001,
0x00000008, 0x00000070, 0x00000002, 0x00000004, 0x00000090, 0x00000003, 0x00000002, 0x000000a0,
0x00000005, 0x00000003, 0x000000b8, 0x00000006, 0x00000001, 0x000000d0, 0x00002001, 0x00000002,
0x000000f0, 0x00001001, 0x00000001, 0x0000011c, 0x00002002, 0x00000008, 0x00000122, 0x00002003,
0x00000002, 0x00000173, 0x00002000, 0x00000001, 0x0000017e, 0x00001000, 0x00000001, 0x0000018c,
};
TEST(DexFileTest, from_file_open_non_exist) {
DexFileFromFile dex_file;
ASSERT_FALSE(dex_file.Open(0, "/file/does/not/exist"));

View file

@ -0,0 +1,311 @@
/*
* 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/DexFiles.h>
#include <unwindstack/Elf.h>
#include <unwindstack/MapInfo.h>
#include <unwindstack/Maps.h>
#include <unwindstack/Memory.h>
#include "DexFileData.h"
#include "ElfFake.h"
#include "MemoryFake.h"
namespace unwindstack {
class DexFilesTest : public ::testing::Test {
protected:
void SetUp() override {
memory_ = new MemoryFake;
process_memory_.reset(memory_);
dex_files_.reset(new DexFiles(process_memory_));
dex_files_->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 r-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"
"300000-400000 rw-p 0000000 00:00 0\n"));
ASSERT_TRUE(maps_->Parse());
// Global variable in a section that is not readable/executable.
MapInfo* map_info = maps_->Get(kMapGlobalNonReadableExectable);
ASSERT_TRUE(map_info != nullptr);
MemoryFake* memory = new MemoryFake;
ElfFake* elf = new ElfFake(memory);
elf->FakeSetValid(true);
ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
elf->FakeSetInterface(interface);
interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
map_info->elf.reset(elf);
// Global variable not set by default.
map_info = maps_->Get(kMapGlobalSetToZero);
ASSERT_TRUE(map_info != nullptr);
memory = new MemoryFake;
elf = new ElfFake(memory);
elf->FakeSetValid(true);
interface = new ElfInterfaceFake(memory);
elf->FakeSetInterface(interface);
interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
map_info->elf.reset(elf);
// Global variable set in this map.
map_info = maps_->Get(kMapGlobal);
ASSERT_TRUE(map_info != nullptr);
memory = new MemoryFake;
elf = new ElfFake(memory);
elf->FakeSetValid(true);
interface = new ElfInterfaceFake(memory);
elf->FakeSetInterface(interface);
interface->FakeSetGlobalVariable("__art_debug_dexfiles", 0x800);
map_info->elf.reset(elf);
}
void WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev, uint32_t dex_file);
void WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev, uint64_t dex_file);
void WriteDex(uint64_t dex_file);
static constexpr size_t kMapGlobalNonReadableExectable = 3;
static constexpr size_t kMapGlobalSetToZero = 4;
static constexpr size_t kMapGlobal = 5;
static constexpr size_t kMapDexFileEntries = 7;
static constexpr size_t kMapDexFiles = 8;
std::shared_ptr<Memory> process_memory_;
MemoryFake* memory_;
std::unique_ptr<DexFiles> dex_files_;
std::unique_ptr<BufferMaps> maps_;
};
void DexFilesTest::WriteEntry32(uint64_t entry_addr, uint32_t next, uint32_t prev,
uint32_t dex_file) {
// Format of the 32 bit DEXFileEntry structure:
// uint32_t next
memory_->SetData32(entry_addr, next);
// uint32_t prev
memory_->SetData32(entry_addr + 4, prev);
// uint32_t dex_file
memory_->SetData32(entry_addr + 8, dex_file);
}
void DexFilesTest::WriteEntry64(uint64_t entry_addr, uint64_t next, uint64_t prev,
uint64_t dex_file) {
// Format of the 64 bit DEXFileEntry structure:
// uint64_t next
memory_->SetData64(entry_addr, next);
// uint64_t prev
memory_->SetData64(entry_addr + 8, prev);
// uint64_t dex_file
memory_->SetData64(entry_addr + 16, dex_file);
}
void DexFilesTest::WriteDex(uint64_t dex_file) {
memory_->SetMemory(dex_file, kDexData, sizeof(kDexData) * sizeof(uint32_t));
}
TEST_F(DexFilesTest, get_method_information_invalid) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFileEntries);
dex_files_->GetMethodInformation(maps_.get(), info, 0, &method_name, &method_offset);
EXPECT_EQ("nothing", method_name);
EXPECT_EQ(0x124U, method_offset);
}
TEST_F(DexFilesTest, get_method_information_32) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
memory_->SetData32(0xf800, 0x200000);
WriteEntry32(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
}
TEST_F(DexFilesTest, get_method_information_64) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
dex_files_->SetArch(ARCH_ARM64);
memory_->SetData64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x301000);
WriteDex(0x301000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x301102, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(2U, method_offset);
}
TEST_F(DexFilesTest, get_method_information_not_first_entry_32) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
memory_->SetData32(0xf800, 0x200000);
WriteEntry32(0x200000, 0x200100, 0, 0x100000);
WriteEntry32(0x200100, 0, 0x200000, 0x300000);
WriteDex(0x300000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(4U, method_offset);
}
TEST_F(DexFilesTest, get_method_information_not_first_entry_64) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
dex_files_->SetArch(ARCH_ARM64);
memory_->SetData64(0xf800, 0x200000);
WriteEntry64(0x200000, 0x200100, 0, 0x100000);
WriteEntry64(0x200100, 0, 0x200000, 0x300000);
WriteDex(0x300000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300106, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(6U, method_offset);
}
TEST_F(DexFilesTest, get_method_information_cached) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
memory_->SetData32(0xf800, 0x200000);
WriteEntry32(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
// Clear all memory and make sure that data is acquired from the cache.
memory_->Clear();
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
}
TEST_F(DexFilesTest, get_method_information_search_libs) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
memory_->SetData32(0xf800, 0x200000);
WriteEntry32(0x200000, 0x200100, 0, 0x100000);
WriteEntry32(0x200100, 0, 0x200000, 0x300000);
WriteDex(0x300000);
// Only search a given named list of libs.
std::vector<std::string> libs{"libart.so"};
dex_files_.reset(new DexFiles(process_memory_, libs));
dex_files_->SetArch(ARCH_ARM);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
EXPECT_EQ("nothing", method_name);
EXPECT_EQ(0x124U, method_offset);
MapInfo* map_info = maps_->Get(kMapGlobal);
map_info->name = "/system/lib/libart.so";
dex_files_.reset(new DexFiles(process_memory_, libs));
dex_files_->SetArch(ARCH_ARM);
// Make sure that clearing out copy of the libs doesn't affect the
// DexFiles object.
libs.clear();
dex_files_->GetMethodInformation(maps_.get(), info, 0x300104, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(4U, method_offset);
}
TEST_F(DexFilesTest, get_method_information_global_skip_zero_32) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
// First global variable found, but value is zero.
memory_->SetData32(0xc800, 0);
memory_->SetData32(0xf800, 0x200000);
WriteEntry32(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
// Verify that second is ignored when first is set to non-zero
dex_files_.reset(new DexFiles(process_memory_));
dex_files_->SetArch(ARCH_ARM);
method_name = "fail";
method_offset = 0x123;
memory_->SetData32(0xc800, 0x100000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("fail", method_name);
EXPECT_EQ(0x123U, method_offset);
}
TEST_F(DexFilesTest, get_method_information_global_skip_zero_64) {
std::string method_name = "nothing";
uint64_t method_offset = 0x124;
MapInfo* info = maps_->Get(kMapDexFiles);
// First global variable found, but value is zero.
memory_->SetData64(0xc800, 0);
memory_->SetData64(0xf800, 0x200000);
WriteEntry64(0x200000, 0, 0, 0x300000);
WriteDex(0x300000);
dex_files_->SetArch(ARCH_ARM64);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("Main.<init>", method_name);
EXPECT_EQ(0U, method_offset);
// Verify that second is ignored when first is set to non-zero
dex_files_.reset(new DexFiles(process_memory_));
dex_files_->SetArch(ARCH_ARM64);
method_name = "fail";
method_offset = 0x123;
memory_->SetData32(0xc800, 0x100000);
dex_files_->GetMethodInformation(maps_.get(), info, 0x300100, &method_name, &method_offset);
EXPECT_EQ("fail", method_name);
EXPECT_EQ(0x123U, method_offset);
}
} // namespace unwindstack

View file

@ -56,30 +56,30 @@ class JitDebugTest : public ::testing::Test {
MapInfo* map_info = maps_->Get(3);
ASSERT_TRUE(map_info != nullptr);
elf_memories_.push_back(new MemoryFake);
ElfFake* elf = new ElfFake(elf_memories_.back());
MemoryFake* memory = new MemoryFake;
ElfFake* elf = new ElfFake(memory);
elf->FakeSetValid(true);
ElfInterfaceFake* interface = new ElfInterfaceFake(elf_memories_.back());
ElfInterfaceFake* interface = new ElfInterfaceFake(memory);
elf->FakeSetInterface(interface);
interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
map_info->elf.reset(elf);
map_info = maps_->Get(5);
ASSERT_TRUE(map_info != nullptr);
elf_memories_.push_back(new MemoryFake);
elf = new ElfFake(elf_memories_.back());
memory = new MemoryFake;
elf = new ElfFake(memory);
elf->FakeSetValid(true);
interface = new ElfInterfaceFake(elf_memories_.back());
interface = new ElfInterfaceFake(memory);
elf->FakeSetInterface(interface);
interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
map_info->elf.reset(elf);
map_info = maps_->Get(6);
ASSERT_TRUE(map_info != nullptr);
elf_memories_.push_back(new MemoryFake);
elf = new ElfFake(elf_memories_.back());
memory = new MemoryFake;
elf = new ElfFake(memory);
elf->FakeSetValid(true);
interface = new ElfInterfaceFake(elf_memories_.back());
interface = new ElfInterfaceFake(memory);
elf->FakeSetInterface(interface);
interface->FakeSetGlobalVariable("__jit_debug_descriptor", 0x800);
map_info->elf.reset(elf);
@ -171,7 +171,6 @@ class JitDebugTest : public ::testing::Test {
std::shared_ptr<Memory> process_memory_;
MemoryFake* memory_;
std::vector<MemoryFake*> elf_memories_;
std::unique_ptr<JitDebug> jit_debug_;
std::unique_ptr<BufferMaps> maps_;
};

View file

@ -26,6 +26,7 @@
#include <sys/types.h>
#include <unistd.h>
#include <unwindstack/DexFiles.h>
#include <unwindstack/Elf.h>
#include <unwindstack/JitDebug.h>
#include <unwindstack/Maps.h>
@ -91,8 +92,13 @@ 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());
unwindstack::DexFiles dex_files(process_memory);
unwinder.SetDexFiles(&dex_files, regs->Arch());
unwinder.Unwind();
// Print the frames.