Home | History | Annotate | Download | only in simpleperf
      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 <vector>
     25 
     26 #include <android-base/logging.h>
     27 
     28 #include "environment.h"
     29 #include "read_apk.h"
     30 #include "read_elf.h"
     31 #include "utils.h"
     32 
     33 static OneTimeFreeAllocator symbol_name_allocator;
     34 
     35 Symbol::Symbol(const std::string& name, uint64_t addr, uint64_t len)
     36     : addr(addr),
     37       len(len),
     38       name_(symbol_name_allocator.AllocateString(name)),
     39       demangled_name_(nullptr) {
     40 }
     41 
     42 const char* Symbol::DemangledName() const {
     43   if (demangled_name_ == nullptr) {
     44     const std::string s = Dso::Demangle(name_);
     45     if (s == name_) {
     46       demangled_name_ = name_;
     47     } else {
     48       demangled_name_ = symbol_name_allocator.AllocateString(s);
     49     }
     50   }
     51   return demangled_name_;
     52 }
     53 
     54 bool Dso::demangle_ = true;
     55 std::string Dso::symfs_dir_;
     56 std::string Dso::vmlinux_;
     57 std::unordered_map<std::string, BuildId> Dso::build_id_map_;
     58 size_t Dso::dso_count_;
     59 
     60 void Dso::SetDemangle(bool demangle) {
     61   demangle_ = demangle;
     62 }
     63 
     64 extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status);
     65 
     66 std::string Dso::Demangle(const std::string& name) {
     67   if (!demangle_) {
     68     return name;
     69   }
     70   int status;
     71   bool is_linker_symbol = (name.find(linker_prefix) == 0);
     72   const char* mangled_str = name.c_str();
     73   if (is_linker_symbol) {
     74     mangled_str += linker_prefix.size();
     75   }
     76   std::string result = name;
     77   char* demangled_name = __cxa_demangle(mangled_str, nullptr, nullptr, &status);
     78   if (status == 0) {
     79     if (is_linker_symbol) {
     80       result = std::string("[linker]") + demangled_name;
     81     } else {
     82       result = demangled_name;
     83     }
     84     free(demangled_name);
     85   } else if (is_linker_symbol) {
     86     result = std::string("[linker]") + mangled_str;
     87   }
     88   return result;
     89 }
     90 
     91 bool Dso::SetSymFsDir(const std::string& symfs_dir) {
     92   std::string dirname = symfs_dir;
     93   if (!dirname.empty()) {
     94     if (dirname.back() != '/') {
     95       dirname.push_back('/');
     96     }
     97     std::vector<std::string> files;
     98     std::vector<std::string> subdirs;
     99     GetEntriesInDir(symfs_dir, &files, &subdirs);
    100     if (files.empty() && subdirs.empty()) {
    101       LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir << "'";
    102       return false;
    103     }
    104   }
    105   symfs_dir_ = dirname;
    106   return true;
    107 }
    108 
    109 void Dso::SetVmlinux(const std::string& vmlinux) {
    110   vmlinux_ = vmlinux;
    111 }
    112 
    113 void Dso::SetBuildIds(const std::vector<std::pair<std::string, BuildId>>& build_ids) {
    114   std::unordered_map<std::string, BuildId> map;
    115   for (auto& pair : build_ids) {
    116     LOG(DEBUG) << "build_id_map: " << pair.first << ", " << pair.second.ToString();
    117     map.insert(pair);
    118   }
    119   build_id_map_ = std::move(map);
    120 }
    121 
    122 BuildId Dso::GetExpectedBuildId(const std::string& filename) {
    123   auto it = build_id_map_.find(filename);
    124   if (it != build_id_map_.end()) {
    125     return it->second;
    126   }
    127   return BuildId();
    128 }
    129 
    130 std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path) {
    131   std::string path = dso_path;
    132   if (dso_type == DSO_KERNEL) {
    133     path = "[kernel.kallsyms]";
    134   }
    135   return std::unique_ptr<Dso>(new Dso(dso_type, path));
    136 }
    137 
    138 Dso::Dso(DsoType type, const std::string& path)
    139     : type_(type), path_(path), min_vaddr_(std::numeric_limits<uint64_t>::max()), is_loaded_(false) {
    140   dso_count_++;
    141 }
    142 
    143 Dso::~Dso() {
    144   if (--dso_count_ == 0) {
    145     symbol_name_allocator.Clear();
    146   }
    147 }
    148 
    149 struct SymbolComparator {
    150   bool operator()(const Symbol& symbol1, const Symbol& symbol2) {
    151     return symbol1.addr < symbol2.addr;
    152   }
    153 };
    154 
    155 std::string Dso::GetAccessiblePath() const {
    156   return symfs_dir_ + path_;
    157 }
    158 
    159 const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) {
    160   if (!is_loaded_) {
    161     is_loaded_ = true;
    162     if (!Load()) {
    163       LOG(DEBUG) << "failed to load dso: " << path_;
    164       return nullptr;
    165     }
    166   }
    167 
    168   auto it = std::upper_bound(symbols_.begin(), symbols_.end(), Symbol("", vaddr_in_dso, 0),
    169                              SymbolComparator());
    170   if (it != symbols_.begin()) {
    171     --it;
    172     if (it->addr <= vaddr_in_dso && it->addr + it->len > vaddr_in_dso) {
    173       return &*it;
    174     }
    175   }
    176   return nullptr;
    177 }
    178 
    179 uint64_t Dso::MinVirtualAddress() {
    180   if (min_vaddr_ == std::numeric_limits<uint64_t>::max()) {
    181     min_vaddr_ = 0;
    182     if (type_ == DSO_ELF_FILE) {
    183       BuildId build_id = GetExpectedBuildId(GetAccessiblePath());
    184 
    185       uint64_t addr;
    186       if (ReadMinExecutableVirtualAddressFromElfFile(GetAccessiblePath(), build_id, &addr)) {
    187         min_vaddr_ = addr;
    188       }
    189     }
    190   }
    191   return min_vaddr_;
    192 }
    193 
    194 bool Dso::Load() {
    195   bool result = false;
    196   switch (type_) {
    197     case DSO_KERNEL:
    198       result = LoadKernel();
    199       break;
    200     case DSO_KERNEL_MODULE:
    201       result = LoadKernelModule();
    202       break;
    203     case DSO_ELF_FILE: {
    204       if (std::get<0>(SplitUrlInApk(path_))) {
    205         result = LoadEmbeddedElfFile();
    206       } else {
    207         result = LoadElfFile();
    208       }
    209       break;
    210     }
    211   }
    212   if (result) {
    213     std::sort(symbols_.begin(), symbols_.end(), SymbolComparator());
    214     FixupSymbolLength();
    215   }
    216   return result;
    217 }
    218 
    219 static bool IsKernelFunctionSymbol(const KernelSymbol& symbol) {
    220   return (symbol.type == 'T' || symbol.type == 't' || symbol.type == 'W' || symbol.type == 'w');
    221 }
    222 
    223 bool Dso::KernelSymbolCallback(const KernelSymbol& kernel_symbol, Dso* dso) {
    224   if (IsKernelFunctionSymbol(kernel_symbol)) {
    225     dso->InsertSymbol(Symbol(kernel_symbol.name, kernel_symbol.addr, 0));
    226   }
    227   return false;
    228 }
    229 
    230 void Dso::VmlinuxSymbolCallback(const ElfFileSymbol& elf_symbol, Dso* dso) {
    231   if (elf_symbol.is_func) {
    232     dso->InsertSymbol(Symbol(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len));
    233   }
    234 }
    235 
    236 bool Dso::LoadKernel() {
    237   BuildId build_id = GetExpectedBuildId(DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID);
    238   if (!vmlinux_.empty()) {
    239     ParseSymbolsFromElfFile(vmlinux_, build_id,
    240                             std::bind(VmlinuxSymbolCallback, std::placeholders::_1, this));
    241   } else {
    242     if (!build_id.IsEmpty()) {
    243       BuildId real_build_id;
    244       GetKernelBuildId(&real_build_id);
    245       bool match = (build_id == real_build_id);
    246       LOG(DEBUG) << "check kernel build id (" << (match ? "match" : "mismatch") << "): expected "
    247                  << build_id.ToString() << ", real " << real_build_id.ToString();
    248       if (!match) {
    249         return false;
    250       }
    251     }
    252 
    253     ProcessKernelSymbols("/proc/kallsyms",
    254                          std::bind(&KernelSymbolCallback, std::placeholders::_1, this));
    255     bool allZero = true;
    256     for (auto& symbol : symbols_) {
    257       if (symbol.addr != 0) {
    258         allZero = false;
    259         break;
    260       }
    261     }
    262     if (allZero) {
    263       LOG(WARNING) << "Symbol addresses in /proc/kallsyms are all zero. Check "
    264                       "/proc/sys/kernel/kptr_restrict if possible.";
    265       symbols_.clear();
    266       return false;
    267     }
    268   }
    269   return true;
    270 }
    271 
    272 void Dso::ElfFileSymbolCallback(const ElfFileSymbol& elf_symbol, Dso* dso,
    273                                 bool (*filter)(const ElfFileSymbol&)) {
    274   if (filter(elf_symbol)) {
    275     dso->InsertSymbol(Symbol(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len));
    276   }
    277 }
    278 
    279 static bool SymbolFilterForKernelModule(const ElfFileSymbol& elf_symbol) {
    280   // TODO: Parse symbol outside of .text section.
    281   return (elf_symbol.is_func && elf_symbol.is_in_text_section);
    282 }
    283 
    284 bool Dso::LoadKernelModule() {
    285   BuildId build_id = GetExpectedBuildId(path_);
    286   ParseSymbolsFromElfFile(
    287       symfs_dir_ + path_, build_id,
    288       std::bind(ElfFileSymbolCallback, std::placeholders::_1, this, SymbolFilterForKernelModule));
    289   return true;
    290 }
    291 
    292 static bool SymbolFilterForDso(const ElfFileSymbol& elf_symbol) {
    293   return elf_symbol.is_func || (elf_symbol.is_label && elf_symbol.is_in_text_section);
    294 }
    295 
    296 bool Dso::LoadElfFile() {
    297   bool loaded = false;
    298   BuildId build_id = GetExpectedBuildId(GetAccessiblePath());
    299 
    300   if (symfs_dir_.empty()) {
    301     // Linux host can store debug shared libraries in /usr/lib/debug.
    302     loaded = ParseSymbolsFromElfFile(
    303         "/usr/lib/debug" + path_, build_id,
    304         std::bind(ElfFileSymbolCallback, std::placeholders::_1, this, SymbolFilterForDso));
    305   }
    306   if (!loaded) {
    307     loaded = ParseSymbolsFromElfFile(
    308         GetAccessiblePath(), build_id,
    309         std::bind(ElfFileSymbolCallback, std::placeholders::_1, this, SymbolFilterForDso));
    310   }
    311   return loaded;
    312 }
    313 
    314 bool Dso::LoadEmbeddedElfFile() {
    315   std::string path = GetAccessiblePath();
    316   BuildId build_id = GetExpectedBuildId(path);
    317   auto tuple = SplitUrlInApk(path);
    318   CHECK(std::get<0>(tuple));
    319   return ParseSymbolsFromApkFile(std::get<1>(tuple), std::get<2>(tuple), build_id,
    320                                  std::bind(ElfFileSymbolCallback, std::placeholders::_1,
    321                                            this, SymbolFilterForDso));
    322 }
    323 
    324 void Dso::InsertSymbol(const Symbol& symbol) {
    325   symbols_.push_back(symbol);
    326 }
    327 
    328 void Dso::FixupSymbolLength() {
    329   Symbol* prev_symbol = nullptr;
    330   for (auto& symbol : symbols_) {
    331     if (prev_symbol != nullptr && prev_symbol->len == 0) {
    332       prev_symbol->len = symbol.addr - prev_symbol->addr;
    333     }
    334     prev_symbol = &symbol;
    335   }
    336   if (prev_symbol != nullptr && prev_symbol->len == 0) {
    337     prev_symbol->len = std::numeric_limits<unsigned long long>::max() - prev_symbol->addr;
    338   }
    339 }
    340