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 33static OneTimeFreeAllocator symbol_name_allocator; 34 35Symbol::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 42const 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 54bool Dso::demangle_ = true; 55std::string Dso::symfs_dir_; 56std::string Dso::vmlinux_; 57std::unordered_map<std::string, BuildId> Dso::build_id_map_; 58size_t Dso::dso_count_; 59 60void Dso::SetDemangle(bool demangle) { 61 demangle_ = demangle; 62} 63 64extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status); 65 66std::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 91bool 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 109void Dso::SetVmlinux(const std::string& vmlinux) { 110 vmlinux_ = vmlinux; 111} 112 113void 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 122BuildId 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 130std::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 138Dso::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 143Dso::~Dso() { 144 if (--dso_count_ == 0) { 145 symbol_name_allocator.Clear(); 146 } 147} 148 149struct SymbolComparator { 150 bool operator()(const Symbol& symbol1, const Symbol& symbol2) { 151 return symbol1.addr < symbol2.addr; 152 } 153}; 154 155std::string Dso::GetAccessiblePath() const { 156 return symfs_dir_ + path_; 157} 158 159const 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 179uint64_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 194bool 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 219static bool IsKernelFunctionSymbol(const KernelSymbol& symbol) { 220 return (symbol.type == 'T' || symbol.type == 't' || symbol.type == 'W' || symbol.type == 'w'); 221} 222 223bool 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 230void 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 236bool 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 272void 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 279static 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 284bool 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 292static bool SymbolFilterForDso(const ElfFileSymbol& elf_symbol) { 293 return elf_symbol.is_func || (elf_symbol.is_label && elf_symbol.is_in_text_section); 294} 295 296bool 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 314bool 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 324void Dso::InsertSymbol(const Symbol& symbol) { 325 symbols_.push_back(symbol); 326} 327 328void 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