1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "dso.h"
18 
19 #include <stdlib.h>
20 #include <string.h>
21 
22 #include <algorithm>
23 #include <limits>
24 #include <memory>
25 #include <vector>
26 
27 #include <android-base/file.h>
28 #include <android-base/logging.h>
29 #include <android-base/strings.h>
30 
31 #include "environment.h"
32 #include "read_apk.h"
33 #include "read_dex_file.h"
34 #include "read_elf.h"
35 #include "utils.h"
36 
37 using namespace simpleperf;
38 
39 namespace simpleperf_dso_impl {
40 
RemovePathSeparatorSuffix(const std::string & path)41 std::string RemovePathSeparatorSuffix(const std::string& path) {
42   // Don't remove path separator suffix for '/'.
43   if (android::base::EndsWith(path, OS_PATH_SEPARATOR) && path.size() > 1u) {
44     return path.substr(0, path.size() - 1);
45   }
46   return path;
47 }
48 
Reset()49 void DebugElfFileFinder::Reset() {
50   vdso_64bit_.clear();
51   vdso_32bit_.clear();
52   symfs_dir_.clear();
53   build_id_to_file_map_.clear();
54 }
55 
SetSymFsDir(const std::string & symfs_dir)56 bool DebugElfFileFinder::SetSymFsDir(const std::string& symfs_dir) {
57   symfs_dir_ = RemovePathSeparatorSuffix(symfs_dir);
58   if (!IsDir(symfs_dir_)) {
59     LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir_ << "'";
60     return false;
61   }
62   std::string build_id_list_file = symfs_dir_ + OS_PATH_SEPARATOR + "build_id_list";
63   std::string build_id_list;
64   if (android::base::ReadFileToString(build_id_list_file, &build_id_list)) {
65     for (auto& line : android::base::Split(build_id_list, "\n")) {
66       std::vector<std::string> items = android::base::Split(line, "=");
67       if (items.size() == 2u) {
68         build_id_to_file_map_[items[0]] = symfs_dir_ + OS_PATH_SEPARATOR + items[1];
69       }
70     }
71   }
72   return true;
73 }
74 
AddSymbolDir(const std::string & symbol_dir)75 bool DebugElfFileFinder::AddSymbolDir(const std::string& symbol_dir) {
76   if (!IsDir(symbol_dir)) {
77     LOG(ERROR) << "Invalid symbol dir " << symbol_dir;
78     return false;
79   }
80   std::string dir = RemovePathSeparatorSuffix(symbol_dir);
81   CollectBuildIdInDir(dir);
82   return true;
83 }
84 
CollectBuildIdInDir(const std::string & dir)85 void DebugElfFileFinder::CollectBuildIdInDir(const std::string& dir) {
86   for (const std::string& entry : GetEntriesInDir(dir)) {
87     std::string path = dir + OS_PATH_SEPARATOR + entry;
88     if (IsDir(path)) {
89       CollectBuildIdInDir(path);
90     } else {
91       BuildId build_id;
92       ElfStatus status;
93       auto elf = ElfFile::Open(path, &status);
94       if (status == ElfStatus::NO_ERROR && elf->GetBuildId(&build_id) == ElfStatus::NO_ERROR) {
95         build_id_to_file_map_[build_id.ToString()] = path;
96       }
97     }
98   }
99 }
100 
SetVdsoFile(const std::string & vdso_file,bool is_64bit)101 void DebugElfFileFinder::SetVdsoFile(const std::string& vdso_file, bool is_64bit) {
102   if (is_64bit) {
103     vdso_64bit_ = vdso_file;
104   } else {
105     vdso_32bit_ = vdso_file;
106   }
107 }
108 
CheckDebugFilePath(const std::string & path,BuildId & build_id,bool report_build_id_mismatch)109 static bool CheckDebugFilePath(const std::string& path, BuildId& build_id,
110                                bool report_build_id_mismatch) {
111   ElfStatus status;
112   auto elf = ElfFile::Open(path, &status);
113   if (!elf) {
114     return false;
115   }
116   BuildId debug_build_id;
117   status = elf->GetBuildId(&debug_build_id);
118   if (status != ElfStatus::NO_ERROR && status != ElfStatus::NO_BUILD_ID) {
119     return false;
120   }
121 
122   // Native libraries in apks and kernel modules may not have build ids.
123   // So build_id and debug_build_id can either be empty, or have the same value.
124   bool match = build_id == debug_build_id;
125   if (!match && report_build_id_mismatch) {
126     LOG(WARNING) << path << " isn't used because of build id mismatch: expected " << build_id
127                  << ", real " << debug_build_id;
128   }
129   return match;
130 }
131 
FindDebugFile(const std::string & dso_path,bool force_64bit,BuildId & build_id)132 std::string DebugElfFileFinder::FindDebugFile(const std::string& dso_path, bool force_64bit,
133                                               BuildId& build_id) {
134   if (dso_path == "[vdso]") {
135     if (force_64bit && !vdso_64bit_.empty()) {
136       return vdso_64bit_;
137     } else if (!force_64bit && !vdso_32bit_.empty()) {
138       return vdso_32bit_;
139     }
140   }
141   if (build_id.IsEmpty()) {
142     // Try reading build id from file if we don't already have one.
143     GetBuildIdFromDsoPath(dso_path, &build_id);
144   }
145 
146   // 1. Try build_id_to_file_map.
147   if (!build_id_to_file_map_.empty()) {
148     if (!build_id.IsEmpty() || GetBuildIdFromDsoPath(dso_path, &build_id)) {
149       auto it = build_id_to_file_map_.find(build_id.ToString());
150       if (it != build_id_to_file_map_.end() && CheckDebugFilePath(it->second, build_id, false)) {
151         return it->second;
152       }
153     }
154   }
155   if (!symfs_dir_.empty()) {
156     // 2. Try concatenating symfs_dir and dso_path.
157     std::string path = GetPathInSymFsDir(dso_path);
158     if (CheckDebugFilePath(path, build_id, true)) {
159       return path;
160     }
161     // 3. Try concatenating symfs_dir and basename of dso_path.
162     path = symfs_dir_ + OS_PATH_SEPARATOR + android::base::Basename(dso_path);
163     if (CheckDebugFilePath(path, build_id, false)) {
164       return path;
165     }
166   }
167   // 4. Try concatenating /usr/lib/debug and dso_path.
168   // Linux host can store debug shared libraries in /usr/lib/debug.
169   if (CheckDebugFilePath("/usr/lib/debug" + dso_path, build_id, false)) {
170     return "/usr/lib/debug" + dso_path;
171   }
172   return dso_path;
173 }
174 
GetPathInSymFsDir(const std::string & path)175 std::string DebugElfFileFinder::GetPathInSymFsDir(const std::string& path) {
176   auto add_symfs_prefix = [&](const std::string& path) {
177     if (android::base::StartsWith(path, OS_PATH_SEPARATOR)) {
178       return symfs_dir_ + path;
179     }
180     return symfs_dir_ + OS_PATH_SEPARATOR + path;
181   };
182   if (OS_PATH_SEPARATOR == '/') {
183     return add_symfs_prefix(path);
184   }
185   // Paths in recorded perf.data uses '/' as path separator. When reporting on Windows, it needs
186   // to be converted to '\\'.
187   auto tuple = SplitUrlInApk(path);
188   if (std::get<0>(tuple)) {
189     std::string apk_path = std::get<1>(tuple);
190     std::string entry_path = std::get<2>(tuple);
191     std::replace(apk_path.begin(), apk_path.end(), '/', OS_PATH_SEPARATOR);
192     return GetUrlInApk(add_symfs_prefix(apk_path), entry_path);
193   }
194   std::string elf_path = path;
195   std::replace(elf_path.begin(), elf_path.end(), '/', OS_PATH_SEPARATOR);
196   return add_symfs_prefix(elf_path);
197 }
198 }  // namespace simpleperf_dso_imp
199 
200 static OneTimeFreeAllocator symbol_name_allocator;
201 
Symbol(std::string_view name,uint64_t addr,uint64_t len)202 Symbol::Symbol(std::string_view name, uint64_t addr, uint64_t len)
203     : addr(addr),
204       len(len),
205       name_(symbol_name_allocator.AllocateString(name)),
206       demangled_name_(nullptr),
207       dump_id_(UINT_MAX) {
208 }
209 
DemangledName() const210 const char* Symbol::DemangledName() const {
211   if (demangled_name_ == nullptr) {
212     const std::string s = Dso::Demangle(name_);
213     if (s == name_) {
214       demangled_name_ = name_;
215     } else {
216       demangled_name_ = symbol_name_allocator.AllocateString(s);
217     }
218   }
219   return demangled_name_;
220 }
221 
222 bool Dso::demangle_ = true;
223 std::string Dso::vmlinux_;
224 std::string Dso::kallsyms_;
225 bool Dso::read_kernel_symbols_from_proc_;
226 std::unordered_map<std::string, BuildId> Dso::build_id_map_;
227 size_t Dso::dso_count_;
228 uint32_t Dso::g_dump_id_;
229 simpleperf_dso_impl::DebugElfFileFinder Dso::debug_elf_file_finder_;
230 
SetDemangle(bool demangle)231 void Dso::SetDemangle(bool demangle) { demangle_ = demangle; }
232 
233 extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n,
234                                 int* status);
235 
Demangle(const std::string & name)236 std::string Dso::Demangle(const std::string& name) {
237   if (!demangle_) {
238     return name;
239   }
240   int status;
241   bool is_linker_symbol = (name.find(linker_prefix) == 0);
242   const char* mangled_str = name.c_str();
243   if (is_linker_symbol) {
244     mangled_str += linker_prefix.size();
245   }
246   std::string result = name;
247   char* demangled_name = __cxa_demangle(mangled_str, nullptr, nullptr, &status);
248   if (status == 0) {
249     if (is_linker_symbol) {
250       result = std::string("[linker]") + demangled_name;
251     } else {
252       result = demangled_name;
253     }
254     free(demangled_name);
255   } else if (is_linker_symbol) {
256     result = std::string("[linker]") + mangled_str;
257   }
258   return result;
259 }
260 
SetSymFsDir(const std::string & symfs_dir)261 bool Dso::SetSymFsDir(const std::string& symfs_dir) {
262   return debug_elf_file_finder_.SetSymFsDir(symfs_dir);
263 }
264 
AddSymbolDir(const std::string & symbol_dir)265 bool Dso::AddSymbolDir(const std::string& symbol_dir) {
266   return debug_elf_file_finder_.AddSymbolDir(symbol_dir);
267 }
268 
SetVmlinux(const std::string & vmlinux)269 void Dso::SetVmlinux(const std::string& vmlinux) { vmlinux_ = vmlinux; }
270 
SetBuildIds(const std::vector<std::pair<std::string,BuildId>> & build_ids)271 void Dso::SetBuildIds(
272     const std::vector<std::pair<std::string, BuildId>>& build_ids) {
273   std::unordered_map<std::string, BuildId> map;
274   for (auto& pair : build_ids) {
275     LOG(DEBUG) << "build_id_map: " << pair.first << ", "
276                << pair.second.ToString();
277     map.insert(pair);
278   }
279   build_id_map_ = std::move(map);
280 }
281 
SetVdsoFile(const std::string & vdso_file,bool is_64bit)282 void Dso::SetVdsoFile(const std::string& vdso_file, bool is_64bit) {
283   debug_elf_file_finder_.SetVdsoFile(vdso_file, is_64bit);
284 }
285 
FindExpectedBuildIdForPath(const std::string & path)286 BuildId Dso::FindExpectedBuildIdForPath(const std::string& path) {
287   auto it = build_id_map_.find(path);
288   if (it != build_id_map_.end()) {
289     return it->second;
290   }
291   return BuildId();
292 }
293 
GetExpectedBuildId()294 BuildId Dso::GetExpectedBuildId() {
295   return FindExpectedBuildIdForPath(path_);
296 }
297 
Dso(DsoType type,const std::string & path,const std::string & debug_file_path)298 Dso::Dso(DsoType type, const std::string& path, const std::string& debug_file_path)
299     : type_(type),
300       path_(path),
301       debug_file_path_(debug_file_path),
302       is_loaded_(false),
303       dump_id_(UINT_MAX),
304       symbol_dump_id_(0),
305       symbol_warning_loglevel_(android::base::WARNING) {
306   size_t pos = path.find_last_of("/\\");
307   if (pos != std::string::npos) {
308     file_name_ = path.substr(pos + 1);
309   } else {
310     file_name_ = path;
311   }
312   dso_count_++;
313 }
314 
~Dso()315 Dso::~Dso() {
316   if (--dso_count_ == 0) {
317     // Clean up global variables when no longer used.
318     symbol_name_allocator.Clear();
319     demangle_ = true;
320     vmlinux_.clear();
321     kallsyms_.clear();
322     read_kernel_symbols_from_proc_ = false;
323     build_id_map_.clear();
324     g_dump_id_ = 0;
325     debug_elf_file_finder_.Reset();
326   }
327 }
328 
CreateDumpId()329 uint32_t Dso::CreateDumpId() {
330   CHECK(!HasDumpId());
331   return dump_id_ = g_dump_id_++;
332 }
333 
CreateSymbolDumpId(const Symbol * symbol)334 uint32_t Dso::CreateSymbolDumpId(const Symbol* symbol) {
335   CHECK(!symbol->HasDumpId());
336   symbol->dump_id_ = symbol_dump_id_++;
337   return symbol->dump_id_;
338 }
339 
FindSymbol(uint64_t vaddr_in_dso)340 const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) {
341   if (!is_loaded_) {
342     Load();
343   }
344   auto it = std::upper_bound(symbols_.begin(), symbols_.end(),
345                              Symbol("", vaddr_in_dso, 0),
346                              Symbol::CompareValueByAddr);
347   if (it != symbols_.begin()) {
348     --it;
349     if (it->addr <= vaddr_in_dso && (it->addr + it->len > vaddr_in_dso)) {
350       return &*it;
351     }
352   }
353   if (!unknown_symbols_.empty()) {
354     auto it = unknown_symbols_.find(vaddr_in_dso);
355     if (it != unknown_symbols_.end()) {
356       return &it->second;
357     }
358   }
359   return nullptr;
360 }
361 
SetSymbols(std::vector<Symbol> * symbols)362 void Dso::SetSymbols(std::vector<Symbol>* symbols) {
363   symbols_ = std::move(*symbols);
364   symbols->clear();
365 }
366 
AddUnknownSymbol(uint64_t vaddr_in_dso,const std::string & name)367 void Dso::AddUnknownSymbol(uint64_t vaddr_in_dso, const std::string& name) {
368   unknown_symbols_.insert(std::make_pair(vaddr_in_dso, Symbol(name, vaddr_in_dso, 1)));
369 }
370 
IsForJavaMethod()371 bool Dso::IsForJavaMethod() {
372   if (type_ == DSO_DEX_FILE) {
373     return true;
374   }
375   if (type_ == DSO_ELF_FILE) {
376     // JITDebugReader generates jit symfiles in "jit_app_cache:<file_start>-<file_end>" format.
377     if (path_.find(':') != std::string::npos) {
378       return true;
379     }
380   }
381   return false;
382 }
383 
Load()384 void Dso::Load() {
385   is_loaded_ = true;
386   std::vector<Symbol> symbols = LoadSymbols();
387   if (symbols_.empty()) {
388     symbols_ = std::move(symbols);
389   } else {
390     std::vector<Symbol> merged_symbols;
391     std::set_union(symbols_.begin(), symbols_.end(), symbols.begin(), symbols.end(),
392                    std::back_inserter(merged_symbols), Symbol::CompareValueByAddr);
393     symbols_ = std::move(merged_symbols);
394   }
395 }
396 
ReportReadElfSymbolResult(ElfStatus result,const std::string & path,const std::string & debug_file_path,android::base::LogSeverity warning_loglevel=android::base::WARNING)397 static void ReportReadElfSymbolResult(ElfStatus result, const std::string& path,
398     const std::string& debug_file_path,
399     android::base::LogSeverity warning_loglevel = android::base::WARNING) {
400   if (result == ElfStatus::NO_ERROR) {
401     LOG(VERBOSE) << "Read symbols from " << debug_file_path << " successfully";
402   } else if (result == ElfStatus::NO_SYMBOL_TABLE) {
403     if (path == "[vdso]") {
404       // Vdso only contains dynamic symbol table, and we can't change that.
405       return;
406     }
407     // Lacking symbol table isn't considered as an error but worth reporting.
408     LOG(warning_loglevel) << debug_file_path << " doesn't contain symbol table";
409   } else {
410     LOG(warning_loglevel) << "failed to read symbols from " << debug_file_path << ": " << result;
411   }
412 }
413 
SortAndFixSymbols(std::vector<Symbol> & symbols)414 static void SortAndFixSymbols(std::vector<Symbol>& symbols) {
415   std::sort(symbols.begin(), symbols.end(), Symbol::CompareValueByAddr);
416   Symbol* prev_symbol = nullptr;
417   for (auto& symbol : symbols) {
418     if (prev_symbol != nullptr && prev_symbol->len == 0) {
419       prev_symbol->len = symbol.addr - prev_symbol->addr;
420     }
421     prev_symbol = &symbol;
422   }
423 }
424 
425 class DexFileDso : public Dso {
426  public:
DexFileDso(const std::string & path,const std::string & debug_file_path)427   DexFileDso(const std::string& path, const std::string& debug_file_path)
428       : Dso(DSO_DEX_FILE, path, debug_file_path) {}
429 
AddDexFileOffset(uint64_t dex_file_offset)430   void AddDexFileOffset(uint64_t dex_file_offset) override {
431     auto it = std::lower_bound(dex_file_offsets_.begin(), dex_file_offsets_.end(),
432                                dex_file_offset);
433     if (it != dex_file_offsets_.end() && *it == dex_file_offset) {
434       return;
435     }
436     dex_file_offsets_.insert(it, dex_file_offset);
437   }
438 
DexFileOffsets()439   const std::vector<uint64_t>* DexFileOffsets() override {
440     return &dex_file_offsets_;
441   }
442 
IpToVaddrInFile(uint64_t ip,uint64_t map_start,uint64_t map_pgoff)443   uint64_t IpToVaddrInFile(uint64_t ip, uint64_t map_start, uint64_t map_pgoff) override {
444     return ip - map_start + map_pgoff;
445   }
446 
LoadSymbols()447   std::vector<Symbol> LoadSymbols() override {
448     std::vector<Symbol> symbols;
449     std::vector<DexFileSymbol> dex_file_symbols;
450     auto tuple = SplitUrlInApk(debug_file_path_);
451     bool status = false;
452     if (std::get<0>(tuple)) {
453       std::unique_ptr<ArchiveHelper> ahelper = ArchiveHelper::CreateInstance(std::get<1>(tuple));
454       ZipEntry entry;
455       std::vector<uint8_t> data;
456       if (ahelper &&
457           ahelper->FindEntry(std::get<2>(tuple), &entry) && ahelper->GetEntryData(entry, &data)) {
458         status = ReadSymbolsFromDexFileInMemory(data.data(), data.size(), dex_file_offsets_,
459                                                 &dex_file_symbols);
460       }
461     } else {
462       status = ReadSymbolsFromDexFile(debug_file_path_, dex_file_offsets_, &dex_file_symbols);
463     }
464     if (!status) {
465       android::base::LogSeverity level = symbols_.empty() ? android::base::WARNING
466                                                           : android::base::DEBUG;
467       LOG(level) << "Failed to read symbols from " << debug_file_path_;
468       return symbols;
469     }
470     LOG(VERBOSE) << "Read symbols from " << debug_file_path_ << " successfully";
471     for (auto& symbol : dex_file_symbols) {
472       symbols.emplace_back(symbol.name, symbol.offset, symbol.len);
473     }
474     SortAndFixSymbols(symbols);
475     return symbols;
476   }
477 
478  private:
479   std::vector<uint64_t> dex_file_offsets_;
480 };
481 
482 class ElfDso : public Dso {
483  public:
ElfDso(const std::string & path,const std::string & debug_file_path)484   ElfDso(const std::string& path, const std::string& debug_file_path)
485       : Dso(DSO_ELF_FILE, path, debug_file_path) {}
486 
GetReportPath() const487   std::string_view GetReportPath() const override {
488     if (size_t colon_pos = path_.find(':'); colon_pos != std::string::npos) {
489       return "[JIT app cache]";
490     }
491     return path_;
492   }
493 
SetMinExecutableVaddr(uint64_t min_vaddr,uint64_t file_offset)494   void SetMinExecutableVaddr(uint64_t min_vaddr, uint64_t file_offset) override {
495     min_vaddr_ = min_vaddr;
496     file_offset_of_min_vaddr_ = file_offset;
497   }
498 
GetMinExecutableVaddr(uint64_t * min_vaddr,uint64_t * file_offset)499   void GetMinExecutableVaddr(uint64_t* min_vaddr, uint64_t* file_offset) override {
500     if (type_ == DSO_DEX_FILE) {
501       return dex_file_dso_->GetMinExecutableVaddr(min_vaddr, file_offset);
502     }
503     if (min_vaddr_ == uninitialized_value) {
504       min_vaddr_ = 0;
505       BuildId build_id = GetExpectedBuildId();
506 
507       ElfStatus status;
508       auto elf = ElfFile::Open(debug_file_path_, &build_id, &status);
509       if (elf) {
510         min_vaddr_ = elf->ReadMinExecutableVaddr(&file_offset_of_min_vaddr_);
511       } else {
512         LOG(WARNING) << "failed to read min virtual address of " << debug_file_path_ << ": "
513                      << status;
514       }
515     }
516     *min_vaddr = min_vaddr_;
517     *file_offset = file_offset_of_min_vaddr_;
518   }
519 
IpToVaddrInFile(uint64_t ip,uint64_t map_start,uint64_t map_pgoff)520   uint64_t IpToVaddrInFile(uint64_t ip, uint64_t map_start, uint64_t map_pgoff) override {
521     if (type_ == DSO_DEX_FILE) {
522       return dex_file_dso_->IpToVaddrInFile(ip, map_start, map_pgoff);
523     }
524     uint64_t min_vaddr;
525     uint64_t file_offset_of_min_vaddr;
526     GetMinExecutableVaddr(&min_vaddr, &file_offset_of_min_vaddr);
527     if (file_offset_of_min_vaddr == uninitialized_value) {
528       return ip - map_start + min_vaddr;
529     }
530     // Apps may make part of the executable segment of a shared library writeable, which can
531     // generate multiple executable segments at runtime. So use map_pgoff to calculate
532     // vaddr_in_file.
533     return ip - map_start + map_pgoff - file_offset_of_min_vaddr + min_vaddr;
534   }
535 
AddDexFileOffset(uint64_t dex_file_offset)536   void AddDexFileOffset(uint64_t dex_file_offset) override {
537     if (type_ == DSO_ELF_FILE) {
538       // When simpleperf does unwinding while recording, it processes mmap records before reading
539       // dex file linked list (via JITDebugReader). To process mmap records, it creates Dso
540       // objects of type ELF_FILE. Then after reading dex file linked list, it realizes some
541       // ELF_FILE Dso objects should actually be DEX_FILE, because they have dex file offsets.
542       // So here converts ELF_FILE Dso into DEX_FILE Dso.
543       type_ = DSO_DEX_FILE;
544       dex_file_dso_.reset(new DexFileDso(path_, path_));
545     }
546     dex_file_dso_->AddDexFileOffset(dex_file_offset);
547   }
548 
DexFileOffsets()549   const std::vector<uint64_t>* DexFileOffsets() override {
550     return dex_file_dso_ ? dex_file_dso_->DexFileOffsets() : nullptr;
551   }
552 
553  protected:
LoadSymbols()554   std::vector<Symbol> LoadSymbols() override {
555     if (dex_file_dso_) {
556       return dex_file_dso_->LoadSymbols();
557     }
558     std::vector<Symbol> symbols;
559     BuildId build_id = GetExpectedBuildId();
560     auto symbol_callback = [&](const ElfFileSymbol& symbol) {
561       if (symbol.is_func || (symbol.is_label && symbol.is_in_text_section)) {
562         symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len);
563       }
564     };
565     ElfStatus status;
566     auto elf = ElfFile::Open(debug_file_path_, &build_id, &status);
567     if (elf) {
568       status = elf->ParseSymbols(symbol_callback);
569     }
570     ReportReadElfSymbolResult(status, path_, debug_file_path_,
571                               symbols_.empty() ? android::base::WARNING : android::base::DEBUG);
572     SortAndFixSymbols(symbols);
573     return symbols;
574   }
575 
576  private:
577   static constexpr uint64_t uninitialized_value = std::numeric_limits<uint64_t>::max();
578 
579   uint64_t min_vaddr_ = uninitialized_value;
580   uint64_t file_offset_of_min_vaddr_ = uninitialized_value;
581   std::unique_ptr<DexFileDso> dex_file_dso_;
582 };
583 
584 class KernelDso : public Dso {
585  public:
KernelDso(const std::string & path,const std::string & debug_file_path)586   KernelDso(const std::string& path, const std::string& debug_file_path)
587       : Dso(DSO_KERNEL, path, debug_file_path) {}
588 
IpToVaddrInFile(uint64_t ip,uint64_t,uint64_t)589   uint64_t IpToVaddrInFile(uint64_t ip, uint64_t, uint64_t) override {
590     return ip;
591   }
592 
593  protected:
LoadSymbols()594   std::vector<Symbol> LoadSymbols() override {
595     std::vector<Symbol> symbols;
596     BuildId build_id = GetExpectedBuildId();
597     if (!vmlinux_.empty()) {
598       auto symbol_callback = [&](const ElfFileSymbol& symbol) {
599         if (symbol.is_func) {
600           symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len);
601         }
602       };
603       ElfStatus status;
604       auto elf = ElfFile::Open(vmlinux_, &build_id, &status);
605       if (elf) {
606         status = elf->ParseSymbols(symbol_callback);
607       }
608       ReportReadElfSymbolResult(status, path_, vmlinux_);
609     } else if (!kallsyms_.empty()) {
610       symbols = ReadSymbolsFromKallsyms(kallsyms_);
611     } else if (read_kernel_symbols_from_proc_ || !build_id.IsEmpty()) {
612       // Try /proc/kallsyms only when asked to do so, or when build id matches.
613       // Otherwise, it is likely to use /proc/kallsyms on host for perf.data recorded on device.
614       bool can_read_kallsyms = true;
615       if (!build_id.IsEmpty()) {
616         BuildId real_build_id;
617         if (!GetKernelBuildId(&real_build_id) || build_id != real_build_id) {
618           LOG(DEBUG) << "failed to read symbols from /proc/kallsyms: Build id mismatch";
619           can_read_kallsyms = false;
620         }
621       }
622       if (can_read_kallsyms) {
623         std::string kallsyms;
624         if (!android::base::ReadFileToString("/proc/kallsyms", &kallsyms)) {
625           LOG(DEBUG) << "failed to read /proc/kallsyms";
626         } else {
627           symbols = ReadSymbolsFromKallsyms(kallsyms);
628         }
629       }
630     }
631     SortAndFixSymbols(symbols);
632     if (!symbols.empty()) {
633       symbols.back().len = std::numeric_limits<uint64_t>::max() - symbols.back().addr;
634     }
635     return symbols;
636   }
637 
638  private:
ReadSymbolsFromKallsyms(std::string & kallsyms)639   std::vector<Symbol> ReadSymbolsFromKallsyms(std::string& kallsyms) {
640     std::vector<Symbol> symbols;
641     auto symbol_callback = [&](const KernelSymbol& symbol) {
642       if (strchr("TtWw", symbol.type) && symbol.addr != 0u) {
643         symbols.emplace_back(symbol.name, symbol.addr, 0);
644       }
645       return false;
646     };
647     ProcessKernelSymbols(kallsyms, symbol_callback);
648     if (symbols.empty()) {
649       LOG(WARNING) << "Symbol addresses in /proc/kallsyms on device are all zero. "
650                       "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible.";
651     }
652     return symbols;
653   }
654 };
655 
656 class KernelModuleDso : public Dso {
657  public:
KernelModuleDso(const std::string & path,const std::string & debug_file_path)658   KernelModuleDso(const std::string& path, const std::string& debug_file_path)
659       : Dso(DSO_KERNEL_MODULE, path, debug_file_path) {}
660 
IpToVaddrInFile(uint64_t ip,uint64_t map_start,uint64_t)661   uint64_t IpToVaddrInFile(uint64_t ip, uint64_t map_start, uint64_t) override {
662     return ip - map_start;
663   }
664 
665  protected:
LoadSymbols()666   std::vector<Symbol> LoadSymbols() override {
667     std::vector<Symbol> symbols;
668     BuildId build_id = GetExpectedBuildId();
669     auto symbol_callback = [&](const ElfFileSymbol& symbol) {
670       if (symbol.is_func || symbol.is_in_text_section) {
671         symbols.emplace_back(symbol.name, symbol.vaddr, symbol.len);
672       }
673     };
674     ElfStatus status;
675     auto elf = ElfFile::Open(debug_file_path_, &build_id, &status);
676     if (elf) {
677       status = elf->ParseSymbols(symbol_callback);
678     }
679     ReportReadElfSymbolResult(status, path_, debug_file_path_,
680                               symbols_.empty() ? android::base::WARNING : android::base::DEBUG);
681     SortAndFixSymbols(symbols);
682     return symbols;
683   }
684 };
685 
686 class UnknownDso : public Dso {
687  public:
UnknownDso(const std::string & path)688   UnknownDso(const std::string& path) : Dso(DSO_UNKNOWN_FILE, path, path) {}
689 
IpToVaddrInFile(uint64_t ip,uint64_t,uint64_t)690   uint64_t IpToVaddrInFile(uint64_t ip, uint64_t, uint64_t) override {
691     return ip;
692   }
693 
694  protected:
LoadSymbols()695   std::vector<Symbol> LoadSymbols() override {
696     return std::vector<Symbol>();
697   }
698 };
699 
CreateDso(DsoType dso_type,const std::string & dso_path,bool force_64bit)700 std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path,
701                                     bool force_64bit) {
702   switch (dso_type) {
703     case DSO_ELF_FILE: {
704       BuildId build_id = FindExpectedBuildIdForPath(dso_path);
705       return std::unique_ptr<Dso>(new ElfDso(dso_path,
706           debug_elf_file_finder_.FindDebugFile(dso_path, force_64bit, build_id)));
707     }
708     case DSO_KERNEL:
709       return std::unique_ptr<Dso>(new KernelDso(dso_path, dso_path));
710     case DSO_KERNEL_MODULE: {
711       BuildId build_id = FindExpectedBuildIdForPath(dso_path);
712       return std::unique_ptr<Dso>(new KernelModuleDso(
713           dso_path, debug_elf_file_finder_.FindDebugFile(dso_path, force_64bit, build_id)));
714     }
715     case DSO_DEX_FILE:
716       return std::unique_ptr<Dso>(new DexFileDso(dso_path, dso_path));
717     case DSO_UNKNOWN_FILE:
718       return std::unique_ptr<Dso>(new UnknownDso(dso_path));
719     default:
720       LOG(FATAL) << "Unexpected dso_type " << static_cast<int>(dso_type);
721   }
722   return nullptr;
723 }
724 
CreateElfDsoWithBuildId(const std::string & dso_path,BuildId & build_id)725 std::unique_ptr<Dso> Dso::CreateElfDsoWithBuildId(const std::string& dso_path, BuildId& build_id) {
726   return std::unique_ptr<Dso>(
727       new ElfDso(dso_path, debug_elf_file_finder_.FindDebugFile(dso_path, false, build_id)));
728 }
729 
DsoTypeToString(DsoType dso_type)730 const char* DsoTypeToString(DsoType dso_type) {
731   switch (dso_type) {
732     case DSO_KERNEL:
733       return "dso_kernel";
734     case DSO_KERNEL_MODULE:
735       return "dso_kernel_module";
736     case DSO_ELF_FILE:
737       return "dso_elf_file";
738     case DSO_DEX_FILE:
739       return "dso_dex_file";
740     default:
741       return "unknown";
742   }
743 }
744 
GetBuildIdFromDsoPath(const std::string & dso_path,BuildId * build_id)745 bool GetBuildIdFromDsoPath(const std::string& dso_path, BuildId* build_id) {
746   ElfStatus status;
747   auto elf = ElfFile::Open(dso_path, &status);
748   if (status == ElfStatus::NO_ERROR && elf->GetBuildId(build_id) == ElfStatus::NO_ERROR) {
749     return true;
750   }
751   return false;
752 }
753