Path: blob/main/contrib/llvm-project/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp
35266 views
//===-- LLVMSymbolize.cpp -------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Implementation for LLVM symbolization library.9//10//===----------------------------------------------------------------------===//1112#include "llvm/DebugInfo/Symbolize/Symbolize.h"1314#include "llvm/ADT/STLExtras.h"15#include "llvm/DebugInfo/BTF/BTFContext.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>3637namespace llvm {38namespace codeview {39union DebugInfo;40}41namespace symbolize {4243LLVMSymbolizer::LLVMSymbolizer() = default;4445LLVMSymbolizer::LLVMSymbolizer(const Options &Opts)46: Opts(Opts),47BIDFetcher(std::make_unique<BuildIDFetcher>(Opts.DebugFileDirectory)) {}4849LLVMSymbolizer::~LLVMSymbolizer() = default;5051template <typename T>52Expected<DILineInfo>53LLVMSymbolizer::symbolizeCodeCommon(const T &ModuleSpecifier,54object::SectionedAddress ModuleOffset) {5556auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier);57if (!InfoOrErr)58return InfoOrErr.takeError();5960SymbolizableModule *Info = *InfoOrErr;6162// A null module means an error has already been reported. Return an empty63// result.64if (!Info)65return DILineInfo();6667// If the user is giving us relative addresses, add the preferred base of the68// object to the offset before we do the query. It's what DIContext expects.69if (Opts.RelativeAddresses)70ModuleOffset.Address += Info->getModulePreferredBase();7172DILineInfo LineInfo = Info->symbolizeCode(73ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions),74Opts.UseSymbolTable);75if (Opts.Demangle)76LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info);77return LineInfo;78}7980Expected<DILineInfo>81LLVMSymbolizer::symbolizeCode(const ObjectFile &Obj,82object::SectionedAddress ModuleOffset) {83return symbolizeCodeCommon(Obj, ModuleOffset);84}8586Expected<DILineInfo>87LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,88object::SectionedAddress ModuleOffset) {89return symbolizeCodeCommon(ModuleName, ModuleOffset);90}9192Expected<DILineInfo>93LLVMSymbolizer::symbolizeCode(ArrayRef<uint8_t> BuildID,94object::SectionedAddress ModuleOffset) {95return symbolizeCodeCommon(BuildID, ModuleOffset);96}9798template <typename T>99Expected<DIInliningInfo> LLVMSymbolizer::symbolizeInlinedCodeCommon(100const T &ModuleSpecifier, object::SectionedAddress ModuleOffset) {101auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier);102if (!InfoOrErr)103return InfoOrErr.takeError();104105SymbolizableModule *Info = *InfoOrErr;106107// A null module means an error has already been reported. Return an empty108// result.109if (!Info)110return DIInliningInfo();111112// If the user is giving us relative addresses, add the preferred base of the113// object to the offset before we do the query. It's what DIContext expects.114if (Opts.RelativeAddresses)115ModuleOffset.Address += Info->getModulePreferredBase();116117DIInliningInfo InlinedContext = Info->symbolizeInlinedCode(118ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions),119Opts.UseSymbolTable);120if (Opts.Demangle) {121for (int i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) {122auto *Frame = InlinedContext.getMutableFrame(i);123Frame->FunctionName = DemangleName(Frame->FunctionName, Info);124}125}126return InlinedContext;127}128129Expected<DIInliningInfo>130LLVMSymbolizer::symbolizeInlinedCode(const ObjectFile &Obj,131object::SectionedAddress ModuleOffset) {132return symbolizeInlinedCodeCommon(Obj, ModuleOffset);133}134135Expected<DIInliningInfo>136LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName,137object::SectionedAddress ModuleOffset) {138return symbolizeInlinedCodeCommon(ModuleName, ModuleOffset);139}140141Expected<DIInliningInfo>142LLVMSymbolizer::symbolizeInlinedCode(ArrayRef<uint8_t> BuildID,143object::SectionedAddress ModuleOffset) {144return symbolizeInlinedCodeCommon(BuildID, ModuleOffset);145}146147template <typename T>148Expected<DIGlobal>149LLVMSymbolizer::symbolizeDataCommon(const T &ModuleSpecifier,150object::SectionedAddress ModuleOffset) {151152auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier);153if (!InfoOrErr)154return InfoOrErr.takeError();155156SymbolizableModule *Info = *InfoOrErr;157// A null module means an error has already been reported. Return an empty158// result.159if (!Info)160return DIGlobal();161162// If the user is giving us relative addresses, add the preferred base of163// the object to the offset before we do the query. It's what DIContext164// expects.165if (Opts.RelativeAddresses)166ModuleOffset.Address += Info->getModulePreferredBase();167168DIGlobal Global = Info->symbolizeData(ModuleOffset);169if (Opts.Demangle)170Global.Name = DemangleName(Global.Name, Info);171return Global;172}173174Expected<DIGlobal>175LLVMSymbolizer::symbolizeData(const ObjectFile &Obj,176object::SectionedAddress ModuleOffset) {177return symbolizeDataCommon(Obj, ModuleOffset);178}179180Expected<DIGlobal>181LLVMSymbolizer::symbolizeData(const std::string &ModuleName,182object::SectionedAddress ModuleOffset) {183return symbolizeDataCommon(ModuleName, ModuleOffset);184}185186Expected<DIGlobal>187LLVMSymbolizer::symbolizeData(ArrayRef<uint8_t> BuildID,188object::SectionedAddress ModuleOffset) {189return symbolizeDataCommon(BuildID, ModuleOffset);190}191192template <typename T>193Expected<std::vector<DILocal>>194LLVMSymbolizer::symbolizeFrameCommon(const T &ModuleSpecifier,195object::SectionedAddress ModuleOffset) {196auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier);197if (!InfoOrErr)198return InfoOrErr.takeError();199200SymbolizableModule *Info = *InfoOrErr;201// A null module means an error has already been reported. Return an empty202// result.203if (!Info)204return std::vector<DILocal>();205206// If the user is giving us relative addresses, add the preferred base of207// the object to the offset before we do the query. It's what DIContext208// expects.209if (Opts.RelativeAddresses)210ModuleOffset.Address += Info->getModulePreferredBase();211212return Info->symbolizeFrame(ModuleOffset);213}214215Expected<std::vector<DILocal>>216LLVMSymbolizer::symbolizeFrame(const ObjectFile &Obj,217object::SectionedAddress ModuleOffset) {218return symbolizeFrameCommon(Obj, ModuleOffset);219}220221Expected<std::vector<DILocal>>222LLVMSymbolizer::symbolizeFrame(const std::string &ModuleName,223object::SectionedAddress ModuleOffset) {224return symbolizeFrameCommon(ModuleName, ModuleOffset);225}226227Expected<std::vector<DILocal>>228LLVMSymbolizer::symbolizeFrame(ArrayRef<uint8_t> BuildID,229object::SectionedAddress ModuleOffset) {230return symbolizeFrameCommon(BuildID, ModuleOffset);231}232233template <typename T>234Expected<std::vector<DILineInfo>>235LLVMSymbolizer::findSymbolCommon(const T &ModuleSpecifier, StringRef Symbol,236uint64_t Offset) {237auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier);238if (!InfoOrErr)239return InfoOrErr.takeError();240241SymbolizableModule *Info = *InfoOrErr;242std::vector<DILineInfo> Result;243244// A null module means an error has already been reported. Return an empty245// result.246if (!Info)247return Result;248249for (object::SectionedAddress A : Info->findSymbol(Symbol, Offset)) {250DILineInfo LineInfo = Info->symbolizeCode(251A, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions),252Opts.UseSymbolTable);253if (LineInfo.FileName != DILineInfo::BadString) {254if (Opts.Demangle)255LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info);256Result.push_back(LineInfo);257}258}259260return Result;261}262263Expected<std::vector<DILineInfo>>264LLVMSymbolizer::findSymbol(const ObjectFile &Obj, StringRef Symbol,265uint64_t Offset) {266return findSymbolCommon(Obj, Symbol, Offset);267}268269Expected<std::vector<DILineInfo>>270LLVMSymbolizer::findSymbol(const std::string &ModuleName, StringRef Symbol,271uint64_t Offset) {272return findSymbolCommon(ModuleName, Symbol, Offset);273}274275Expected<std::vector<DILineInfo>>276LLVMSymbolizer::findSymbol(ArrayRef<uint8_t> BuildID, StringRef Symbol,277uint64_t Offset) {278return findSymbolCommon(BuildID, Symbol, Offset);279}280281void LLVMSymbolizer::flush() {282ObjectForUBPathAndArch.clear();283LRUBinaries.clear();284CacheSize = 0;285BinaryForPath.clear();286ObjectPairForPathArch.clear();287Modules.clear();288BuildIDPaths.clear();289}290291namespace {292293// For Path="/path/to/foo" and Basename="foo" assume that debug info is in294// /path/to/foo.dSYM/Contents/Resources/DWARF/foo.295// For Path="/path/to/bar.dSYM" and Basename="foo" assume that debug info is in296// /path/to/bar.dSYM/Contents/Resources/DWARF/foo.297std::string getDarwinDWARFResourceForPath(const std::string &Path,298const std::string &Basename) {299SmallString<16> ResourceName = StringRef(Path);300if (sys::path::extension(Path) != ".dSYM") {301ResourceName += ".dSYM";302}303sys::path::append(ResourceName, "Contents", "Resources", "DWARF");304sys::path::append(ResourceName, Basename);305return std::string(ResourceName);306}307308bool checkFileCRC(StringRef Path, uint32_t CRCHash) {309ErrorOr<std::unique_ptr<MemoryBuffer>> MB =310MemoryBuffer::getFileOrSTDIN(Path);311if (!MB)312return false;313return CRCHash == llvm::crc32(arrayRefFromStringRef(MB.get()->getBuffer()));314}315316bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName,317uint32_t &CRCHash) {318if (!Obj)319return false;320for (const SectionRef &Section : Obj->sections()) {321StringRef Name;322consumeError(Section.getName().moveInto(Name));323324Name = Name.substr(Name.find_first_not_of("._"));325if (Name == "gnu_debuglink") {326Expected<StringRef> ContentsOrErr = Section.getContents();327if (!ContentsOrErr) {328consumeError(ContentsOrErr.takeError());329return false;330}331DataExtractor DE(*ContentsOrErr, Obj->isLittleEndian(), 0);332uint64_t Offset = 0;333if (const char *DebugNameStr = DE.getCStr(&Offset)) {334// 4-byte align the offset.335Offset = (Offset + 3) & ~0x3;336if (DE.isValidOffsetForDataOfSize(Offset, 4)) {337DebugName = DebugNameStr;338CRCHash = DE.getU32(&Offset);339return true;340}341}342break;343}344}345return false;346}347348bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj,349const MachOObjectFile *Obj) {350ArrayRef<uint8_t> dbg_uuid = DbgObj->getUuid();351ArrayRef<uint8_t> bin_uuid = Obj->getUuid();352if (dbg_uuid.empty() || bin_uuid.empty())353return false;354return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size());355}356357} // end anonymous namespace358359ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath,360const MachOObjectFile *MachExeObj,361const std::string &ArchName) {362// On Darwin we may find DWARF in separate object file in363// resource directory.364std::vector<std::string> DsymPaths;365StringRef Filename = sys::path::filename(ExePath);366DsymPaths.push_back(367getDarwinDWARFResourceForPath(ExePath, std::string(Filename)));368for (const auto &Path : Opts.DsymHints) {369DsymPaths.push_back(370getDarwinDWARFResourceForPath(Path, std::string(Filename)));371}372for (const auto &Path : DsymPaths) {373auto DbgObjOrErr = getOrCreateObject(Path, ArchName);374if (!DbgObjOrErr) {375// Ignore errors, the file might not exist.376consumeError(DbgObjOrErr.takeError());377continue;378}379ObjectFile *DbgObj = DbgObjOrErr.get();380if (!DbgObj)381continue;382const MachOObjectFile *MachDbgObj = dyn_cast<const MachOObjectFile>(DbgObj);383if (!MachDbgObj)384continue;385if (darwinDsymMatchesBinary(MachDbgObj, MachExeObj))386return DbgObj;387}388return nullptr;389}390391ObjectFile *LLVMSymbolizer::lookUpDebuglinkObject(const std::string &Path,392const ObjectFile *Obj,393const std::string &ArchName) {394std::string DebuglinkName;395uint32_t CRCHash;396std::string DebugBinaryPath;397if (!getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash))398return nullptr;399if (!findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath))400return nullptr;401auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName);402if (!DbgObjOrErr) {403// Ignore errors, the file might not exist.404consumeError(DbgObjOrErr.takeError());405return nullptr;406}407return DbgObjOrErr.get();408}409410ObjectFile *LLVMSymbolizer::lookUpBuildIDObject(const std::string &Path,411const ELFObjectFileBase *Obj,412const std::string &ArchName) {413auto BuildID = getBuildID(Obj);414if (BuildID.size() < 2)415return nullptr;416std::string DebugBinaryPath;417if (!getOrFindDebugBinary(BuildID, DebugBinaryPath))418return nullptr;419auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName);420if (!DbgObjOrErr) {421consumeError(DbgObjOrErr.takeError());422return nullptr;423}424return DbgObjOrErr.get();425}426427bool LLVMSymbolizer::findDebugBinary(const std::string &OrigPath,428const std::string &DebuglinkName,429uint32_t CRCHash, std::string &Result) {430SmallString<16> OrigDir(OrigPath);431llvm::sys::path::remove_filename(OrigDir);432SmallString<16> DebugPath = OrigDir;433// Try relative/path/to/original_binary/debuglink_name434llvm::sys::path::append(DebugPath, DebuglinkName);435if (checkFileCRC(DebugPath, CRCHash)) {436Result = std::string(DebugPath);437return true;438}439// Try relative/path/to/original_binary/.debug/debuglink_name440DebugPath = OrigDir;441llvm::sys::path::append(DebugPath, ".debug", DebuglinkName);442if (checkFileCRC(DebugPath, CRCHash)) {443Result = std::string(DebugPath);444return true;445}446// Make the path absolute so that lookups will go to447// "/usr/lib/debug/full/path/to/debug", not448// "/usr/lib/debug/to/debug"449llvm::sys::fs::make_absolute(OrigDir);450if (!Opts.FallbackDebugPath.empty()) {451// Try <FallbackDebugPath>/absolute/path/to/original_binary/debuglink_name452DebugPath = Opts.FallbackDebugPath;453} else {454#if defined(__NetBSD__)455// Try /usr/libdata/debug/absolute/path/to/original_binary/debuglink_name456DebugPath = "/usr/libdata/debug";457#else458// Try /usr/lib/debug/absolute/path/to/original_binary/debuglink_name459DebugPath = "/usr/lib/debug";460#endif461}462llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir),463DebuglinkName);464if (checkFileCRC(DebugPath, CRCHash)) {465Result = std::string(DebugPath);466return true;467}468return false;469}470471static StringRef getBuildIDStr(ArrayRef<uint8_t> BuildID) {472return StringRef(reinterpret_cast<const char *>(BuildID.data()),473BuildID.size());474}475476bool LLVMSymbolizer::getOrFindDebugBinary(const ArrayRef<uint8_t> BuildID,477std::string &Result) {478StringRef BuildIDStr = getBuildIDStr(BuildID);479auto I = BuildIDPaths.find(BuildIDStr);480if (I != BuildIDPaths.end()) {481Result = I->second;482return true;483}484if (!BIDFetcher)485return false;486if (std::optional<std::string> Path = BIDFetcher->fetch(BuildID)) {487Result = *Path;488auto InsertResult = BuildIDPaths.insert({BuildIDStr, Result});489assert(InsertResult.second);490(void)InsertResult;491return true;492}493494return false;495}496497Expected<LLVMSymbolizer::ObjectPair>498LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path,499const std::string &ArchName) {500auto I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName));501if (I != ObjectPairForPathArch.end()) {502recordAccess(BinaryForPath.find(Path)->second);503return I->second;504}505506auto ObjOrErr = getOrCreateObject(Path, ArchName);507if (!ObjOrErr) {508ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName),509ObjectPair(nullptr, nullptr));510return ObjOrErr.takeError();511}512513ObjectFile *Obj = ObjOrErr.get();514assert(Obj != nullptr);515ObjectFile *DbgObj = nullptr;516517if (auto MachObj = dyn_cast<const MachOObjectFile>(Obj))518DbgObj = lookUpDsymFile(Path, MachObj, ArchName);519else if (auto ELFObj = dyn_cast<const ELFObjectFileBase>(Obj))520DbgObj = lookUpBuildIDObject(Path, ELFObj, ArchName);521if (!DbgObj)522DbgObj = lookUpDebuglinkObject(Path, Obj, ArchName);523if (!DbgObj)524DbgObj = Obj;525ObjectPair Res = std::make_pair(Obj, DbgObj);526std::string DbgObjPath = DbgObj->getFileName().str();527auto Pair =528ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), Res);529BinaryForPath.find(DbgObjPath)->second.pushEvictor([this, I = Pair.first]() {530ObjectPairForPathArch.erase(I);531});532return Res;533}534535Expected<ObjectFile *>536LLVMSymbolizer::getOrCreateObject(const std::string &Path,537const std::string &ArchName) {538Binary *Bin;539auto Pair = BinaryForPath.emplace(Path, OwningBinary<Binary>());540if (!Pair.second) {541Bin = Pair.first->second->getBinary();542recordAccess(Pair.first->second);543} else {544Expected<OwningBinary<Binary>> BinOrErr = createBinary(Path);545if (!BinOrErr)546return BinOrErr.takeError();547548CachedBinary &CachedBin = Pair.first->second;549CachedBin = std::move(BinOrErr.get());550CachedBin.pushEvictor([this, I = Pair.first]() { BinaryForPath.erase(I); });551LRUBinaries.push_back(CachedBin);552CacheSize += CachedBin.size();553Bin = CachedBin->getBinary();554}555556if (!Bin)557return static_cast<ObjectFile *>(nullptr);558559if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) {560auto I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName));561if (I != ObjectForUBPathAndArch.end())562return I->second.get();563564Expected<std::unique_ptr<ObjectFile>> ObjOrErr =565UB->getMachOObjectForArch(ArchName);566if (!ObjOrErr) {567ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName),568std::unique_ptr<ObjectFile>());569return ObjOrErr.takeError();570}571ObjectFile *Res = ObjOrErr->get();572auto Pair = ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName),573std::move(ObjOrErr.get()));574BinaryForPath.find(Path)->second.pushEvictor(575[this, Iter = Pair.first]() { ObjectForUBPathAndArch.erase(Iter); });576return Res;577}578if (Bin->isObject()) {579return cast<ObjectFile>(Bin);580}581return errorCodeToError(object_error::arch_not_found);582}583584Expected<SymbolizableModule *>585LLVMSymbolizer::createModuleInfo(const ObjectFile *Obj,586std::unique_ptr<DIContext> Context,587StringRef ModuleName) {588auto InfoOrErr = SymbolizableObjectFile::create(Obj, std::move(Context),589Opts.UntagAddresses);590std::unique_ptr<SymbolizableModule> SymMod;591if (InfoOrErr)592SymMod = std::move(*InfoOrErr);593auto InsertResult = Modules.insert(594std::make_pair(std::string(ModuleName), std::move(SymMod)));595assert(InsertResult.second);596if (!InfoOrErr)597return InfoOrErr.takeError();598return InsertResult.first->second.get();599}600601Expected<SymbolizableModule *>602LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {603std::string BinaryName = ModuleName;604std::string ArchName = Opts.DefaultArch;605size_t ColonPos = ModuleName.find_last_of(':');606// Verify that substring after colon form a valid arch name.607if (ColonPos != std::string::npos) {608std::string ArchStr = ModuleName.substr(ColonPos + 1);609if (Triple(ArchStr).getArch() != Triple::UnknownArch) {610BinaryName = ModuleName.substr(0, ColonPos);611ArchName = ArchStr;612}613}614615auto I = Modules.find(ModuleName);616if (I != Modules.end()) {617recordAccess(BinaryForPath.find(BinaryName)->second);618return I->second.get();619}620621auto ObjectsOrErr = getOrCreateObjectPair(BinaryName, ArchName);622if (!ObjectsOrErr) {623// Failed to find valid object file.624Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());625return ObjectsOrErr.takeError();626}627ObjectPair Objects = ObjectsOrErr.get();628629std::unique_ptr<DIContext> Context;630// If this is a COFF object containing PDB info and not containing DWARF631// section, use a PDBContext to symbolize. Otherwise, use DWARF.632if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) {633const codeview::DebugInfo *DebugInfo;634StringRef PDBFileName;635auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName);636// Use DWARF if there're DWARF sections.637bool HasDwarf =638llvm::any_of(Objects.first->sections(), [](SectionRef Section) -> bool {639if (Expected<StringRef> SectionName = Section.getName())640return SectionName.get() == ".debug_info";641return false;642});643if (!EC && !HasDwarf && DebugInfo != nullptr && !PDBFileName.empty()) {644#if 0645using namespace pdb;646std::unique_ptr<IPDBSession> Session;647648PDB_ReaderType ReaderType =649Opts.UseDIA ? PDB_ReaderType::DIA : PDB_ReaderType::Native;650if (auto Err = loadDataForEXE(ReaderType, Objects.first->getFileName(),651Session)) {652Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());653// Return along the PDB filename to provide more context654return createFileError(PDBFileName, std::move(Err));655}656Context.reset(new PDBContext(*CoffObject, std::move(Session)));657#else658return make_error<StringError>(659"PDB support not compiled in",660std::make_error_code(std::errc::not_supported));661#endif662}663}664if (!Context)665Context = DWARFContext::create(666*Objects.second, DWARFContext::ProcessDebugRelocations::Process,667nullptr, Opts.DWPName);668auto ModuleOrErr =669createModuleInfo(Objects.first, std::move(Context), ModuleName);670if (ModuleOrErr) {671auto I = Modules.find(ModuleName);672BinaryForPath.find(BinaryName)->second.pushEvictor([this, I]() {673Modules.erase(I);674});675}676return ModuleOrErr;677}678679// For BPF programs .BTF.ext section contains line numbers information,680// use it if regular DWARF is not available (e.g. for stripped binary).681static bool useBTFContext(const ObjectFile &Obj) {682return Obj.makeTriple().isBPF() && !Obj.hasDebugInfo() &&683BTFParser::hasBTFSections(Obj);684}685686Expected<SymbolizableModule *>687LLVMSymbolizer::getOrCreateModuleInfo(const ObjectFile &Obj) {688StringRef ObjName = Obj.getFileName();689auto I = Modules.find(ObjName);690if (I != Modules.end())691return I->second.get();692693std::unique_ptr<DIContext> Context;694if (useBTFContext(Obj))695Context = BTFContext::create(Obj);696else697Context = DWARFContext::create(Obj);698// FIXME: handle COFF object with PDB info to use PDBContext699return createModuleInfo(&Obj, std::move(Context), ObjName);700}701702Expected<SymbolizableModule *>703LLVMSymbolizer::getOrCreateModuleInfo(ArrayRef<uint8_t> BuildID) {704std::string Path;705if (!getOrFindDebugBinary(BuildID, Path)) {706return createStringError(errc::no_such_file_or_directory,707"could not find build ID");708}709return getOrCreateModuleInfo(Path);710}711712namespace {713714// Undo these various manglings for Win32 extern "C" functions:715// cdecl - _foo716// stdcall - _foo@12717// fastcall - @foo@12718// vectorcall - foo@@12719// These are all different linkage names for 'foo'.720StringRef demanglePE32ExternCFunc(StringRef SymbolName) {721char Front = SymbolName.empty() ? '\0' : SymbolName[0];722723// Remove any '@[0-9]+' suffix.724bool HasAtNumSuffix = false;725if (Front != '?') {726size_t AtPos = SymbolName.rfind('@');727if (AtPos != StringRef::npos &&728all_of(drop_begin(SymbolName, AtPos + 1), isDigit)) {729SymbolName = SymbolName.substr(0, AtPos);730HasAtNumSuffix = true;731}732}733734// Remove any ending '@' for vectorcall.735bool IsVectorCall = false;736if (HasAtNumSuffix && SymbolName.ends_with("@")) {737SymbolName = SymbolName.drop_back();738IsVectorCall = true;739}740741// If not vectorcall, remove any '_' or '@' prefix.742if (!IsVectorCall && (Front == '_' || Front == '@'))743SymbolName = SymbolName.drop_front();744745return SymbolName;746}747748} // end anonymous namespace749750std::string751LLVMSymbolizer::DemangleName(const std::string &Name,752const SymbolizableModule *DbiModuleDescriptor) {753std::string Result;754if (nonMicrosoftDemangle(Name, Result))755return Result;756757if (!Name.empty() && Name.front() == '?') {758// Only do MSVC C++ demangling on symbols starting with '?'.759int status = 0;760char *DemangledName = microsoftDemangle(761Name, nullptr, &status,762MSDemangleFlags(MSDF_NoAccessSpecifier | MSDF_NoCallingConvention |763MSDF_NoMemberType | MSDF_NoReturnType));764if (status != 0)765return Name;766Result = DemangledName;767free(DemangledName);768return Result;769}770771if (DbiModuleDescriptor && DbiModuleDescriptor->isWin32Module()) {772std::string DemangledCName(demanglePE32ExternCFunc(Name));773// On i386 Windows, the C name mangling for different calling conventions774// may also be applied on top of the Itanium or Rust name mangling.775if (nonMicrosoftDemangle(DemangledCName, Result))776return Result;777return DemangledCName;778}779return Name;780}781782void LLVMSymbolizer::recordAccess(CachedBinary &Bin) {783if (Bin->getBinary())784LRUBinaries.splice(LRUBinaries.end(), LRUBinaries, Bin.getIterator());785}786787void LLVMSymbolizer::pruneCache() {788// Evict the LRU binary until the max cache size is reached or there's <= 1789// item in the cache. The MRU binary is always kept to avoid thrashing if it's790// larger than the cache size.791while (CacheSize > Opts.MaxCacheSize && !LRUBinaries.empty() &&792std::next(LRUBinaries.begin()) != LRUBinaries.end()) {793CachedBinary &Bin = LRUBinaries.front();794CacheSize -= Bin.size();795LRUBinaries.pop_front();796Bin.evict();797}798}799800void CachedBinary::pushEvictor(std::function<void()> NewEvictor) {801if (Evictor) {802this->Evictor = [OldEvictor = std::move(this->Evictor),803NewEvictor = std::move(NewEvictor)]() {804NewEvictor();805OldEvictor();806};807} else {808this->Evictor = std::move(NewEvictor);809}810}811812} // namespace symbolize813} // namespace llvm814815816