109467b48Spatrick //===-- LLVMSymbolize.cpp -------------------------------------------------===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // Implementation for LLVM symbolization library.
1009467b48Spatrick //
1109467b48Spatrick //===----------------------------------------------------------------------===//
1209467b48Spatrick
1309467b48Spatrick #include "llvm/DebugInfo/Symbolize/Symbolize.h"
1409467b48Spatrick
1509467b48Spatrick #include "llvm/ADT/STLExtras.h"
1609467b48Spatrick #include "llvm/DebugInfo/DWARF/DWARFContext.h"
1709467b48Spatrick #include "llvm/DebugInfo/PDB/PDB.h"
1809467b48Spatrick #include "llvm/DebugInfo/PDB/PDBContext.h"
19*d415bd75Srobert #include "llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h"
2009467b48Spatrick #include "llvm/Demangle/Demangle.h"
21*d415bd75Srobert #include "llvm/Object/BuildID.h"
2209467b48Spatrick #include "llvm/Object/COFF.h"
23*d415bd75Srobert #include "llvm/Object/ELFObjectFile.h"
2409467b48Spatrick #include "llvm/Object/MachO.h"
2509467b48Spatrick #include "llvm/Object/MachOUniversal.h"
2609467b48Spatrick #include "llvm/Support/CRC.h"
2709467b48Spatrick #include "llvm/Support/Casting.h"
2809467b48Spatrick #include "llvm/Support/DataExtractor.h"
2909467b48Spatrick #include "llvm/Support/Errc.h"
3009467b48Spatrick #include "llvm/Support/FileSystem.h"
3109467b48Spatrick #include "llvm/Support/MemoryBuffer.h"
3209467b48Spatrick #include "llvm/Support/Path.h"
3309467b48Spatrick #include <algorithm>
3409467b48Spatrick #include <cassert>
3509467b48Spatrick #include <cstring>
3609467b48Spatrick
3709467b48Spatrick namespace llvm {
38*d415bd75Srobert namespace codeview {
39*d415bd75Srobert union DebugInfo;
40*d415bd75Srobert }
4109467b48Spatrick namespace symbolize {
4209467b48Spatrick
43*d415bd75Srobert LLVMSymbolizer::LLVMSymbolizer() = default;
44*d415bd75Srobert
LLVMSymbolizer(const Options & Opts)45*d415bd75Srobert LLVMSymbolizer::LLVMSymbolizer(const Options &Opts)
46*d415bd75Srobert : Opts(Opts),
47*d415bd75Srobert BIDFetcher(std::make_unique<BuildIDFetcher>(Opts.DebugFileDirectory)) {}
48*d415bd75Srobert
49*d415bd75Srobert LLVMSymbolizer::~LLVMSymbolizer() = default;
50*d415bd75Srobert
5173471bf0Spatrick template <typename T>
5209467b48Spatrick Expected<DILineInfo>
symbolizeCodeCommon(const T & ModuleSpecifier,object::SectionedAddress ModuleOffset)5373471bf0Spatrick LLVMSymbolizer::symbolizeCodeCommon(const T &ModuleSpecifier,
5409467b48Spatrick object::SectionedAddress ModuleOffset) {
5573471bf0Spatrick
5673471bf0Spatrick auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier);
5773471bf0Spatrick if (!InfoOrErr)
5873471bf0Spatrick return InfoOrErr.takeError();
5973471bf0Spatrick
6073471bf0Spatrick SymbolizableModule *Info = *InfoOrErr;
6173471bf0Spatrick
6209467b48Spatrick // A null module means an error has already been reported. Return an empty
6309467b48Spatrick // result.
6409467b48Spatrick if (!Info)
6509467b48Spatrick return DILineInfo();
6609467b48Spatrick
6709467b48Spatrick // If the user is giving us relative addresses, add the preferred base of the
6809467b48Spatrick // object to the offset before we do the query. It's what DIContext expects.
6909467b48Spatrick if (Opts.RelativeAddresses)
7009467b48Spatrick ModuleOffset.Address += Info->getModulePreferredBase();
7109467b48Spatrick
72097a140dSpatrick DILineInfo LineInfo = Info->symbolizeCode(
73097a140dSpatrick ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions),
7409467b48Spatrick Opts.UseSymbolTable);
7509467b48Spatrick if (Opts.Demangle)
7609467b48Spatrick LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info);
7709467b48Spatrick return LineInfo;
7809467b48Spatrick }
7909467b48Spatrick
8009467b48Spatrick Expected<DILineInfo>
symbolizeCode(const ObjectFile & Obj,object::SectionedAddress ModuleOffset)8109467b48Spatrick LLVMSymbolizer::symbolizeCode(const ObjectFile &Obj,
8209467b48Spatrick object::SectionedAddress ModuleOffset) {
8373471bf0Spatrick return symbolizeCodeCommon(Obj, ModuleOffset);
8409467b48Spatrick }
8509467b48Spatrick
8609467b48Spatrick Expected<DILineInfo>
symbolizeCode(const std::string & ModuleName,object::SectionedAddress ModuleOffset)8709467b48Spatrick LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,
8809467b48Spatrick object::SectionedAddress ModuleOffset) {
8973471bf0Spatrick return symbolizeCodeCommon(ModuleName, ModuleOffset);
9009467b48Spatrick }
9109467b48Spatrick
92*d415bd75Srobert Expected<DILineInfo>
symbolizeCode(ArrayRef<uint8_t> BuildID,object::SectionedAddress ModuleOffset)93*d415bd75Srobert LLVMSymbolizer::symbolizeCode(ArrayRef<uint8_t> BuildID,
94*d415bd75Srobert object::SectionedAddress ModuleOffset) {
95*d415bd75Srobert return symbolizeCodeCommon(BuildID, ModuleOffset);
96*d415bd75Srobert }
97*d415bd75Srobert
9873471bf0Spatrick template <typename T>
symbolizeInlinedCodeCommon(const T & ModuleSpecifier,object::SectionedAddress ModuleOffset)9973471bf0Spatrick Expected<DIInliningInfo> LLVMSymbolizer::symbolizeInlinedCodeCommon(
10073471bf0Spatrick const T &ModuleSpecifier, object::SectionedAddress ModuleOffset) {
10173471bf0Spatrick auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier);
10273471bf0Spatrick if (!InfoOrErr)
10309467b48Spatrick return InfoOrErr.takeError();
10409467b48Spatrick
10573471bf0Spatrick SymbolizableModule *Info = *InfoOrErr;
10673471bf0Spatrick
10709467b48Spatrick // A null module means an error has already been reported. Return an empty
10809467b48Spatrick // result.
10909467b48Spatrick if (!Info)
11009467b48Spatrick return DIInliningInfo();
11109467b48Spatrick
11209467b48Spatrick // If the user is giving us relative addresses, add the preferred base of the
11309467b48Spatrick // object to the offset before we do the query. It's what DIContext expects.
11409467b48Spatrick if (Opts.RelativeAddresses)
11509467b48Spatrick ModuleOffset.Address += Info->getModulePreferredBase();
11609467b48Spatrick
11709467b48Spatrick DIInliningInfo InlinedContext = Info->symbolizeInlinedCode(
118097a140dSpatrick ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions),
119097a140dSpatrick Opts.UseSymbolTable);
12009467b48Spatrick if (Opts.Demangle) {
12109467b48Spatrick for (int i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) {
12209467b48Spatrick auto *Frame = InlinedContext.getMutableFrame(i);
12309467b48Spatrick Frame->FunctionName = DemangleName(Frame->FunctionName, Info);
12409467b48Spatrick }
12509467b48Spatrick }
12609467b48Spatrick return InlinedContext;
12709467b48Spatrick }
12809467b48Spatrick
12973471bf0Spatrick Expected<DIInliningInfo>
symbolizeInlinedCode(const ObjectFile & Obj,object::SectionedAddress ModuleOffset)13073471bf0Spatrick LLVMSymbolizer::symbolizeInlinedCode(const ObjectFile &Obj,
13109467b48Spatrick object::SectionedAddress ModuleOffset) {
13273471bf0Spatrick return symbolizeInlinedCodeCommon(Obj, ModuleOffset);
13373471bf0Spatrick }
13473471bf0Spatrick
13573471bf0Spatrick Expected<DIInliningInfo>
symbolizeInlinedCode(const std::string & ModuleName,object::SectionedAddress ModuleOffset)13673471bf0Spatrick LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName,
13773471bf0Spatrick object::SectionedAddress ModuleOffset) {
13873471bf0Spatrick return symbolizeInlinedCodeCommon(ModuleName, ModuleOffset);
13973471bf0Spatrick }
14073471bf0Spatrick
141*d415bd75Srobert Expected<DIInliningInfo>
symbolizeInlinedCode(ArrayRef<uint8_t> BuildID,object::SectionedAddress ModuleOffset)142*d415bd75Srobert LLVMSymbolizer::symbolizeInlinedCode(ArrayRef<uint8_t> BuildID,
143*d415bd75Srobert object::SectionedAddress ModuleOffset) {
144*d415bd75Srobert return symbolizeInlinedCodeCommon(BuildID, ModuleOffset);
145*d415bd75Srobert }
146*d415bd75Srobert
14773471bf0Spatrick template <typename T>
14873471bf0Spatrick Expected<DIGlobal>
symbolizeDataCommon(const T & ModuleSpecifier,object::SectionedAddress ModuleOffset)14973471bf0Spatrick LLVMSymbolizer::symbolizeDataCommon(const T &ModuleSpecifier,
15073471bf0Spatrick object::SectionedAddress ModuleOffset) {
15173471bf0Spatrick
15273471bf0Spatrick auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier);
15373471bf0Spatrick if (!InfoOrErr)
15409467b48Spatrick return InfoOrErr.takeError();
15509467b48Spatrick
15673471bf0Spatrick SymbolizableModule *Info = *InfoOrErr;
15709467b48Spatrick // A null module means an error has already been reported. Return an empty
15809467b48Spatrick // result.
15909467b48Spatrick if (!Info)
16009467b48Spatrick return DIGlobal();
16109467b48Spatrick
16209467b48Spatrick // If the user is giving us relative addresses, add the preferred base of
16309467b48Spatrick // the object to the offset before we do the query. It's what DIContext
16409467b48Spatrick // expects.
16509467b48Spatrick if (Opts.RelativeAddresses)
16609467b48Spatrick ModuleOffset.Address += Info->getModulePreferredBase();
16709467b48Spatrick
16809467b48Spatrick DIGlobal Global = Info->symbolizeData(ModuleOffset);
16909467b48Spatrick if (Opts.Demangle)
17009467b48Spatrick Global.Name = DemangleName(Global.Name, Info);
17109467b48Spatrick return Global;
17209467b48Spatrick }
17309467b48Spatrick
17473471bf0Spatrick Expected<DIGlobal>
symbolizeData(const ObjectFile & Obj,object::SectionedAddress ModuleOffset)17573471bf0Spatrick LLVMSymbolizer::symbolizeData(const ObjectFile &Obj,
17609467b48Spatrick object::SectionedAddress ModuleOffset) {
17773471bf0Spatrick return symbolizeDataCommon(Obj, ModuleOffset);
17873471bf0Spatrick }
17973471bf0Spatrick
18073471bf0Spatrick Expected<DIGlobal>
symbolizeData(const std::string & ModuleName,object::SectionedAddress ModuleOffset)18173471bf0Spatrick LLVMSymbolizer::symbolizeData(const std::string &ModuleName,
18273471bf0Spatrick object::SectionedAddress ModuleOffset) {
18373471bf0Spatrick return symbolizeDataCommon(ModuleName, ModuleOffset);
18473471bf0Spatrick }
18573471bf0Spatrick
186*d415bd75Srobert Expected<DIGlobal>
symbolizeData(ArrayRef<uint8_t> BuildID,object::SectionedAddress ModuleOffset)187*d415bd75Srobert LLVMSymbolizer::symbolizeData(ArrayRef<uint8_t> BuildID,
188*d415bd75Srobert object::SectionedAddress ModuleOffset) {
189*d415bd75Srobert return symbolizeDataCommon(BuildID, ModuleOffset);
190*d415bd75Srobert }
191*d415bd75Srobert
19273471bf0Spatrick template <typename T>
19373471bf0Spatrick Expected<std::vector<DILocal>>
symbolizeFrameCommon(const T & ModuleSpecifier,object::SectionedAddress ModuleOffset)19473471bf0Spatrick LLVMSymbolizer::symbolizeFrameCommon(const T &ModuleSpecifier,
19573471bf0Spatrick object::SectionedAddress ModuleOffset) {
19673471bf0Spatrick auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier);
19773471bf0Spatrick if (!InfoOrErr)
19809467b48Spatrick return InfoOrErr.takeError();
19909467b48Spatrick
20073471bf0Spatrick SymbolizableModule *Info = *InfoOrErr;
20109467b48Spatrick // A null module means an error has already been reported. Return an empty
20209467b48Spatrick // result.
20309467b48Spatrick if (!Info)
20409467b48Spatrick return std::vector<DILocal>();
20509467b48Spatrick
20609467b48Spatrick // If the user is giving us relative addresses, add the preferred base of
20709467b48Spatrick // the object to the offset before we do the query. It's what DIContext
20809467b48Spatrick // expects.
20909467b48Spatrick if (Opts.RelativeAddresses)
21009467b48Spatrick ModuleOffset.Address += Info->getModulePreferredBase();
21109467b48Spatrick
21209467b48Spatrick return Info->symbolizeFrame(ModuleOffset);
21309467b48Spatrick }
21409467b48Spatrick
21573471bf0Spatrick Expected<std::vector<DILocal>>
symbolizeFrame(const ObjectFile & Obj,object::SectionedAddress ModuleOffset)21673471bf0Spatrick LLVMSymbolizer::symbolizeFrame(const ObjectFile &Obj,
21773471bf0Spatrick object::SectionedAddress ModuleOffset) {
21873471bf0Spatrick return symbolizeFrameCommon(Obj, ModuleOffset);
21973471bf0Spatrick }
22073471bf0Spatrick
22173471bf0Spatrick Expected<std::vector<DILocal>>
symbolizeFrame(const std::string & ModuleName,object::SectionedAddress ModuleOffset)22273471bf0Spatrick LLVMSymbolizer::symbolizeFrame(const std::string &ModuleName,
22373471bf0Spatrick object::SectionedAddress ModuleOffset) {
22473471bf0Spatrick return symbolizeFrameCommon(ModuleName, ModuleOffset);
22573471bf0Spatrick }
22673471bf0Spatrick
227*d415bd75Srobert Expected<std::vector<DILocal>>
symbolizeFrame(ArrayRef<uint8_t> BuildID,object::SectionedAddress ModuleOffset)228*d415bd75Srobert LLVMSymbolizer::symbolizeFrame(ArrayRef<uint8_t> BuildID,
229*d415bd75Srobert object::SectionedAddress ModuleOffset) {
230*d415bd75Srobert return symbolizeFrameCommon(BuildID, ModuleOffset);
231*d415bd75Srobert }
232*d415bd75Srobert
flush()23309467b48Spatrick void LLVMSymbolizer::flush() {
23409467b48Spatrick ObjectForUBPathAndArch.clear();
235*d415bd75Srobert LRUBinaries.clear();
236*d415bd75Srobert CacheSize = 0;
23709467b48Spatrick BinaryForPath.clear();
23809467b48Spatrick ObjectPairForPathArch.clear();
23909467b48Spatrick Modules.clear();
240*d415bd75Srobert BuildIDPaths.clear();
24109467b48Spatrick }
24209467b48Spatrick
24309467b48Spatrick namespace {
24409467b48Spatrick
24509467b48Spatrick // For Path="/path/to/foo" and Basename="foo" assume that debug info is in
24609467b48Spatrick // /path/to/foo.dSYM/Contents/Resources/DWARF/foo.
24709467b48Spatrick // For Path="/path/to/bar.dSYM" and Basename="foo" assume that debug info is in
24809467b48Spatrick // /path/to/bar.dSYM/Contents/Resources/DWARF/foo.
getDarwinDWARFResourceForPath(const std::string & Path,const std::string & Basename)24973471bf0Spatrick std::string getDarwinDWARFResourceForPath(const std::string &Path,
25073471bf0Spatrick const std::string &Basename) {
25109467b48Spatrick SmallString<16> ResourceName = StringRef(Path);
25209467b48Spatrick if (sys::path::extension(Path) != ".dSYM") {
25309467b48Spatrick ResourceName += ".dSYM";
25409467b48Spatrick }
25509467b48Spatrick sys::path::append(ResourceName, "Contents", "Resources", "DWARF");
25609467b48Spatrick sys::path::append(ResourceName, Basename);
257097a140dSpatrick return std::string(ResourceName.str());
25809467b48Spatrick }
25909467b48Spatrick
checkFileCRC(StringRef Path,uint32_t CRCHash)26009467b48Spatrick bool checkFileCRC(StringRef Path, uint32_t CRCHash) {
26109467b48Spatrick ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
26209467b48Spatrick MemoryBuffer::getFileOrSTDIN(Path);
26309467b48Spatrick if (!MB)
26409467b48Spatrick return false;
26509467b48Spatrick return CRCHash == llvm::crc32(arrayRefFromStringRef(MB.get()->getBuffer()));
26609467b48Spatrick }
26709467b48Spatrick
getGNUDebuglinkContents(const ObjectFile * Obj,std::string & DebugName,uint32_t & CRCHash)26809467b48Spatrick bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName,
26909467b48Spatrick uint32_t &CRCHash) {
27009467b48Spatrick if (!Obj)
27109467b48Spatrick return false;
27209467b48Spatrick for (const SectionRef &Section : Obj->sections()) {
27309467b48Spatrick StringRef Name;
274*d415bd75Srobert consumeError(Section.getName().moveInto(Name));
27509467b48Spatrick
27609467b48Spatrick Name = Name.substr(Name.find_first_not_of("._"));
27709467b48Spatrick if (Name == "gnu_debuglink") {
27809467b48Spatrick Expected<StringRef> ContentsOrErr = Section.getContents();
27909467b48Spatrick if (!ContentsOrErr) {
28009467b48Spatrick consumeError(ContentsOrErr.takeError());
28109467b48Spatrick return false;
28209467b48Spatrick }
28309467b48Spatrick DataExtractor DE(*ContentsOrErr, Obj->isLittleEndian(), 0);
28409467b48Spatrick uint64_t Offset = 0;
28509467b48Spatrick if (const char *DebugNameStr = DE.getCStr(&Offset)) {
28609467b48Spatrick // 4-byte align the offset.
28709467b48Spatrick Offset = (Offset + 3) & ~0x3;
28809467b48Spatrick if (DE.isValidOffsetForDataOfSize(Offset, 4)) {
28909467b48Spatrick DebugName = DebugNameStr;
29009467b48Spatrick CRCHash = DE.getU32(&Offset);
29109467b48Spatrick return true;
29209467b48Spatrick }
29309467b48Spatrick }
29409467b48Spatrick break;
29509467b48Spatrick }
29609467b48Spatrick }
29709467b48Spatrick return false;
29809467b48Spatrick }
29909467b48Spatrick
darwinDsymMatchesBinary(const MachOObjectFile * DbgObj,const MachOObjectFile * Obj)30009467b48Spatrick bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj,
30109467b48Spatrick const MachOObjectFile *Obj) {
30209467b48Spatrick ArrayRef<uint8_t> dbg_uuid = DbgObj->getUuid();
30309467b48Spatrick ArrayRef<uint8_t> bin_uuid = Obj->getUuid();
30409467b48Spatrick if (dbg_uuid.empty() || bin_uuid.empty())
30509467b48Spatrick return false;
30609467b48Spatrick return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size());
30709467b48Spatrick }
30809467b48Spatrick
30909467b48Spatrick } // end anonymous namespace
31009467b48Spatrick
lookUpDsymFile(const std::string & ExePath,const MachOObjectFile * MachExeObj,const std::string & ArchName)31109467b48Spatrick ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath,
31273471bf0Spatrick const MachOObjectFile *MachExeObj,
31373471bf0Spatrick const std::string &ArchName) {
31409467b48Spatrick // On Darwin we may find DWARF in separate object file in
31509467b48Spatrick // resource directory.
31609467b48Spatrick std::vector<std::string> DsymPaths;
31709467b48Spatrick StringRef Filename = sys::path::filename(ExePath);
318097a140dSpatrick DsymPaths.push_back(
319097a140dSpatrick getDarwinDWARFResourceForPath(ExePath, std::string(Filename)));
32009467b48Spatrick for (const auto &Path : Opts.DsymHints) {
321097a140dSpatrick DsymPaths.push_back(
322097a140dSpatrick getDarwinDWARFResourceForPath(Path, std::string(Filename)));
32309467b48Spatrick }
32409467b48Spatrick for (const auto &Path : DsymPaths) {
32509467b48Spatrick auto DbgObjOrErr = getOrCreateObject(Path, ArchName);
32609467b48Spatrick if (!DbgObjOrErr) {
32709467b48Spatrick // Ignore errors, the file might not exist.
32809467b48Spatrick consumeError(DbgObjOrErr.takeError());
32909467b48Spatrick continue;
33009467b48Spatrick }
33109467b48Spatrick ObjectFile *DbgObj = DbgObjOrErr.get();
33209467b48Spatrick if (!DbgObj)
33309467b48Spatrick continue;
33409467b48Spatrick const MachOObjectFile *MachDbgObj = dyn_cast<const MachOObjectFile>(DbgObj);
33509467b48Spatrick if (!MachDbgObj)
33609467b48Spatrick continue;
33709467b48Spatrick if (darwinDsymMatchesBinary(MachDbgObj, MachExeObj))
33809467b48Spatrick return DbgObj;
33909467b48Spatrick }
34009467b48Spatrick return nullptr;
34109467b48Spatrick }
34209467b48Spatrick
lookUpDebuglinkObject(const std::string & Path,const ObjectFile * Obj,const std::string & ArchName)34309467b48Spatrick ObjectFile *LLVMSymbolizer::lookUpDebuglinkObject(const std::string &Path,
34409467b48Spatrick const ObjectFile *Obj,
34509467b48Spatrick const std::string &ArchName) {
34609467b48Spatrick std::string DebuglinkName;
34709467b48Spatrick uint32_t CRCHash;
34809467b48Spatrick std::string DebugBinaryPath;
34909467b48Spatrick if (!getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash))
35009467b48Spatrick return nullptr;
351*d415bd75Srobert if (!findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath))
35209467b48Spatrick return nullptr;
35309467b48Spatrick auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName);
35409467b48Spatrick if (!DbgObjOrErr) {
35509467b48Spatrick // Ignore errors, the file might not exist.
35609467b48Spatrick consumeError(DbgObjOrErr.takeError());
35709467b48Spatrick return nullptr;
35809467b48Spatrick }
35909467b48Spatrick return DbgObjOrErr.get();
36009467b48Spatrick }
36109467b48Spatrick
lookUpBuildIDObject(const std::string & Path,const ELFObjectFileBase * Obj,const std::string & ArchName)36209467b48Spatrick ObjectFile *LLVMSymbolizer::lookUpBuildIDObject(const std::string &Path,
36309467b48Spatrick const ELFObjectFileBase *Obj,
36409467b48Spatrick const std::string &ArchName) {
36509467b48Spatrick auto BuildID = getBuildID(Obj);
36609467b48Spatrick if (!BuildID)
36709467b48Spatrick return nullptr;
36809467b48Spatrick if (BuildID->size() < 2)
36909467b48Spatrick return nullptr;
37009467b48Spatrick std::string DebugBinaryPath;
371*d415bd75Srobert if (!getOrFindDebugBinary(*BuildID, DebugBinaryPath))
37209467b48Spatrick return nullptr;
37309467b48Spatrick auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName);
37409467b48Spatrick if (!DbgObjOrErr) {
37509467b48Spatrick consumeError(DbgObjOrErr.takeError());
37609467b48Spatrick return nullptr;
37709467b48Spatrick }
37809467b48Spatrick return DbgObjOrErr.get();
37909467b48Spatrick }
38009467b48Spatrick
findDebugBinary(const std::string & OrigPath,const std::string & DebuglinkName,uint32_t CRCHash,std::string & Result)381*d415bd75Srobert bool LLVMSymbolizer::findDebugBinary(const std::string &OrigPath,
382*d415bd75Srobert const std::string &DebuglinkName,
383*d415bd75Srobert uint32_t CRCHash, std::string &Result) {
384*d415bd75Srobert SmallString<16> OrigDir(OrigPath);
385*d415bd75Srobert llvm::sys::path::remove_filename(OrigDir);
386*d415bd75Srobert SmallString<16> DebugPath = OrigDir;
387*d415bd75Srobert // Try relative/path/to/original_binary/debuglink_name
388*d415bd75Srobert llvm::sys::path::append(DebugPath, DebuglinkName);
389*d415bd75Srobert if (checkFileCRC(DebugPath, CRCHash)) {
390*d415bd75Srobert Result = std::string(DebugPath.str());
391*d415bd75Srobert return true;
392*d415bd75Srobert }
393*d415bd75Srobert // Try relative/path/to/original_binary/.debug/debuglink_name
394*d415bd75Srobert DebugPath = OrigDir;
395*d415bd75Srobert llvm::sys::path::append(DebugPath, ".debug", DebuglinkName);
396*d415bd75Srobert if (checkFileCRC(DebugPath, CRCHash)) {
397*d415bd75Srobert Result = std::string(DebugPath.str());
398*d415bd75Srobert return true;
399*d415bd75Srobert }
400*d415bd75Srobert // Make the path absolute so that lookups will go to
401*d415bd75Srobert // "/usr/lib/debug/full/path/to/debug", not
402*d415bd75Srobert // "/usr/lib/debug/to/debug"
403*d415bd75Srobert llvm::sys::fs::make_absolute(OrigDir);
404*d415bd75Srobert if (!Opts.FallbackDebugPath.empty()) {
405*d415bd75Srobert // Try <FallbackDebugPath>/absolute/path/to/original_binary/debuglink_name
406*d415bd75Srobert DebugPath = Opts.FallbackDebugPath;
407*d415bd75Srobert } else {
408*d415bd75Srobert #if defined(__NetBSD__)
409*d415bd75Srobert // Try /usr/libdata/debug/absolute/path/to/original_binary/debuglink_name
410*d415bd75Srobert DebugPath = "/usr/libdata/debug";
411*d415bd75Srobert #else
412*d415bd75Srobert // Try /usr/lib/debug/absolute/path/to/original_binary/debuglink_name
413*d415bd75Srobert DebugPath = "/usr/lib/debug";
414*d415bd75Srobert #endif
415*d415bd75Srobert }
416*d415bd75Srobert llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir),
417*d415bd75Srobert DebuglinkName);
418*d415bd75Srobert if (checkFileCRC(DebugPath, CRCHash)) {
419*d415bd75Srobert Result = std::string(DebugPath.str());
420*d415bd75Srobert return true;
421*d415bd75Srobert }
422*d415bd75Srobert return false;
423*d415bd75Srobert }
424*d415bd75Srobert
getBuildIDStr(ArrayRef<uint8_t> BuildID)425*d415bd75Srobert static StringRef getBuildIDStr(ArrayRef<uint8_t> BuildID) {
426*d415bd75Srobert return StringRef(reinterpret_cast<const char *>(BuildID.data()),
427*d415bd75Srobert BuildID.size());
428*d415bd75Srobert }
429*d415bd75Srobert
getOrFindDebugBinary(const ArrayRef<uint8_t> BuildID,std::string & Result)430*d415bd75Srobert bool LLVMSymbolizer::getOrFindDebugBinary(const ArrayRef<uint8_t> BuildID,
431*d415bd75Srobert std::string &Result) {
432*d415bd75Srobert StringRef BuildIDStr = getBuildIDStr(BuildID);
433*d415bd75Srobert auto I = BuildIDPaths.find(BuildIDStr);
434*d415bd75Srobert if (I != BuildIDPaths.end()) {
435*d415bd75Srobert Result = I->second;
436*d415bd75Srobert return true;
437*d415bd75Srobert }
438*d415bd75Srobert if (!BIDFetcher)
439*d415bd75Srobert return false;
440*d415bd75Srobert if (std::optional<std::string> Path = BIDFetcher->fetch(BuildID)) {
441*d415bd75Srobert Result = *Path;
442*d415bd75Srobert auto InsertResult = BuildIDPaths.insert({BuildIDStr, Result});
443*d415bd75Srobert assert(InsertResult.second);
444*d415bd75Srobert (void)InsertResult;
445*d415bd75Srobert return true;
446*d415bd75Srobert }
447*d415bd75Srobert
448*d415bd75Srobert return false;
449*d415bd75Srobert }
450*d415bd75Srobert
45109467b48Spatrick Expected<LLVMSymbolizer::ObjectPair>
getOrCreateObjectPair(const std::string & Path,const std::string & ArchName)45209467b48Spatrick LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path,
45309467b48Spatrick const std::string &ArchName) {
45409467b48Spatrick auto I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName));
455*d415bd75Srobert if (I != ObjectPairForPathArch.end()) {
456*d415bd75Srobert recordAccess(BinaryForPath.find(Path)->second);
45709467b48Spatrick return I->second;
458*d415bd75Srobert }
45909467b48Spatrick
46009467b48Spatrick auto ObjOrErr = getOrCreateObject(Path, ArchName);
46109467b48Spatrick if (!ObjOrErr) {
46209467b48Spatrick ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName),
46309467b48Spatrick ObjectPair(nullptr, nullptr));
46409467b48Spatrick return ObjOrErr.takeError();
46509467b48Spatrick }
46609467b48Spatrick
46709467b48Spatrick ObjectFile *Obj = ObjOrErr.get();
46809467b48Spatrick assert(Obj != nullptr);
46909467b48Spatrick ObjectFile *DbgObj = nullptr;
47009467b48Spatrick
47109467b48Spatrick if (auto MachObj = dyn_cast<const MachOObjectFile>(Obj))
47209467b48Spatrick DbgObj = lookUpDsymFile(Path, MachObj, ArchName);
47309467b48Spatrick else if (auto ELFObj = dyn_cast<const ELFObjectFileBase>(Obj))
47409467b48Spatrick DbgObj = lookUpBuildIDObject(Path, ELFObj, ArchName);
47509467b48Spatrick if (!DbgObj)
47609467b48Spatrick DbgObj = lookUpDebuglinkObject(Path, Obj, ArchName);
47709467b48Spatrick if (!DbgObj)
47809467b48Spatrick DbgObj = Obj;
47909467b48Spatrick ObjectPair Res = std::make_pair(Obj, DbgObj);
480*d415bd75Srobert std::string DbgObjPath = DbgObj->getFileName().str();
481*d415bd75Srobert auto Pair =
48209467b48Spatrick ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), Res);
483*d415bd75Srobert BinaryForPath.find(DbgObjPath)->second.pushEvictor([this, I = Pair.first]() {
484*d415bd75Srobert ObjectPairForPathArch.erase(I);
485*d415bd75Srobert });
48609467b48Spatrick return Res;
48709467b48Spatrick }
48809467b48Spatrick
48909467b48Spatrick Expected<ObjectFile *>
getOrCreateObject(const std::string & Path,const std::string & ArchName)49009467b48Spatrick LLVMSymbolizer::getOrCreateObject(const std::string &Path,
49109467b48Spatrick const std::string &ArchName) {
49209467b48Spatrick Binary *Bin;
49309467b48Spatrick auto Pair = BinaryForPath.emplace(Path, OwningBinary<Binary>());
49409467b48Spatrick if (!Pair.second) {
495*d415bd75Srobert Bin = Pair.first->second->getBinary();
496*d415bd75Srobert recordAccess(Pair.first->second);
49709467b48Spatrick } else {
49809467b48Spatrick Expected<OwningBinary<Binary>> BinOrErr = createBinary(Path);
49909467b48Spatrick if (!BinOrErr)
50009467b48Spatrick return BinOrErr.takeError();
501*d415bd75Srobert
502*d415bd75Srobert CachedBinary &CachedBin = Pair.first->second;
503*d415bd75Srobert CachedBin = std::move(BinOrErr.get());
504*d415bd75Srobert CachedBin.pushEvictor([this, I = Pair.first]() { BinaryForPath.erase(I); });
505*d415bd75Srobert LRUBinaries.push_back(CachedBin);
506*d415bd75Srobert CacheSize += CachedBin.size();
507*d415bd75Srobert Bin = CachedBin->getBinary();
50809467b48Spatrick }
50909467b48Spatrick
51009467b48Spatrick if (!Bin)
51109467b48Spatrick return static_cast<ObjectFile *>(nullptr);
51209467b48Spatrick
51309467b48Spatrick if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) {
51409467b48Spatrick auto I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName));
51509467b48Spatrick if (I != ObjectForUBPathAndArch.end())
51609467b48Spatrick return I->second.get();
51709467b48Spatrick
51809467b48Spatrick Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
51909467b48Spatrick UB->getMachOObjectForArch(ArchName);
52009467b48Spatrick if (!ObjOrErr) {
52109467b48Spatrick ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName),
52209467b48Spatrick std::unique_ptr<ObjectFile>());
52309467b48Spatrick return ObjOrErr.takeError();
52409467b48Spatrick }
52509467b48Spatrick ObjectFile *Res = ObjOrErr->get();
526*d415bd75Srobert auto Pair = ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName),
52709467b48Spatrick std::move(ObjOrErr.get()));
528*d415bd75Srobert BinaryForPath.find(Path)->second.pushEvictor(
529*d415bd75Srobert [this, Iter = Pair.first]() { ObjectForUBPathAndArch.erase(Iter); });
53009467b48Spatrick return Res;
53109467b48Spatrick }
53209467b48Spatrick if (Bin->isObject()) {
53309467b48Spatrick return cast<ObjectFile>(Bin);
53409467b48Spatrick }
53509467b48Spatrick return errorCodeToError(object_error::arch_not_found);
53609467b48Spatrick }
53709467b48Spatrick
53809467b48Spatrick Expected<SymbolizableModule *>
createModuleInfo(const ObjectFile * Obj,std::unique_ptr<DIContext> Context,StringRef ModuleName)53909467b48Spatrick LLVMSymbolizer::createModuleInfo(const ObjectFile *Obj,
54009467b48Spatrick std::unique_ptr<DIContext> Context,
54109467b48Spatrick StringRef ModuleName) {
54209467b48Spatrick auto InfoOrErr = SymbolizableObjectFile::create(Obj, std::move(Context),
54309467b48Spatrick Opts.UntagAddresses);
54409467b48Spatrick std::unique_ptr<SymbolizableModule> SymMod;
54509467b48Spatrick if (InfoOrErr)
54609467b48Spatrick SymMod = std::move(*InfoOrErr);
547097a140dSpatrick auto InsertResult = Modules.insert(
548097a140dSpatrick std::make_pair(std::string(ModuleName), std::move(SymMod)));
54909467b48Spatrick assert(InsertResult.second);
550097a140dSpatrick if (!InfoOrErr)
551097a140dSpatrick return InfoOrErr.takeError();
55209467b48Spatrick return InsertResult.first->second.get();
55309467b48Spatrick }
55409467b48Spatrick
55509467b48Spatrick Expected<SymbolizableModule *>
getOrCreateModuleInfo(const std::string & ModuleName)55609467b48Spatrick LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
55709467b48Spatrick std::string BinaryName = ModuleName;
55809467b48Spatrick std::string ArchName = Opts.DefaultArch;
55909467b48Spatrick size_t ColonPos = ModuleName.find_last_of(':');
56009467b48Spatrick // Verify that substring after colon form a valid arch name.
56109467b48Spatrick if (ColonPos != std::string::npos) {
56209467b48Spatrick std::string ArchStr = ModuleName.substr(ColonPos + 1);
56309467b48Spatrick if (Triple(ArchStr).getArch() != Triple::UnknownArch) {
56409467b48Spatrick BinaryName = ModuleName.substr(0, ColonPos);
56509467b48Spatrick ArchName = ArchStr;
56609467b48Spatrick }
56709467b48Spatrick }
568*d415bd75Srobert
569*d415bd75Srobert auto I = Modules.find(ModuleName);
570*d415bd75Srobert if (I != Modules.end()) {
571*d415bd75Srobert recordAccess(BinaryForPath.find(BinaryName)->second);
572*d415bd75Srobert return I->second.get();
573*d415bd75Srobert }
574*d415bd75Srobert
57509467b48Spatrick auto ObjectsOrErr = getOrCreateObjectPair(BinaryName, ArchName);
57609467b48Spatrick if (!ObjectsOrErr) {
57709467b48Spatrick // Failed to find valid object file.
57809467b48Spatrick Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());
57909467b48Spatrick return ObjectsOrErr.takeError();
58009467b48Spatrick }
58109467b48Spatrick ObjectPair Objects = ObjectsOrErr.get();
58209467b48Spatrick
58309467b48Spatrick std::unique_ptr<DIContext> Context;
58409467b48Spatrick // If this is a COFF object containing PDB info, use a PDBContext to
58509467b48Spatrick // symbolize. Otherwise, use DWARF.
58609467b48Spatrick if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) {
58709467b48Spatrick const codeview::DebugInfo *DebugInfo;
58809467b48Spatrick StringRef PDBFileName;
58909467b48Spatrick auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName);
59009467b48Spatrick if (!EC && DebugInfo != nullptr && !PDBFileName.empty()) {
59109467b48Spatrick using namespace pdb;
59209467b48Spatrick std::unique_ptr<IPDBSession> Session;
59373471bf0Spatrick
59473471bf0Spatrick PDB_ReaderType ReaderType =
59573471bf0Spatrick Opts.UseDIA ? PDB_ReaderType::DIA : PDB_ReaderType::Native;
596097a140dSpatrick if (auto Err = loadDataForEXE(ReaderType, Objects.first->getFileName(),
597097a140dSpatrick Session)) {
59809467b48Spatrick Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());
59909467b48Spatrick // Return along the PDB filename to provide more context
60009467b48Spatrick return createFileError(PDBFileName, std::move(Err));
60109467b48Spatrick }
60209467b48Spatrick Context.reset(new PDBContext(*CoffObject, std::move(Session)));
60309467b48Spatrick }
60409467b48Spatrick }
60509467b48Spatrick if (!Context)
606*d415bd75Srobert Context = DWARFContext::create(
607*d415bd75Srobert *Objects.second, DWARFContext::ProcessDebugRelocations::Process,
608*d415bd75Srobert nullptr, Opts.DWPName);
609*d415bd75Srobert auto ModuleOrErr =
610*d415bd75Srobert createModuleInfo(Objects.first, std::move(Context), ModuleName);
611*d415bd75Srobert if (ModuleOrErr) {
612*d415bd75Srobert auto I = Modules.find(ModuleName);
613*d415bd75Srobert BinaryForPath.find(BinaryName)->second.pushEvictor([this, I]() {
614*d415bd75Srobert Modules.erase(I);
615*d415bd75Srobert });
616*d415bd75Srobert }
617*d415bd75Srobert return ModuleOrErr;
61809467b48Spatrick }
61909467b48Spatrick
62073471bf0Spatrick Expected<SymbolizableModule *>
getOrCreateModuleInfo(const ObjectFile & Obj)62173471bf0Spatrick LLVMSymbolizer::getOrCreateModuleInfo(const ObjectFile &Obj) {
62273471bf0Spatrick StringRef ObjName = Obj.getFileName();
62373471bf0Spatrick auto I = Modules.find(ObjName);
62473471bf0Spatrick if (I != Modules.end())
62573471bf0Spatrick return I->second.get();
62673471bf0Spatrick
62773471bf0Spatrick std::unique_ptr<DIContext> Context = DWARFContext::create(Obj);
62873471bf0Spatrick // FIXME: handle COFF object with PDB info to use PDBContext
62973471bf0Spatrick return createModuleInfo(&Obj, std::move(Context), ObjName);
63073471bf0Spatrick }
63173471bf0Spatrick
632*d415bd75Srobert Expected<SymbolizableModule *>
getOrCreateModuleInfo(ArrayRef<uint8_t> BuildID)633*d415bd75Srobert LLVMSymbolizer::getOrCreateModuleInfo(ArrayRef<uint8_t> BuildID) {
634*d415bd75Srobert std::string Path;
635*d415bd75Srobert if (!getOrFindDebugBinary(BuildID, Path)) {
636*d415bd75Srobert return createStringError(errc::no_such_file_or_directory,
637*d415bd75Srobert Twine("could not find build ID '") +
638*d415bd75Srobert toHex(BuildID) + "'");
639*d415bd75Srobert }
640*d415bd75Srobert return getOrCreateModuleInfo(Path);
641*d415bd75Srobert }
642*d415bd75Srobert
64309467b48Spatrick namespace {
64409467b48Spatrick
64509467b48Spatrick // Undo these various manglings for Win32 extern "C" functions:
64609467b48Spatrick // cdecl - _foo
64709467b48Spatrick // stdcall - _foo@12
64809467b48Spatrick // fastcall - @foo@12
64909467b48Spatrick // vectorcall - foo@@12
65009467b48Spatrick // These are all different linkage names for 'foo'.
demanglePE32ExternCFunc(StringRef SymbolName)65109467b48Spatrick StringRef demanglePE32ExternCFunc(StringRef SymbolName) {
65209467b48Spatrick char Front = SymbolName.empty() ? '\0' : SymbolName[0];
65309467b48Spatrick
65409467b48Spatrick // Remove any '@[0-9]+' suffix.
655*d415bd75Srobert bool HasAtNumSuffix = false;
65609467b48Spatrick if (Front != '?') {
65709467b48Spatrick size_t AtPos = SymbolName.rfind('@');
65809467b48Spatrick if (AtPos != StringRef::npos &&
659*d415bd75Srobert all_of(drop_begin(SymbolName, AtPos + 1), isDigit)) {
66009467b48Spatrick SymbolName = SymbolName.substr(0, AtPos);
661*d415bd75Srobert HasAtNumSuffix = true;
662*d415bd75Srobert }
66309467b48Spatrick }
66409467b48Spatrick
66509467b48Spatrick // Remove any ending '@' for vectorcall.
666*d415bd75Srobert bool IsVectorCall = false;
667*d415bd75Srobert if (HasAtNumSuffix && SymbolName.endswith("@")) {
66809467b48Spatrick SymbolName = SymbolName.drop_back();
669*d415bd75Srobert IsVectorCall = true;
670*d415bd75Srobert }
671*d415bd75Srobert
672*d415bd75Srobert // If not vectorcall, remove any '_' or '@' prefix.
673*d415bd75Srobert if (!IsVectorCall && (Front == '_' || Front == '@'))
674*d415bd75Srobert SymbolName = SymbolName.drop_front();
67509467b48Spatrick
67609467b48Spatrick return SymbolName;
67709467b48Spatrick }
67809467b48Spatrick
67909467b48Spatrick } // end anonymous namespace
68009467b48Spatrick
68109467b48Spatrick std::string
DemangleName(const std::string & Name,const SymbolizableModule * DbiModuleDescriptor)68209467b48Spatrick LLVMSymbolizer::DemangleName(const std::string &Name,
68309467b48Spatrick const SymbolizableModule *DbiModuleDescriptor) {
684*d415bd75Srobert std::string Result;
685*d415bd75Srobert if (nonMicrosoftDemangle(Name.c_str(), Result))
68609467b48Spatrick return Result;
68709467b48Spatrick
68809467b48Spatrick if (!Name.empty() && Name.front() == '?') {
68909467b48Spatrick // Only do MSVC C++ demangling on symbols starting with '?'.
69009467b48Spatrick int status = 0;
69109467b48Spatrick char *DemangledName = microsoftDemangle(
692097a140dSpatrick Name.c_str(), nullptr, nullptr, nullptr, &status,
69309467b48Spatrick MSDemangleFlags(MSDF_NoAccessSpecifier | MSDF_NoCallingConvention |
69409467b48Spatrick MSDF_NoMemberType | MSDF_NoReturnType));
69509467b48Spatrick if (status != 0)
69609467b48Spatrick return Name;
697*d415bd75Srobert Result = DemangledName;
69809467b48Spatrick free(DemangledName);
69909467b48Spatrick return Result;
70009467b48Spatrick }
70109467b48Spatrick
702*d415bd75Srobert if (DbiModuleDescriptor && DbiModuleDescriptor->isWin32Module()) {
703*d415bd75Srobert std::string DemangledCName(demanglePE32ExternCFunc(Name));
704*d415bd75Srobert // On i386 Windows, the C name mangling for different calling conventions
705*d415bd75Srobert // may also be applied on top of the Itanium or Rust name mangling.
706*d415bd75Srobert if (nonMicrosoftDemangle(DemangledCName.c_str(), Result))
707*d415bd75Srobert return Result;
708*d415bd75Srobert return DemangledCName;
709*d415bd75Srobert }
71009467b48Spatrick return Name;
71109467b48Spatrick }
71209467b48Spatrick
recordAccess(CachedBinary & Bin)713*d415bd75Srobert void LLVMSymbolizer::recordAccess(CachedBinary &Bin) {
714*d415bd75Srobert if (Bin->getBinary())
715*d415bd75Srobert LRUBinaries.splice(LRUBinaries.end(), LRUBinaries, Bin.getIterator());
716*d415bd75Srobert }
717*d415bd75Srobert
pruneCache()718*d415bd75Srobert void LLVMSymbolizer::pruneCache() {
719*d415bd75Srobert // Evict the LRU binary until the max cache size is reached or there's <= 1
720*d415bd75Srobert // item in the cache. The MRU binary is always kept to avoid thrashing if it's
721*d415bd75Srobert // larger than the cache size.
722*d415bd75Srobert while (CacheSize > Opts.MaxCacheSize && !LRUBinaries.empty() &&
723*d415bd75Srobert std::next(LRUBinaries.begin()) != LRUBinaries.end()) {
724*d415bd75Srobert CachedBinary &Bin = LRUBinaries.front();
725*d415bd75Srobert CacheSize -= Bin.size();
726*d415bd75Srobert LRUBinaries.pop_front();
727*d415bd75Srobert Bin.evict();
728*d415bd75Srobert }
729*d415bd75Srobert }
730*d415bd75Srobert
pushEvictor(std::function<void ()> NewEvictor)731*d415bd75Srobert void CachedBinary::pushEvictor(std::function<void()> NewEvictor) {
732*d415bd75Srobert if (Evictor) {
733*d415bd75Srobert this->Evictor = [OldEvictor = std::move(this->Evictor),
734*d415bd75Srobert NewEvictor = std::move(NewEvictor)]() {
735*d415bd75Srobert NewEvictor();
736*d415bd75Srobert OldEvictor();
737*d415bd75Srobert };
738*d415bd75Srobert } else {
739*d415bd75Srobert this->Evictor = std::move(NewEvictor);
740*d415bd75Srobert }
741*d415bd75Srobert }
742*d415bd75Srobert
74309467b48Spatrick } // namespace symbolize
74409467b48Spatrick } // namespace llvm
745