xref: /aosp_15_r20/system/unwinding/libunwindstack/DexFile.cpp (revision eb293b8f56ee8303637c5595cfcdeef8039e85c6)
1*eb293b8fSAndroid Build Coastguard Worker /*
2*eb293b8fSAndroid Build Coastguard Worker  * Copyright (C) 2018 The Android Open Source Project
3*eb293b8fSAndroid Build Coastguard Worker  *
4*eb293b8fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*eb293b8fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*eb293b8fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*eb293b8fSAndroid Build Coastguard Worker  *
8*eb293b8fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*eb293b8fSAndroid Build Coastguard Worker  *
10*eb293b8fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*eb293b8fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*eb293b8fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*eb293b8fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*eb293b8fSAndroid Build Coastguard Worker  * limitations under the License.
15*eb293b8fSAndroid Build Coastguard Worker  */
16*eb293b8fSAndroid Build Coastguard Worker 
17*eb293b8fSAndroid Build Coastguard Worker #include <stdint.h>
18*eb293b8fSAndroid Build Coastguard Worker #include <sys/mman.h>
19*eb293b8fSAndroid Build Coastguard Worker #include <sys/stat.h>
20*eb293b8fSAndroid Build Coastguard Worker #include <sys/types.h>
21*eb293b8fSAndroid Build Coastguard Worker #include <unistd.h>
22*eb293b8fSAndroid Build Coastguard Worker 
23*eb293b8fSAndroid Build Coastguard Worker #include <memory>
24*eb293b8fSAndroid Build Coastguard Worker 
25*eb293b8fSAndroid Build Coastguard Worker #include <android-base/unique_fd.h>
26*eb293b8fSAndroid Build Coastguard Worker #include <art_api/dex_file_support.h>
27*eb293b8fSAndroid Build Coastguard Worker 
28*eb293b8fSAndroid Build Coastguard Worker #include <unwindstack/Log.h>
29*eb293b8fSAndroid Build Coastguard Worker #include <unwindstack/MapInfo.h>
30*eb293b8fSAndroid Build Coastguard Worker #include <unwindstack/Memory.h>
31*eb293b8fSAndroid Build Coastguard Worker 
32*eb293b8fSAndroid Build Coastguard Worker #include "DexFile.h"
33*eb293b8fSAndroid Build Coastguard Worker #include "MemoryBuffer.h"
34*eb293b8fSAndroid Build Coastguard Worker 
35*eb293b8fSAndroid Build Coastguard Worker namespace unwindstack {
36*eb293b8fSAndroid Build Coastguard Worker 
37*eb293b8fSAndroid Build Coastguard Worker std::map<DexFile::MappedFileKey, std::weak_ptr<DexFile::DexFileApi>> DexFile::g_mapped_dex_files;
38*eb293b8fSAndroid Build Coastguard Worker std::mutex DexFile::g_lock;
39*eb293b8fSAndroid Build Coastguard Worker 
CheckDexSupport()40*eb293b8fSAndroid Build Coastguard Worker static bool CheckDexSupport() {
41*eb293b8fSAndroid Build Coastguard Worker   if (std::string err_msg; !art_api::dex::TryLoadLibdexfile(&err_msg)) {
42*eb293b8fSAndroid Build Coastguard Worker     Log::Error("Failed to initialize DEX file support: %s", err_msg.c_str());
43*eb293b8fSAndroid Build Coastguard Worker     return false;
44*eb293b8fSAndroid Build Coastguard Worker   }
45*eb293b8fSAndroid Build Coastguard Worker   return true;
46*eb293b8fSAndroid Build Coastguard Worker }
47*eb293b8fSAndroid Build Coastguard Worker 
CreateFromDisk(uint64_t addr,uint64_t size,MapInfo * map)48*eb293b8fSAndroid Build Coastguard Worker std::shared_ptr<DexFile> DexFile::CreateFromDisk(uint64_t addr, uint64_t size, MapInfo* map) {
49*eb293b8fSAndroid Build Coastguard Worker   if (map == nullptr || map->name().empty()) {
50*eb293b8fSAndroid Build Coastguard Worker     return nullptr;  // MapInfo not backed by file.
51*eb293b8fSAndroid Build Coastguard Worker   }
52*eb293b8fSAndroid Build Coastguard Worker   if (!(map->start() <= addr && addr < map->end())) {
53*eb293b8fSAndroid Build Coastguard Worker     return nullptr;  // addr is not in MapInfo range.
54*eb293b8fSAndroid Build Coastguard Worker   }
55*eb293b8fSAndroid Build Coastguard Worker   if (size > (map->end() - addr)) {
56*eb293b8fSAndroid Build Coastguard Worker     return nullptr;  // size is past the MapInfo end.
57*eb293b8fSAndroid Build Coastguard Worker   }
58*eb293b8fSAndroid Build Coastguard Worker   uint64_t offset_in_file = (addr - map->start()) + map->offset();
59*eb293b8fSAndroid Build Coastguard Worker 
60*eb293b8fSAndroid Build Coastguard Worker   // Fast-path: Check if the dex file was already mapped from disk.
61*eb293b8fSAndroid Build Coastguard Worker   std::lock_guard<std::mutex> guard(g_lock);
62*eb293b8fSAndroid Build Coastguard Worker   MappedFileKey cache_key(map->name(), offset_in_file, size);
63*eb293b8fSAndroid Build Coastguard Worker   std::weak_ptr<DexFileApi>& cache_entry = g_mapped_dex_files[cache_key];
64*eb293b8fSAndroid Build Coastguard Worker   std::shared_ptr<DexFileApi> dex_api = cache_entry.lock();
65*eb293b8fSAndroid Build Coastguard Worker   if (dex_api != nullptr) {
66*eb293b8fSAndroid Build Coastguard Worker     return std::shared_ptr<DexFile>(new DexFile(addr, size, std::move(dex_api)));
67*eb293b8fSAndroid Build Coastguard Worker   }
68*eb293b8fSAndroid Build Coastguard Worker 
69*eb293b8fSAndroid Build Coastguard Worker   // Load the file from disk and cache it.
70*eb293b8fSAndroid Build Coastguard Worker   auto memory = Memory::CreateFileMemory(map->name(), offset_in_file, size);
71*eb293b8fSAndroid Build Coastguard Worker   if (memory == nullptr) {
72*eb293b8fSAndroid Build Coastguard Worker     return nullptr;  // failed to map the file.
73*eb293b8fSAndroid Build Coastguard Worker   }
74*eb293b8fSAndroid Build Coastguard Worker   std::unique_ptr<art_api::dex::DexFile> dex;
75*eb293b8fSAndroid Build Coastguard Worker   art_api::dex::DexFile::Create(memory->GetPtr(), size, nullptr, map->name().c_str(), &dex);
76*eb293b8fSAndroid Build Coastguard Worker   if (dex == nullptr) {
77*eb293b8fSAndroid Build Coastguard Worker     return nullptr;  // invalid DEX file.
78*eb293b8fSAndroid Build Coastguard Worker   }
79*eb293b8fSAndroid Build Coastguard Worker   dex_api.reset(new DexFileApi{std::move(dex), memory, std::mutex()});
80*eb293b8fSAndroid Build Coastguard Worker   cache_entry = dex_api;
81*eb293b8fSAndroid Build Coastguard Worker   return std::shared_ptr<DexFile>(new DexFile(addr, size, std::move(dex_api)));
82*eb293b8fSAndroid Build Coastguard Worker }
83*eb293b8fSAndroid Build Coastguard Worker 
Create(uint64_t base_addr,uint64_t file_size,Memory * memory,MapInfo * info)84*eb293b8fSAndroid Build Coastguard Worker std::shared_ptr<DexFile> DexFile::Create(uint64_t base_addr, uint64_t file_size, Memory* memory,
85*eb293b8fSAndroid Build Coastguard Worker                                          MapInfo* info) {
86*eb293b8fSAndroid Build Coastguard Worker   static bool has_dex_support = CheckDexSupport();
87*eb293b8fSAndroid Build Coastguard Worker   if (!has_dex_support || file_size == 0) {
88*eb293b8fSAndroid Build Coastguard Worker     return nullptr;
89*eb293b8fSAndroid Build Coastguard Worker   }
90*eb293b8fSAndroid Build Coastguard Worker 
91*eb293b8fSAndroid Build Coastguard Worker   // Do not try to open the DEX file if the file name ends with "(deleted)". It does not exist.
92*eb293b8fSAndroid Build Coastguard Worker   // This happens when an app is background-optimized by ART and all of its files are replaced.
93*eb293b8fSAndroid Build Coastguard Worker   // Furthermore, do NOT try to fallback to in-memory copy. It would work, but all apps tend to
94*eb293b8fSAndroid Build Coastguard Worker   // be background-optimized at the same time, so it would lead to excessive memory use during
95*eb293b8fSAndroid Build Coastguard Worker   // system-wide profiling (essentially copying all dex files for all apps: hundreds of MBs).
96*eb293b8fSAndroid Build Coastguard Worker   // This will cause missing symbols in the backtrace, however, that outcome is inevitable
97*eb293b8fSAndroid Build Coastguard Worker   // anyway, since we can not obtain mini-debug-info for the deleted .oat files.
98*eb293b8fSAndroid Build Coastguard Worker   const std::string_view filename(info != nullptr ? info->name() : "");
99*eb293b8fSAndroid Build Coastguard Worker   const std::string_view kDeleted("(deleted)");
100*eb293b8fSAndroid Build Coastguard Worker   if (filename.size() >= kDeleted.size() &&
101*eb293b8fSAndroid Build Coastguard Worker       filename.substr(filename.size() - kDeleted.size()) == kDeleted) {
102*eb293b8fSAndroid Build Coastguard Worker     return nullptr;
103*eb293b8fSAndroid Build Coastguard Worker   }
104*eb293b8fSAndroid Build Coastguard Worker 
105*eb293b8fSAndroid Build Coastguard Worker   std::shared_ptr<DexFile> dex_file = CreateFromDisk(base_addr, file_size, info);
106*eb293b8fSAndroid Build Coastguard Worker   if (dex_file != nullptr) {
107*eb293b8fSAndroid Build Coastguard Worker     return dex_file;
108*eb293b8fSAndroid Build Coastguard Worker   }
109*eb293b8fSAndroid Build Coastguard Worker 
110*eb293b8fSAndroid Build Coastguard Worker   // Fallback: make copy in local buffer.
111*eb293b8fSAndroid Build Coastguard Worker   std::unique_ptr<MemoryBuffer> copy(new MemoryBuffer(file_size));
112*eb293b8fSAndroid Build Coastguard Worker   uint8_t* dst_ptr = copy->GetPtr(0);
113*eb293b8fSAndroid Build Coastguard Worker   if (dst_ptr == nullptr || !memory->ReadFully(base_addr, dst_ptr, file_size)) {
114*eb293b8fSAndroid Build Coastguard Worker     return nullptr;
115*eb293b8fSAndroid Build Coastguard Worker   }
116*eb293b8fSAndroid Build Coastguard Worker   std::unique_ptr<art_api::dex::DexFile> dex;
117*eb293b8fSAndroid Build Coastguard Worker   art_api::dex::DexFile::Create(copy->GetPtr(0), file_size, nullptr, "", &dex);
118*eb293b8fSAndroid Build Coastguard Worker   if (dex == nullptr) {
119*eb293b8fSAndroid Build Coastguard Worker     return nullptr;
120*eb293b8fSAndroid Build Coastguard Worker   }
121*eb293b8fSAndroid Build Coastguard Worker   std::shared_ptr<DexFileApi> api(new DexFileApi{std::move(dex), std::move(copy), std::mutex()});
122*eb293b8fSAndroid Build Coastguard Worker   return std::shared_ptr<DexFile>(new DexFile(base_addr, file_size, std::move(api)));
123*eb293b8fSAndroid Build Coastguard Worker }
124*eb293b8fSAndroid Build Coastguard Worker 
GetFunctionName(uint64_t dex_pc,SharedString * method_name,uint64_t * method_offset)125*eb293b8fSAndroid Build Coastguard Worker bool DexFile::GetFunctionName(uint64_t dex_pc, SharedString* method_name, uint64_t* method_offset) {
126*eb293b8fSAndroid Build Coastguard Worker   uint64_t dex_offset = dex_pc - base_addr_;  // Convert absolute PC to file-relative offset.
127*eb293b8fSAndroid Build Coastguard Worker 
128*eb293b8fSAndroid Build Coastguard Worker   // Lookup the function in the cache.
129*eb293b8fSAndroid Build Coastguard Worker   std::lock_guard<std::mutex> guard(dex_api_->lock_);  // Protect both the symbols and the C API.
130*eb293b8fSAndroid Build Coastguard Worker   auto it = symbols_.upper_bound(dex_offset);
131*eb293b8fSAndroid Build Coastguard Worker   if (it == symbols_.end() || dex_offset < it->second.offset) {
132*eb293b8fSAndroid Build Coastguard Worker     // Lookup the function in the underlying dex file.
133*eb293b8fSAndroid Build Coastguard Worker     size_t found = dex_api_->dex_->FindMethodAtOffset(dex_offset, [&](const auto& method) {
134*eb293b8fSAndroid Build Coastguard Worker       size_t code_size, name_size;
135*eb293b8fSAndroid Build Coastguard Worker       uint32_t offset = method.GetCodeOffset(&code_size);
136*eb293b8fSAndroid Build Coastguard Worker       const char* name = method.GetQualifiedName(/*with_params=*/false, &name_size);
137*eb293b8fSAndroid Build Coastguard Worker       it = symbols_.emplace(offset + code_size, Info{offset, std::string(name, name_size)}).first;
138*eb293b8fSAndroid Build Coastguard Worker     });
139*eb293b8fSAndroid Build Coastguard Worker     if (found == 0) {
140*eb293b8fSAndroid Build Coastguard Worker       return false;
141*eb293b8fSAndroid Build Coastguard Worker     }
142*eb293b8fSAndroid Build Coastguard Worker   }
143*eb293b8fSAndroid Build Coastguard Worker 
144*eb293b8fSAndroid Build Coastguard Worker   // Return the found function.
145*eb293b8fSAndroid Build Coastguard Worker   *method_offset = dex_offset - it->second.offset;
146*eb293b8fSAndroid Build Coastguard Worker   *method_name = it->second.name;
147*eb293b8fSAndroid Build Coastguard Worker   return true;
148*eb293b8fSAndroid Build Coastguard Worker }
149*eb293b8fSAndroid Build Coastguard Worker 
150*eb293b8fSAndroid Build Coastguard Worker }  // namespace unwindstack
151