1 //===-- LLVMSymbolize.cpp -------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Implementation for LLVM symbolization library. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/DebugInfo/Symbolize/Symbolize.h" 14 15 #include "llvm/ADT/STLExtras.h" 16 #include "llvm/DebugInfo/DWARF/DWARFContext.h" 17 #include "llvm/DebugInfo/PDB/PDB.h" 18 #include "llvm/DebugInfo/PDB/PDBContext.h" 19 #include "llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h" 20 #include "llvm/Demangle/Demangle.h" 21 #include "llvm/Object/BuildID.h" 22 #include "llvm/Object/COFF.h" 23 #include "llvm/Object/ELFObjectFile.h" 24 #include "llvm/Object/MachO.h" 25 #include "llvm/Object/MachOUniversal.h" 26 #include "llvm/Support/CRC.h" 27 #include "llvm/Support/Casting.h" 28 #include "llvm/Support/DataExtractor.h" 29 #include "llvm/Support/Errc.h" 30 #include "llvm/Support/FileSystem.h" 31 #include "llvm/Support/MemoryBuffer.h" 32 #include "llvm/Support/Path.h" 33 #include <algorithm> 34 #include <cassert> 35 #include <cstring> 36 37 namespace llvm { 38 namespace codeview { 39 union DebugInfo; 40 } 41 namespace symbolize { 42 43 LLVMSymbolizer::LLVMSymbolizer() = default; 44 45 LLVMSymbolizer::LLVMSymbolizer(const Options &Opts) 46 : Opts(Opts), 47 BIDFetcher(std::make_unique<BuildIDFetcher>(Opts.DebugFileDirectory)) {} 48 49 LLVMSymbolizer::~LLVMSymbolizer() = default; 50 51 template <typename T> 52 Expected<DILineInfo> 53 LLVMSymbolizer::symbolizeCodeCommon(const T &ModuleSpecifier, 54 object::SectionedAddress ModuleOffset) { 55 56 auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier); 57 if (!InfoOrErr) 58 return InfoOrErr.takeError(); 59 60 SymbolizableModule *Info = *InfoOrErr; 61 62 // A null module means an error has already been reported. Return an empty 63 // result. 64 if (!Info) 65 return DILineInfo(); 66 67 // If the user is giving us relative addresses, add the preferred base of the 68 // object to the offset before we do the query. It's what DIContext expects. 69 if (Opts.RelativeAddresses) 70 ModuleOffset.Address += Info->getModulePreferredBase(); 71 72 DILineInfo LineInfo = Info->symbolizeCode( 73 ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions), 74 Opts.UseSymbolTable); 75 if (Opts.Demangle) 76 LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info); 77 return LineInfo; 78 } 79 80 Expected<DILineInfo> 81 LLVMSymbolizer::symbolizeCode(const ObjectFile &Obj, 82 object::SectionedAddress ModuleOffset) { 83 return symbolizeCodeCommon(Obj, ModuleOffset); 84 } 85 86 Expected<DILineInfo> 87 LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, 88 object::SectionedAddress ModuleOffset) { 89 return symbolizeCodeCommon(ModuleName, ModuleOffset); 90 } 91 92 Expected<DILineInfo> 93 LLVMSymbolizer::symbolizeCode(ArrayRef<uint8_t> BuildID, 94 object::SectionedAddress ModuleOffset) { 95 return symbolizeCodeCommon(BuildID, ModuleOffset); 96 } 97 98 template <typename T> 99 Expected<DIInliningInfo> LLVMSymbolizer::symbolizeInlinedCodeCommon( 100 const T &ModuleSpecifier, object::SectionedAddress ModuleOffset) { 101 auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier); 102 if (!InfoOrErr) 103 return InfoOrErr.takeError(); 104 105 SymbolizableModule *Info = *InfoOrErr; 106 107 // A null module means an error has already been reported. Return an empty 108 // result. 109 if (!Info) 110 return DIInliningInfo(); 111 112 // If the user is giving us relative addresses, add the preferred base of the 113 // object to the offset before we do the query. It's what DIContext expects. 114 if (Opts.RelativeAddresses) 115 ModuleOffset.Address += Info->getModulePreferredBase(); 116 117 DIInliningInfo InlinedContext = Info->symbolizeInlinedCode( 118 ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions), 119 Opts.UseSymbolTable); 120 if (Opts.Demangle) { 121 for (int i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) { 122 auto *Frame = InlinedContext.getMutableFrame(i); 123 Frame->FunctionName = DemangleName(Frame->FunctionName, Info); 124 } 125 } 126 return InlinedContext; 127 } 128 129 Expected<DIInliningInfo> 130 LLVMSymbolizer::symbolizeInlinedCode(const ObjectFile &Obj, 131 object::SectionedAddress ModuleOffset) { 132 return symbolizeInlinedCodeCommon(Obj, ModuleOffset); 133 } 134 135 Expected<DIInliningInfo> 136 LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName, 137 object::SectionedAddress ModuleOffset) { 138 return symbolizeInlinedCodeCommon(ModuleName, ModuleOffset); 139 } 140 141 Expected<DIInliningInfo> 142 LLVMSymbolizer::symbolizeInlinedCode(ArrayRef<uint8_t> BuildID, 143 object::SectionedAddress ModuleOffset) { 144 return symbolizeInlinedCodeCommon(BuildID, ModuleOffset); 145 } 146 147 template <typename T> 148 Expected<DIGlobal> 149 LLVMSymbolizer::symbolizeDataCommon(const T &ModuleSpecifier, 150 object::SectionedAddress ModuleOffset) { 151 152 auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier); 153 if (!InfoOrErr) 154 return InfoOrErr.takeError(); 155 156 SymbolizableModule *Info = *InfoOrErr; 157 // A null module means an error has already been reported. Return an empty 158 // result. 159 if (!Info) 160 return DIGlobal(); 161 162 // If the user is giving us relative addresses, add the preferred base of 163 // the object to the offset before we do the query. It's what DIContext 164 // expects. 165 if (Opts.RelativeAddresses) 166 ModuleOffset.Address += Info->getModulePreferredBase(); 167 168 DIGlobal Global = Info->symbolizeData(ModuleOffset); 169 if (Opts.Demangle) 170 Global.Name = DemangleName(Global.Name, Info); 171 return Global; 172 } 173 174 Expected<DIGlobal> 175 LLVMSymbolizer::symbolizeData(const ObjectFile &Obj, 176 object::SectionedAddress ModuleOffset) { 177 return symbolizeDataCommon(Obj, ModuleOffset); 178 } 179 180 Expected<DIGlobal> 181 LLVMSymbolizer::symbolizeData(const std::string &ModuleName, 182 object::SectionedAddress ModuleOffset) { 183 return symbolizeDataCommon(ModuleName, ModuleOffset); 184 } 185 186 Expected<DIGlobal> 187 LLVMSymbolizer::symbolizeData(ArrayRef<uint8_t> BuildID, 188 object::SectionedAddress ModuleOffset) { 189 return symbolizeDataCommon(BuildID, ModuleOffset); 190 } 191 192 template <typename T> 193 Expected<std::vector<DILocal>> 194 LLVMSymbolizer::symbolizeFrameCommon(const T &ModuleSpecifier, 195 object::SectionedAddress ModuleOffset) { 196 auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier); 197 if (!InfoOrErr) 198 return InfoOrErr.takeError(); 199 200 SymbolizableModule *Info = *InfoOrErr; 201 // A null module means an error has already been reported. Return an empty 202 // result. 203 if (!Info) 204 return std::vector<DILocal>(); 205 206 // If the user is giving us relative addresses, add the preferred base of 207 // the object to the offset before we do the query. It's what DIContext 208 // expects. 209 if (Opts.RelativeAddresses) 210 ModuleOffset.Address += Info->getModulePreferredBase(); 211 212 return Info->symbolizeFrame(ModuleOffset); 213 } 214 215 Expected<std::vector<DILocal>> 216 LLVMSymbolizer::symbolizeFrame(const ObjectFile &Obj, 217 object::SectionedAddress ModuleOffset) { 218 return symbolizeFrameCommon(Obj, ModuleOffset); 219 } 220 221 Expected<std::vector<DILocal>> 222 LLVMSymbolizer::symbolizeFrame(const std::string &ModuleName, 223 object::SectionedAddress ModuleOffset) { 224 return symbolizeFrameCommon(ModuleName, ModuleOffset); 225 } 226 227 Expected<std::vector<DILocal>> 228 LLVMSymbolizer::symbolizeFrame(ArrayRef<uint8_t> BuildID, 229 object::SectionedAddress ModuleOffset) { 230 return symbolizeFrameCommon(BuildID, ModuleOffset); 231 } 232 233 void LLVMSymbolizer::flush() { 234 ObjectForUBPathAndArch.clear(); 235 LRUBinaries.clear(); 236 CacheSize = 0; 237 BinaryForPath.clear(); 238 ObjectPairForPathArch.clear(); 239 Modules.clear(); 240 BuildIDPaths.clear(); 241 } 242 243 namespace { 244 245 // For Path="/path/to/foo" and Basename="foo" assume that debug info is in 246 // /path/to/foo.dSYM/Contents/Resources/DWARF/foo. 247 // For Path="/path/to/bar.dSYM" and Basename="foo" assume that debug info is in 248 // /path/to/bar.dSYM/Contents/Resources/DWARF/foo. 249 std::string getDarwinDWARFResourceForPath(const std::string &Path, 250 const std::string &Basename) { 251 SmallString<16> ResourceName = StringRef(Path); 252 if (sys::path::extension(Path) != ".dSYM") { 253 ResourceName += ".dSYM"; 254 } 255 sys::path::append(ResourceName, "Contents", "Resources", "DWARF"); 256 sys::path::append(ResourceName, Basename); 257 return std::string(ResourceName.str()); 258 } 259 260 bool checkFileCRC(StringRef Path, uint32_t CRCHash) { 261 ErrorOr<std::unique_ptr<MemoryBuffer>> MB = 262 MemoryBuffer::getFileOrSTDIN(Path); 263 if (!MB) 264 return false; 265 return CRCHash == llvm::crc32(arrayRefFromStringRef(MB.get()->getBuffer())); 266 } 267 268 bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName, 269 uint32_t &CRCHash) { 270 if (!Obj) 271 return false; 272 for (const SectionRef &Section : Obj->sections()) { 273 StringRef Name; 274 consumeError(Section.getName().moveInto(Name)); 275 276 Name = Name.substr(Name.find_first_not_of("._")); 277 if (Name == "gnu_debuglink") { 278 Expected<StringRef> ContentsOrErr = Section.getContents(); 279 if (!ContentsOrErr) { 280 consumeError(ContentsOrErr.takeError()); 281 return false; 282 } 283 DataExtractor DE(*ContentsOrErr, Obj->isLittleEndian(), 0); 284 uint64_t Offset = 0; 285 if (const char *DebugNameStr = DE.getCStr(&Offset)) { 286 // 4-byte align the offset. 287 Offset = (Offset + 3) & ~0x3; 288 if (DE.isValidOffsetForDataOfSize(Offset, 4)) { 289 DebugName = DebugNameStr; 290 CRCHash = DE.getU32(&Offset); 291 return true; 292 } 293 } 294 break; 295 } 296 } 297 return false; 298 } 299 300 bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj, 301 const MachOObjectFile *Obj) { 302 ArrayRef<uint8_t> dbg_uuid = DbgObj->getUuid(); 303 ArrayRef<uint8_t> bin_uuid = Obj->getUuid(); 304 if (dbg_uuid.empty() || bin_uuid.empty()) 305 return false; 306 return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size()); 307 } 308 309 } // end anonymous namespace 310 311 ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, 312 const MachOObjectFile *MachExeObj, 313 const std::string &ArchName) { 314 // On Darwin we may find DWARF in separate object file in 315 // resource directory. 316 std::vector<std::string> DsymPaths; 317 StringRef Filename = sys::path::filename(ExePath); 318 DsymPaths.push_back( 319 getDarwinDWARFResourceForPath(ExePath, std::string(Filename))); 320 for (const auto &Path : Opts.DsymHints) { 321 DsymPaths.push_back( 322 getDarwinDWARFResourceForPath(Path, std::string(Filename))); 323 } 324 for (const auto &Path : DsymPaths) { 325 auto DbgObjOrErr = getOrCreateObject(Path, ArchName); 326 if (!DbgObjOrErr) { 327 // Ignore errors, the file might not exist. 328 consumeError(DbgObjOrErr.takeError()); 329 continue; 330 } 331 ObjectFile *DbgObj = DbgObjOrErr.get(); 332 if (!DbgObj) 333 continue; 334 const MachOObjectFile *MachDbgObj = dyn_cast<const MachOObjectFile>(DbgObj); 335 if (!MachDbgObj) 336 continue; 337 if (darwinDsymMatchesBinary(MachDbgObj, MachExeObj)) 338 return DbgObj; 339 } 340 return nullptr; 341 } 342 343 ObjectFile *LLVMSymbolizer::lookUpDebuglinkObject(const std::string &Path, 344 const ObjectFile *Obj, 345 const std::string &ArchName) { 346 std::string DebuglinkName; 347 uint32_t CRCHash; 348 std::string DebugBinaryPath; 349 if (!getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash)) 350 return nullptr; 351 if (!findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) 352 return nullptr; 353 auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); 354 if (!DbgObjOrErr) { 355 // Ignore errors, the file might not exist. 356 consumeError(DbgObjOrErr.takeError()); 357 return nullptr; 358 } 359 return DbgObjOrErr.get(); 360 } 361 362 ObjectFile *LLVMSymbolizer::lookUpBuildIDObject(const std::string &Path, 363 const ELFObjectFileBase *Obj, 364 const std::string &ArchName) { 365 auto BuildID = getBuildID(Obj); 366 if (!BuildID) 367 return nullptr; 368 if (BuildID->size() < 2) 369 return nullptr; 370 std::string DebugBinaryPath; 371 if (!getOrFindDebugBinary(*BuildID, DebugBinaryPath)) 372 return nullptr; 373 auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); 374 if (!DbgObjOrErr) { 375 consumeError(DbgObjOrErr.takeError()); 376 return nullptr; 377 } 378 return DbgObjOrErr.get(); 379 } 380 381 bool LLVMSymbolizer::findDebugBinary(const std::string &OrigPath, 382 const std::string &DebuglinkName, 383 uint32_t CRCHash, std::string &Result) { 384 SmallString<16> OrigDir(OrigPath); 385 llvm::sys::path::remove_filename(OrigDir); 386 SmallString<16> DebugPath = OrigDir; 387 // Try relative/path/to/original_binary/debuglink_name 388 llvm::sys::path::append(DebugPath, DebuglinkName); 389 if (checkFileCRC(DebugPath, CRCHash)) { 390 Result = std::string(DebugPath.str()); 391 return true; 392 } 393 // Try relative/path/to/original_binary/.debug/debuglink_name 394 DebugPath = OrigDir; 395 llvm::sys::path::append(DebugPath, ".debug", DebuglinkName); 396 if (checkFileCRC(DebugPath, CRCHash)) { 397 Result = std::string(DebugPath.str()); 398 return true; 399 } 400 // Make the path absolute so that lookups will go to 401 // "/usr/lib/debug/full/path/to/debug", not 402 // "/usr/lib/debug/to/debug" 403 llvm::sys::fs::make_absolute(OrigDir); 404 if (!Opts.FallbackDebugPath.empty()) { 405 // Try <FallbackDebugPath>/absolute/path/to/original_binary/debuglink_name 406 DebugPath = Opts.FallbackDebugPath; 407 } else { 408 #if defined(__NetBSD__) 409 // Try /usr/libdata/debug/absolute/path/to/original_binary/debuglink_name 410 DebugPath = "/usr/libdata/debug"; 411 #else 412 // Try /usr/lib/debug/absolute/path/to/original_binary/debuglink_name 413 DebugPath = "/usr/lib/debug"; 414 #endif 415 } 416 llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir), 417 DebuglinkName); 418 if (checkFileCRC(DebugPath, CRCHash)) { 419 Result = std::string(DebugPath.str()); 420 return true; 421 } 422 return false; 423 } 424 425 static StringRef getBuildIDStr(ArrayRef<uint8_t> BuildID) { 426 return StringRef(reinterpret_cast<const char *>(BuildID.data()), 427 BuildID.size()); 428 } 429 430 bool LLVMSymbolizer::getOrFindDebugBinary(const ArrayRef<uint8_t> BuildID, 431 std::string &Result) { 432 StringRef BuildIDStr = getBuildIDStr(BuildID); 433 auto I = BuildIDPaths.find(BuildIDStr); 434 if (I != BuildIDPaths.end()) { 435 Result = I->second; 436 return true; 437 } 438 if (!BIDFetcher) 439 return false; 440 if (std::optional<std::string> Path = BIDFetcher->fetch(BuildID)) { 441 Result = *Path; 442 auto InsertResult = BuildIDPaths.insert({BuildIDStr, Result}); 443 assert(InsertResult.second); 444 (void)InsertResult; 445 return true; 446 } 447 448 return false; 449 } 450 451 Expected<LLVMSymbolizer::ObjectPair> 452 LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path, 453 const std::string &ArchName) { 454 auto I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName)); 455 if (I != ObjectPairForPathArch.end()) { 456 recordAccess(BinaryForPath.find(Path)->second); 457 return I->second; 458 } 459 460 auto ObjOrErr = getOrCreateObject(Path, ArchName); 461 if (!ObjOrErr) { 462 ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), 463 ObjectPair(nullptr, nullptr)); 464 return ObjOrErr.takeError(); 465 } 466 467 ObjectFile *Obj = ObjOrErr.get(); 468 assert(Obj != nullptr); 469 ObjectFile *DbgObj = nullptr; 470 471 if (auto MachObj = dyn_cast<const MachOObjectFile>(Obj)) 472 DbgObj = lookUpDsymFile(Path, MachObj, ArchName); 473 else if (auto ELFObj = dyn_cast<const ELFObjectFileBase>(Obj)) 474 DbgObj = lookUpBuildIDObject(Path, ELFObj, ArchName); 475 if (!DbgObj) 476 DbgObj = lookUpDebuglinkObject(Path, Obj, ArchName); 477 if (!DbgObj) 478 DbgObj = Obj; 479 ObjectPair Res = std::make_pair(Obj, DbgObj); 480 std::string DbgObjPath = DbgObj->getFileName().str(); 481 auto Pair = 482 ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), Res); 483 BinaryForPath.find(DbgObjPath)->second.pushEvictor([this, I = Pair.first]() { 484 ObjectPairForPathArch.erase(I); 485 }); 486 return Res; 487 } 488 489 Expected<ObjectFile *> 490 LLVMSymbolizer::getOrCreateObject(const std::string &Path, 491 const std::string &ArchName) { 492 Binary *Bin; 493 auto Pair = BinaryForPath.emplace(Path, OwningBinary<Binary>()); 494 if (!Pair.second) { 495 Bin = Pair.first->second->getBinary(); 496 recordAccess(Pair.first->second); 497 } else { 498 Expected<OwningBinary<Binary>> BinOrErr = createBinary(Path); 499 if (!BinOrErr) 500 return BinOrErr.takeError(); 501 502 CachedBinary &CachedBin = Pair.first->second; 503 CachedBin = std::move(BinOrErr.get()); 504 CachedBin.pushEvictor([this, I = Pair.first]() { BinaryForPath.erase(I); }); 505 LRUBinaries.push_back(CachedBin); 506 CacheSize += CachedBin.size(); 507 Bin = CachedBin->getBinary(); 508 } 509 510 if (!Bin) 511 return static_cast<ObjectFile *>(nullptr); 512 513 if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) { 514 auto I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName)); 515 if (I != ObjectForUBPathAndArch.end()) 516 return I->second.get(); 517 518 Expected<std::unique_ptr<ObjectFile>> ObjOrErr = 519 UB->getMachOObjectForArch(ArchName); 520 if (!ObjOrErr) { 521 ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName), 522 std::unique_ptr<ObjectFile>()); 523 return ObjOrErr.takeError(); 524 } 525 ObjectFile *Res = ObjOrErr->get(); 526 auto Pair = ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName), 527 std::move(ObjOrErr.get())); 528 BinaryForPath.find(Path)->second.pushEvictor( 529 [this, Iter = Pair.first]() { ObjectForUBPathAndArch.erase(Iter); }); 530 return Res; 531 } 532 if (Bin->isObject()) { 533 return cast<ObjectFile>(Bin); 534 } 535 return errorCodeToError(object_error::arch_not_found); 536 } 537 538 Expected<SymbolizableModule *> 539 LLVMSymbolizer::createModuleInfo(const ObjectFile *Obj, 540 std::unique_ptr<DIContext> Context, 541 StringRef ModuleName) { 542 auto InfoOrErr = SymbolizableObjectFile::create(Obj, std::move(Context), 543 Opts.UntagAddresses); 544 std::unique_ptr<SymbolizableModule> SymMod; 545 if (InfoOrErr) 546 SymMod = std::move(*InfoOrErr); 547 auto InsertResult = Modules.insert( 548 std::make_pair(std::string(ModuleName), std::move(SymMod))); 549 assert(InsertResult.second); 550 if (!InfoOrErr) 551 return InfoOrErr.takeError(); 552 return InsertResult.first->second.get(); 553 } 554 555 Expected<SymbolizableModule *> 556 LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { 557 std::string BinaryName = ModuleName; 558 std::string ArchName = Opts.DefaultArch; 559 size_t ColonPos = ModuleName.find_last_of(':'); 560 // Verify that substring after colon form a valid arch name. 561 if (ColonPos != std::string::npos) { 562 std::string ArchStr = ModuleName.substr(ColonPos + 1); 563 if (Triple(ArchStr).getArch() != Triple::UnknownArch) { 564 BinaryName = ModuleName.substr(0, ColonPos); 565 ArchName = ArchStr; 566 } 567 } 568 569 auto I = Modules.find(ModuleName); 570 if (I != Modules.end()) { 571 recordAccess(BinaryForPath.find(BinaryName)->second); 572 return I->second.get(); 573 } 574 575 auto ObjectsOrErr = getOrCreateObjectPair(BinaryName, ArchName); 576 if (!ObjectsOrErr) { 577 // Failed to find valid object file. 578 Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>()); 579 return ObjectsOrErr.takeError(); 580 } 581 ObjectPair Objects = ObjectsOrErr.get(); 582 583 std::unique_ptr<DIContext> Context; 584 // If this is a COFF object containing PDB info, use a PDBContext to 585 // symbolize. Otherwise, use DWARF. 586 if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) { 587 const codeview::DebugInfo *DebugInfo; 588 StringRef PDBFileName; 589 auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName); 590 if (!EC && DebugInfo != nullptr && !PDBFileName.empty()) { 591 #if 0 592 using namespace pdb; 593 std::unique_ptr<IPDBSession> Session; 594 595 PDB_ReaderType ReaderType = 596 Opts.UseDIA ? PDB_ReaderType::DIA : PDB_ReaderType::Native; 597 if (auto Err = loadDataForEXE(ReaderType, Objects.first->getFileName(), 598 Session)) { 599 Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>()); 600 // Return along the PDB filename to provide more context 601 return createFileError(PDBFileName, std::move(Err)); 602 } 603 Context.reset(new PDBContext(*CoffObject, std::move(Session))); 604 #else 605 return make_error<StringError>( 606 "PDB support not compiled in", 607 std::make_error_code(std::errc::not_supported)); 608 #endif 609 } 610 } 611 if (!Context) 612 Context = DWARFContext::create( 613 *Objects.second, DWARFContext::ProcessDebugRelocations::Process, 614 nullptr, Opts.DWPName); 615 auto ModuleOrErr = 616 createModuleInfo(Objects.first, std::move(Context), ModuleName); 617 if (ModuleOrErr) { 618 auto I = Modules.find(ModuleName); 619 BinaryForPath.find(BinaryName)->second.pushEvictor([this, I]() { 620 Modules.erase(I); 621 }); 622 } 623 return ModuleOrErr; 624 } 625 626 Expected<SymbolizableModule *> 627 LLVMSymbolizer::getOrCreateModuleInfo(const ObjectFile &Obj) { 628 StringRef ObjName = Obj.getFileName(); 629 auto I = Modules.find(ObjName); 630 if (I != Modules.end()) 631 return I->second.get(); 632 633 std::unique_ptr<DIContext> Context = DWARFContext::create(Obj); 634 // FIXME: handle COFF object with PDB info to use PDBContext 635 return createModuleInfo(&Obj, std::move(Context), ObjName); 636 } 637 638 Expected<SymbolizableModule *> 639 LLVMSymbolizer::getOrCreateModuleInfo(ArrayRef<uint8_t> BuildID) { 640 std::string Path; 641 if (!getOrFindDebugBinary(BuildID, Path)) { 642 return createStringError(errc::no_such_file_or_directory, 643 Twine("could not find build ID '") + 644 toHex(BuildID) + "'"); 645 } 646 return getOrCreateModuleInfo(Path); 647 } 648 649 namespace { 650 651 // Undo these various manglings for Win32 extern "C" functions: 652 // cdecl - _foo 653 // stdcall - _foo@12 654 // fastcall - @foo@12 655 // vectorcall - foo@@12 656 // These are all different linkage names for 'foo'. 657 StringRef demanglePE32ExternCFunc(StringRef SymbolName) { 658 char Front = SymbolName.empty() ? '\0' : SymbolName[0]; 659 660 // Remove any '@[0-9]+' suffix. 661 bool HasAtNumSuffix = false; 662 if (Front != '?') { 663 size_t AtPos = SymbolName.rfind('@'); 664 if (AtPos != StringRef::npos && 665 all_of(drop_begin(SymbolName, AtPos + 1), isDigit)) { 666 SymbolName = SymbolName.substr(0, AtPos); 667 HasAtNumSuffix = true; 668 } 669 } 670 671 // Remove any ending '@' for vectorcall. 672 bool IsVectorCall = false; 673 if (HasAtNumSuffix && SymbolName.endswith("@")) { 674 SymbolName = SymbolName.drop_back(); 675 IsVectorCall = true; 676 } 677 678 // If not vectorcall, remove any '_' or '@' prefix. 679 if (!IsVectorCall && (Front == '_' || Front == '@')) 680 SymbolName = SymbolName.drop_front(); 681 682 return SymbolName; 683 } 684 685 } // end anonymous namespace 686 687 std::string 688 LLVMSymbolizer::DemangleName(const std::string &Name, 689 const SymbolizableModule *DbiModuleDescriptor) { 690 std::string Result; 691 if (nonMicrosoftDemangle(Name.c_str(), Result)) 692 return Result; 693 694 if (!Name.empty() && Name.front() == '?') { 695 // Only do MSVC C++ demangling on symbols starting with '?'. 696 int status = 0; 697 char *DemangledName = microsoftDemangle( 698 Name.c_str(), nullptr, nullptr, nullptr, &status, 699 MSDemangleFlags(MSDF_NoAccessSpecifier | MSDF_NoCallingConvention | 700 MSDF_NoMemberType | MSDF_NoReturnType)); 701 if (status != 0) 702 return Name; 703 Result = DemangledName; 704 free(DemangledName); 705 return Result; 706 } 707 708 if (DbiModuleDescriptor && DbiModuleDescriptor->isWin32Module()) { 709 std::string DemangledCName(demanglePE32ExternCFunc(Name)); 710 // On i386 Windows, the C name mangling for different calling conventions 711 // may also be applied on top of the Itanium or Rust name mangling. 712 if (nonMicrosoftDemangle(DemangledCName.c_str(), Result)) 713 return Result; 714 return DemangledCName; 715 } 716 return Name; 717 } 718 719 void LLVMSymbolizer::recordAccess(CachedBinary &Bin) { 720 if (Bin->getBinary()) 721 LRUBinaries.splice(LRUBinaries.end(), LRUBinaries, Bin.getIterator()); 722 } 723 724 void LLVMSymbolizer::pruneCache() { 725 // Evict the LRU binary until the max cache size is reached or there's <= 1 726 // item in the cache. The MRU binary is always kept to avoid thrashing if it's 727 // larger than the cache size. 728 while (CacheSize > Opts.MaxCacheSize && !LRUBinaries.empty() && 729 std::next(LRUBinaries.begin()) != LRUBinaries.end()) { 730 CachedBinary &Bin = LRUBinaries.front(); 731 CacheSize -= Bin.size(); 732 LRUBinaries.pop_front(); 733 Bin.evict(); 734 } 735 } 736 737 void CachedBinary::pushEvictor(std::function<void()> NewEvictor) { 738 if (Evictor) { 739 this->Evictor = [OldEvictor = std::move(this->Evictor), 740 NewEvictor = std::move(NewEvictor)]() { 741 NewEvictor(); 742 OldEvictor(); 743 }; 744 } else { 745 this->Evictor = std::move(NewEvictor); 746 } 747 } 748 749 } // namespace symbolize 750 } // namespace llvm 751