diff --git a/libbacktrace/UnwindStackMap.cpp b/libbacktrace/UnwindStackMap.cpp index 1622e30d4..6dcc6210f 100644 --- a/libbacktrace/UnwindStackMap.cpp +++ b/libbacktrace/UnwindStackMap.cpp @@ -46,7 +46,7 @@ bool UnwindStackMap::Build() { std::vector 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()) { diff --git a/libunwindstack/Android.bp b/libunwindstack/Android.bp index 74dfaa500..3354c90d0 100644 --- a/libunwindstack/Android.bp +++ b/libunwindstack/Android.bp @@ -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, diff --git a/libunwindstack/DexFile.cpp b/libunwindstack/DexFile.cpp index be6c2f71e..b4a992a89 100644 --- a/libunwindstack/DexFile.cpp +++ b/libunwindstack/DexFile.cpp @@ -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(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(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; } diff --git a/libunwindstack/DexFile.h b/libunwindstack/DexFile.h index 22e98df9a..3ce2f1edd 100644 --- a/libunwindstack/DexFile.h +++ b/libunwindstack/DexFile.h @@ -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); diff --git a/libunwindstack/DexFiles.cpp b/libunwindstack/DexFiles.cpp index fe6d3c625..c5f8138ed 100644 --- a/libunwindstack/DexFiles.cpp +++ b/libunwindstack/DexFiles.cpp @@ -24,23 +24,138 @@ #include #include +#include #include #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) {} +DexFiles::DexFiles(std::shared_ptr& memory, std::vector& 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 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 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 diff --git a/libunwindstack/JitDebug.cpp b/libunwindstack/JitDebug.cpp index d1dc0e659..0c319ec32 100644 --- a/libunwindstack/JitDebug.cpp +++ b/libunwindstack/JitDebug.cpp @@ -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; diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index db8278e5c..6119ee0fc 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp @@ -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 } diff --git a/libunwindstack/include/unwindstack/DexFiles.h b/libunwindstack/include/unwindstack/DexFiles.h index 50c9c321e..26f5d35a3 100644 --- a/libunwindstack/include/unwindstack/DexFiles.h +++ b/libunwindstack/include/unwindstack/DexFiles.h @@ -23,6 +23,7 @@ #include #include #include +#include namespace unwindstack { @@ -36,19 +37,40 @@ enum ArchEnum : uint8_t; class DexFiles { public: explicit DexFiles(std::shared_ptr& memory); + DexFiles(std::shared_ptr& memory, std::vector& 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_; - std::mutex files_lock_; + std::vector search_libs_; + + std::mutex lock_; + bool initialized_ = false; std::unordered_map files_; + + uint64_t entry_addr_ = 0; + uint64_t (DexFiles::*read_entry_ptr_func_)(uint64_t) = nullptr; + bool (DexFiles::*read_entry_func_)() = nullptr; + std::vector addrs_; }; } // namespace unwindstack diff --git a/libunwindstack/tests/DexFileData.h b/libunwindstack/tests/DexFileData.h new file mode 100644 index 000000000..6975c688b --- /dev/null +++ b/libunwindstack/tests/DexFileData.h @@ -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 diff --git a/libunwindstack/tests/DexFileTest.cpp b/libunwindstack/tests/DexFileTest.cpp index d1338cba9..6e05c5ed3 100644 --- a/libunwindstack/tests/DexFileTest.cpp +++ b/libunwindstack/tests/DexFileTest.cpp @@ -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")); diff --git a/libunwindstack/tests/DexFilesTest.cpp b/libunwindstack/tests/DexFilesTest.cpp new file mode 100644 index 000000000..dca560549 --- /dev/null +++ b/libunwindstack/tests/DexFilesTest.cpp @@ -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 +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#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 process_memory_; + MemoryFake* memory_; + std::unique_ptr dex_files_; + std::unique_ptr 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.", 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.", 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.", 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.", 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.", 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.", 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 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.", 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.", 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.", 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 diff --git a/libunwindstack/tests/JitDebugTest.cpp b/libunwindstack/tests/JitDebugTest.cpp index 37628f893..c1c45f8fd 100644 --- a/libunwindstack/tests/JitDebugTest.cpp +++ b/libunwindstack/tests/JitDebugTest.cpp @@ -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 process_memory_; MemoryFake* memory_; - std::vector elf_memories_; std::unique_ptr jit_debug_; std::unique_ptr maps_; }; diff --git a/libunwindstack/tools/unwind.cpp b/libunwindstack/tools/unwind.cpp index 07e48af60..22ca7bf1d 100644 --- a/libunwindstack/tools/unwind.cpp +++ b/libunwindstack/tools/unwind.cpp @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -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.