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/file.h> 27 #include <android-base/logging.h> 28 29 #include "environment.h" 30 #include "read_apk.h" 31 #include "read_elf.h" 32 #include "utils.h" 33 34 static OneTimeFreeAllocator symbol_name_allocator; 35 36 Symbol::Symbol(const std::string& name, uint64_t addr, uint64_t len) 37 : addr(addr), 38 len(len), 39 name_(symbol_name_allocator.AllocateString(name)), 40 demangled_name_(nullptr), 41 dump_id_(UINT_MAX) {} 42 43 const char* Symbol::DemangledName() const { 44 if (demangled_name_ == nullptr) { 45 const std::string s = Dso::Demangle(name_); 46 if (s == name_) { 47 demangled_name_ = name_; 48 } else { 49 demangled_name_ = symbol_name_allocator.AllocateString(s); 50 } 51 } 52 return demangled_name_; 53 } 54 55 bool Dso::demangle_ = true; 56 std::string Dso::symfs_dir_; 57 std::string Dso::vmlinux_; 58 std::string Dso::kallsyms_; 59 bool Dso::read_kernel_symbols_from_proc_; 60 std::unordered_map<std::string, BuildId> Dso::build_id_map_; 61 size_t Dso::dso_count_; 62 uint32_t Dso::g_dump_id_; 63 std::string Dso::vdso_64bit_; 64 std::string Dso::vdso_32bit_; 65 66 void Dso::SetDemangle(bool demangle) { demangle_ = demangle; } 67 68 extern "C" char* __cxa_demangle(const char* mangled_name, char* buf, size_t* n, 69 int* status); 70 71 std::string Dso::Demangle(const std::string& name) { 72 if (!demangle_) { 73 return name; 74 } 75 int status; 76 bool is_linker_symbol = (name.find(linker_prefix) == 0); 77 const char* mangled_str = name.c_str(); 78 if (is_linker_symbol) { 79 mangled_str += linker_prefix.size(); 80 } 81 std::string result = name; 82 char* demangled_name = __cxa_demangle(mangled_str, nullptr, nullptr, &status); 83 if (status == 0) { 84 if (is_linker_symbol) { 85 result = std::string("[linker]") + demangled_name; 86 } else { 87 result = demangled_name; 88 } 89 free(demangled_name); 90 } else if (is_linker_symbol) { 91 result = std::string("[linker]") + mangled_str; 92 } 93 return result; 94 } 95 96 bool Dso::SetSymFsDir(const std::string& symfs_dir) { 97 std::string dirname = symfs_dir; 98 if (!dirname.empty()) { 99 if (dirname.back() != '/') { 100 dirname.push_back('/'); 101 } 102 if (!IsDir(symfs_dir)) { 103 LOG(ERROR) << "Invalid symfs_dir '" << symfs_dir << "'"; 104 return false; 105 } 106 } 107 symfs_dir_ = dirname; 108 return true; 109 } 110 111 void Dso::SetVmlinux(const std::string& vmlinux) { vmlinux_ = vmlinux; } 112 113 void Dso::SetBuildIds( 114 const std::vector<std::pair<std::string, BuildId>>& build_ids) { 115 std::unordered_map<std::string, BuildId> map; 116 for (auto& pair : build_ids) { 117 LOG(DEBUG) << "build_id_map: " << pair.first << ", " 118 << pair.second.ToString(); 119 map.insert(pair); 120 } 121 build_id_map_ = std::move(map); 122 } 123 124 void Dso::SetVdsoFile(const std::string& vdso_file, bool is_64bit) { 125 if (is_64bit) { 126 vdso_64bit_ = vdso_file; 127 } else { 128 vdso_32bit_ = vdso_file; 129 } 130 } 131 132 BuildId Dso::FindExpectedBuildIdForPath(const std::string& path) { 133 auto it = build_id_map_.find(path); 134 if (it != build_id_map_.end()) { 135 return it->second; 136 } 137 return BuildId(); 138 } 139 140 BuildId Dso::GetExpectedBuildId() { 141 return FindExpectedBuildIdForPath(path_); 142 } 143 144 std::unique_ptr<Dso> Dso::CreateDso(DsoType dso_type, const std::string& dso_path, 145 bool force_64bit) { 146 return std::unique_ptr<Dso>(new Dso(dso_type, dso_path, force_64bit)); 147 } 148 149 Dso::Dso(DsoType type, const std::string& path, bool force_64bit) 150 : type_(type), 151 path_(path), 152 debug_file_path_(path), 153 min_vaddr_(std::numeric_limits<uint64_t>::max()), 154 is_loaded_(false), 155 dump_id_(UINT_MAX), 156 symbol_dump_id_(0), 157 symbol_warning_loglevel_(android::base::WARNING) { 158 if (type_ == DSO_KERNEL) { 159 min_vaddr_ = 0; 160 } 161 // Check if file matching path_ exists in symfs directory before using it as 162 // debug_file_path_. 163 if (!symfs_dir_.empty()) { 164 std::string path_in_symfs = symfs_dir_ + path_; 165 std::tuple<bool, std::string, std::string> tuple = 166 SplitUrlInApk(path_in_symfs); 167 std::string file_path = 168 std::get<0>(tuple) ? std::get<1>(tuple) : path_in_symfs; 169 if (IsRegularFile(file_path)) { 170 debug_file_path_ = path_in_symfs; 171 } 172 } else if (path == "[vdso]") { 173 if (force_64bit && !vdso_64bit_.empty()) { 174 debug_file_path_ = vdso_64bit_; 175 } else if (!force_64bit && !vdso_32bit_.empty()) { 176 debug_file_path_ = vdso_32bit_; 177 } 178 } 179 size_t pos = path.find_last_of("/\\"); 180 if (pos != std::string::npos) { 181 file_name_ = path.substr(pos + 1); 182 } else { 183 file_name_ = path; 184 } 185 dso_count_++; 186 } 187 188 Dso::~Dso() { 189 if (--dso_count_ == 0) { 190 // Clean up global variables when no longer used. 191 symbol_name_allocator.Clear(); 192 demangle_ = true; 193 symfs_dir_.clear(); 194 vmlinux_.clear(); 195 kallsyms_.clear(); 196 read_kernel_symbols_from_proc_ = false; 197 build_id_map_.clear(); 198 g_dump_id_ = 0; 199 } 200 } 201 202 uint32_t Dso::CreateDumpId() { 203 CHECK(!HasDumpId()); 204 return dump_id_ = g_dump_id_++; 205 } 206 207 uint32_t Dso::CreateSymbolDumpId(const Symbol* symbol) { 208 CHECK(!symbol->HasDumpId()); 209 symbol->dump_id_ = symbol_dump_id_++; 210 return symbol->dump_id_; 211 } 212 213 const Symbol* Dso::FindSymbol(uint64_t vaddr_in_dso) { 214 if (!is_loaded_) { 215 Load(); 216 } 217 if (!symbols_.empty()) { 218 auto it = std::upper_bound(symbols_.begin(), symbols_.end(), 219 Symbol("", vaddr_in_dso, 0), 220 Symbol::CompareValueByAddr); 221 if (it != symbols_.begin()) { 222 --it; 223 if (it->addr <= vaddr_in_dso && (it->addr + it->len > vaddr_in_dso)) { 224 return &*it; 225 } 226 } 227 } 228 if (!unknown_symbols_.empty()) { 229 auto it = unknown_symbols_.find(vaddr_in_dso); 230 if (it != unknown_symbols_.end()) { 231 return &it->second; 232 } 233 } 234 return nullptr; 235 } 236 237 const std::vector<Symbol>& Dso::GetSymbols() { 238 if (!is_loaded_) { 239 Load(); 240 } 241 return symbols_; 242 } 243 244 void Dso::SetSymbols(std::vector<Symbol>* symbols) { 245 symbols_ = std::move(*symbols); 246 symbols->clear(); 247 } 248 249 void Dso::AddUnknownSymbol(uint64_t vaddr_in_dso, const std::string& name) { 250 unknown_symbols_.insert(std::make_pair(vaddr_in_dso, Symbol(name, vaddr_in_dso, 1))); 251 } 252 253 uint64_t Dso::MinVirtualAddress() { 254 if (min_vaddr_ == std::numeric_limits<uint64_t>::max()) { 255 min_vaddr_ = 0; 256 if (type_ == DSO_ELF_FILE) { 257 BuildId build_id = GetExpectedBuildId(); 258 259 uint64_t addr; 260 ElfStatus result = ReadMinExecutableVirtualAddressFromElfFile( 261 GetDebugFilePath(), build_id, &addr); 262 if (result != ElfStatus::NO_ERROR) { 263 LOG(WARNING) << "failed to read min virtual address of " 264 << GetDebugFilePath() << ": " << result; 265 } else { 266 min_vaddr_ = addr; 267 } 268 } 269 } 270 return min_vaddr_; 271 } 272 273 static std::vector<Symbol> MergeSortedSymbols(const std::vector<Symbol>& s1, 274 const std::vector<Symbol>& s2) { 275 std::vector<Symbol> result; 276 std::set_union(s1.begin(), s1.end(), s2.begin(), s2.end(), std::back_inserter(result), 277 Symbol::CompareValueByAddr); 278 return result; 279 } 280 281 void Dso::Load() { 282 is_loaded_ = true; 283 std::vector<Symbol> dumped_symbols; 284 if (!symbols_.empty()) { 285 // If symbols has been read from file feature section of perf.data, move it to 286 // dumped_symbols, so later we can merge them with symbols read from file system. 287 dumped_symbols = std::move(symbols_); 288 symbols_.clear(); 289 // Don't warn missing symbol table if we have dumped symbols in perf.data. 290 symbol_warning_loglevel_ = android::base::DEBUG; 291 } 292 bool result = false; 293 switch (type_) { 294 case DSO_KERNEL: 295 result = LoadKernel(); 296 break; 297 case DSO_KERNEL_MODULE: 298 result = LoadKernelModule(); 299 break; 300 case DSO_ELF_FILE: { 301 if (std::get<0>(SplitUrlInApk(path_))) { 302 result = LoadEmbeddedElfFile(); 303 } else { 304 result = LoadElfFile(); 305 } 306 break; 307 } 308 } 309 if (result) { 310 std::sort(symbols_.begin(), symbols_.end(), Symbol::CompareValueByAddr); 311 FixupSymbolLength(); 312 } else { 313 symbols_.clear(); 314 } 315 316 if (symbols_.empty()) { 317 symbols_ = std::move(dumped_symbols); 318 } else if (!dumped_symbols.empty()) { 319 symbols_ = MergeSortedSymbols(symbols_, dumped_symbols); 320 } 321 322 if (symbols_.empty()) { 323 LOG(DEBUG) << "failed to load dso: " << path_; 324 } 325 } 326 327 static bool IsKernelFunctionSymbol(const KernelSymbol& symbol) { 328 return (symbol.type == 'T' || symbol.type == 't' || symbol.type == 'W' || 329 symbol.type == 'w'); 330 } 331 332 static bool KernelSymbolCallback(const KernelSymbol& kernel_symbol, 333 std::vector<Symbol>* symbols) { 334 if (IsKernelFunctionSymbol(kernel_symbol)) { 335 symbols->emplace_back(Symbol(kernel_symbol.name, kernel_symbol.addr, 0)); 336 } 337 return false; 338 } 339 340 static void VmlinuxSymbolCallback(const ElfFileSymbol& elf_symbol, 341 std::vector<Symbol>* symbols) { 342 if (elf_symbol.is_func) { 343 symbols->emplace_back( 344 Symbol(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len)); 345 } 346 } 347 348 bool Dso::CheckReadSymbolResult(ElfStatus result, const std::string& filename) { 349 if (result == ElfStatus::NO_ERROR) { 350 LOG(VERBOSE) << "Read symbols from " << filename << " successfully"; 351 return true; 352 } else if (result == ElfStatus::NO_SYMBOL_TABLE) { 353 if (path_ == "[vdso]") { 354 // Vdso only contains dynamic symbol table, and we can't change that. 355 return true; 356 } 357 // Lacking symbol table isn't considered as an error but worth reporting. 358 LOG(symbol_warning_loglevel_) << filename << " doesn't contain symbol table"; 359 return true; 360 } else { 361 LOG(symbol_warning_loglevel_) << "failed to read symbols from " << filename << ": " << result; 362 return false; 363 } 364 } 365 366 bool Dso::LoadKernel() { 367 BuildId build_id = GetExpectedBuildId(); 368 if (!vmlinux_.empty()) { 369 ElfStatus result = ParseSymbolsFromElfFile(vmlinux_, build_id, 370 std::bind(VmlinuxSymbolCallback, std::placeholders::_1, &symbols_)); 371 return CheckReadSymbolResult(result, vmlinux_); 372 } else if (!kallsyms_.empty()) { 373 ProcessKernelSymbols(kallsyms_, std::bind(&KernelSymbolCallback, 374 std::placeholders::_1, &symbols_)); 375 bool all_zero = true; 376 for (const auto& symbol : symbols_) { 377 if (symbol.addr != 0) { 378 all_zero = false; 379 break; 380 } 381 } 382 if (all_zero) { 383 LOG(symbol_warning_loglevel_) 384 << "Symbol addresses in /proc/kallsyms on device are all zero. " 385 "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible."; 386 symbols_.clear(); 387 return false; 388 } 389 } else if (read_kernel_symbols_from_proc_ || !build_id.IsEmpty()) { 390 // Try /proc/kallsyms only when asked to do so, or when build id matches. 391 // Otherwise, it is likely to use /proc/kallsyms on host for perf.data recorded on device. 392 if (!build_id.IsEmpty()) { 393 BuildId real_build_id; 394 if (!GetKernelBuildId(&real_build_id)) { 395 return false; 396 } 397 bool match = (build_id == real_build_id); 398 if (!match) { 399 LOG(symbol_warning_loglevel_) << "failed to read symbols from /proc/kallsyms: Build id " 400 << "mismatch"; 401 return false; 402 } 403 } 404 405 std::string kallsyms; 406 if (!android::base::ReadFileToString("/proc/kallsyms", &kallsyms)) { 407 LOG(DEBUG) << "failed to read /proc/kallsyms"; 408 return false; 409 } 410 ProcessKernelSymbols(kallsyms, std::bind(&KernelSymbolCallback, 411 std::placeholders::_1, &symbols_)); 412 bool all_zero = true; 413 for (const auto& symbol : symbols_) { 414 if (symbol.addr != 0) { 415 all_zero = false; 416 break; 417 } 418 } 419 if (all_zero) { 420 LOG(symbol_warning_loglevel_) << "Symbol addresses in /proc/kallsyms are all zero. " 421 "`echo 0 >/proc/sys/kernel/kptr_restrict` if possible."; 422 symbols_.clear(); 423 return false; 424 } 425 } 426 return true; 427 } 428 429 static void ElfFileSymbolCallback(const ElfFileSymbol& elf_symbol, 430 bool (*filter)(const ElfFileSymbol&), 431 std::vector<Symbol>* symbols) { 432 if (filter(elf_symbol)) { 433 symbols->emplace_back(elf_symbol.name, elf_symbol.vaddr, elf_symbol.len); 434 } 435 } 436 437 static bool SymbolFilterForKernelModule(const ElfFileSymbol& elf_symbol) { 438 // TODO: Parse symbol outside of .text section. 439 return (elf_symbol.is_func && elf_symbol.is_in_text_section); 440 } 441 442 bool Dso::LoadKernelModule() { 443 BuildId build_id = GetExpectedBuildId(); 444 ElfStatus result = ParseSymbolsFromElfFile(GetDebugFilePath(), build_id, 445 std::bind(ElfFileSymbolCallback, std::placeholders::_1, 446 SymbolFilterForKernelModule, &symbols_)); 447 return CheckReadSymbolResult(result, GetDebugFilePath()); 448 } 449 450 static bool SymbolFilterForDso(const ElfFileSymbol& elf_symbol) { 451 return elf_symbol.is_func || 452 (elf_symbol.is_label && elf_symbol.is_in_text_section); 453 } 454 455 bool Dso::LoadElfFile() { 456 BuildId build_id = GetExpectedBuildId(); 457 458 if (symfs_dir_.empty()) { 459 // Linux host can store debug shared libraries in /usr/lib/debug. 460 ElfStatus result = ParseSymbolsFromElfFile( 461 "/usr/lib/debug" + path_, build_id, 462 std::bind(ElfFileSymbolCallback, std::placeholders::_1, 463 SymbolFilterForDso, &symbols_)); 464 if (result == ElfStatus::NO_ERROR) { 465 return CheckReadSymbolResult(result, "/usr/lib/debug" + path_); 466 } 467 } 468 // TODO: load std::vector<Symbol> directly from ParseSymbolsFromElfFile 469 // instead of needing to call a callback function for each symbol. 470 ElfStatus result = ParseSymbolsFromElfFile( 471 GetDebugFilePath(), build_id, 472 std::bind(ElfFileSymbolCallback, std::placeholders::_1, 473 SymbolFilterForDso, &symbols_)); 474 return CheckReadSymbolResult(result, GetDebugFilePath()); 475 } 476 477 bool Dso::LoadEmbeddedElfFile() { 478 BuildId build_id = GetExpectedBuildId(); 479 auto tuple = SplitUrlInApk(GetDebugFilePath()); 480 CHECK(std::get<0>(tuple)); 481 ElfStatus result = ParseSymbolsFromApkFile( 482 std::get<1>(tuple), std::get<2>(tuple), build_id, 483 std::bind(ElfFileSymbolCallback, std::placeholders::_1, 484 SymbolFilterForDso, &symbols_)); 485 return CheckReadSymbolResult(result, GetDebugFilePath()); 486 } 487 488 void Dso::FixupSymbolLength() { 489 Symbol* prev_symbol = nullptr; 490 for (auto& symbol : symbols_) { 491 if (prev_symbol != nullptr && prev_symbol->len == 0) { 492 prev_symbol->len = symbol.addr - prev_symbol->addr; 493 } 494 prev_symbol = &symbol; 495 } 496 if (prev_symbol != nullptr && prev_symbol->len == 0) { 497 prev_symbol->len = std::numeric_limits<uint64_t>::max() - prev_symbol->addr; 498 } 499 } 500 501 const char* DsoTypeToString(DsoType dso_type) { 502 switch (dso_type) { 503 case DSO_KERNEL: 504 return "dso_kernel"; 505 case DSO_KERNEL_MODULE: 506 return "dso_kernel_module"; 507 case DSO_ELF_FILE: 508 return "dso_elf_file"; 509 default: 510 return "unknown"; 511 } 512 } 513