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