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