/* * 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 <stdint.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> #include <unistd.h> #include <memory> #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; const uint32_t field_offset = 12; // offset of first_entry_ in the descriptor struct. if (!memory_->ReadFully(addr + field_offset, &entry, sizeof(entry))) { return 0; } return entry; } uint64_t DexFiles::ReadEntryPtr64(uint64_t addr) { uint64_t entry; const uint32_t field_offset = 16; // offset of first_entry_ in the descriptor struct. if (!memory_->ReadFully(addr + field_offset, &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("__dex_debug_descriptor"); 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. DexFile* dex_file; auto entry = files_.find(dex_file_offset); if (entry == files_.end()) { dex_file = DexFile::Create(dex_file_offset, memory_.get(), info); files_[dex_file_offset] = dex_file; } else { dex_file = entry->second; } return dex_file; } 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) { 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; } } } } // namespace unwindstack