xref: /freebsd/contrib/llvm-project/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp (revision a3c35da61bb201168575f1d18f4ca3e96937d35c)
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 "SymbolizableObjectFile.h"
16  
17  #include "llvm/ADT/STLExtras.h"
18  #include "llvm/BinaryFormat/COFF.h"
19  #include "llvm/DebugInfo/DWARF/DWARFContext.h"
20  #include "llvm/DebugInfo/PDB/PDB.h"
21  #include "llvm/DebugInfo/PDB/PDBContext.h"
22  #include "llvm/Demangle/Demangle.h"
23  #include "llvm/Object/COFF.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/Compression.h"
29  #include "llvm/Support/DataExtractor.h"
30  #include "llvm/Support/Errc.h"
31  #include "llvm/Support/FileSystem.h"
32  #include "llvm/Support/MemoryBuffer.h"
33  #include "llvm/Support/Path.h"
34  #include <algorithm>
35  #include <cassert>
36  #include <cstring>
37  
38  #if defined(_MSC_VER)
39  #include <Windows.h>
40  
41  // This must be included after windows.h.
42  #include <DbgHelp.h>
43  #pragma comment(lib, "dbghelp.lib")
44  
45  // Windows.h conflicts with our COFF header definitions.
46  #ifdef IMAGE_FILE_MACHINE_I386
47  #undef IMAGE_FILE_MACHINE_I386
48  #endif
49  #endif
50  
51  namespace llvm {
52  namespace symbolize {
53  
54  Expected<DILineInfo>
55  LLVMSymbolizer::symbolizeCodeCommon(SymbolizableModule *Info,
56                                      object::SectionedAddress ModuleOffset) {
57    // A null module means an error has already been reported. Return an empty
58    // result.
59    if (!Info)
60      return DILineInfo();
61  
62    // If the user is giving us relative addresses, add the preferred base of the
63    // object to the offset before we do the query. It's what DIContext expects.
64    if (Opts.RelativeAddresses)
65      ModuleOffset.Address += Info->getModulePreferredBase();
66  
67    DILineInfo LineInfo = Info->symbolizeCode(ModuleOffset, Opts.PrintFunctions,
68                                              Opts.UseSymbolTable);
69    if (Opts.Demangle)
70      LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info);
71    return LineInfo;
72  }
73  
74  Expected<DILineInfo>
75  LLVMSymbolizer::symbolizeCode(const ObjectFile &Obj,
76                                object::SectionedAddress ModuleOffset) {
77    StringRef ModuleName = Obj.getFileName();
78    auto I = Modules.find(ModuleName);
79    if (I != Modules.end())
80      return symbolizeCodeCommon(I->second.get(), ModuleOffset);
81  
82    std::unique_ptr<DIContext> Context =
83          DWARFContext::create(Obj, nullptr, DWARFContext::defaultErrorHandler);
84    Expected<SymbolizableModule *> InfoOrErr =
85                       createModuleInfo(&Obj, std::move(Context), ModuleName);
86    if (!InfoOrErr)
87      return InfoOrErr.takeError();
88    return symbolizeCodeCommon(*InfoOrErr, ModuleOffset);
89  }
90  
91  Expected<DILineInfo>
92  LLVMSymbolizer::symbolizeCode(const std::string &ModuleName,
93                                object::SectionedAddress ModuleOffset) {
94    Expected<SymbolizableModule *> InfoOrErr = getOrCreateModuleInfo(ModuleName);
95    if (!InfoOrErr)
96      return InfoOrErr.takeError();
97    return symbolizeCodeCommon(*InfoOrErr, ModuleOffset);
98  }
99  
100  Expected<DIInliningInfo>
101  LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName,
102                                       object::SectionedAddress ModuleOffset) {
103    SymbolizableModule *Info;
104    if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName))
105      Info = InfoOrErr.get();
106    else
107      return InfoOrErr.takeError();
108  
109    // A null module means an error has already been reported. Return an empty
110    // result.
111    if (!Info)
112      return DIInliningInfo();
113  
114    // If the user is giving us relative addresses, add the preferred base of the
115    // object to the offset before we do the query. It's what DIContext expects.
116    if (Opts.RelativeAddresses)
117      ModuleOffset.Address += Info->getModulePreferredBase();
118  
119    DIInliningInfo InlinedContext = Info->symbolizeInlinedCode(
120        ModuleOffset, Opts.PrintFunctions, Opts.UseSymbolTable);
121    if (Opts.Demangle) {
122      for (int i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) {
123        auto *Frame = InlinedContext.getMutableFrame(i);
124        Frame->FunctionName = DemangleName(Frame->FunctionName, Info);
125      }
126    }
127    return InlinedContext;
128  }
129  
130  Expected<DIGlobal>
131  LLVMSymbolizer::symbolizeData(const std::string &ModuleName,
132                                object::SectionedAddress ModuleOffset) {
133    SymbolizableModule *Info;
134    if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName))
135      Info = InfoOrErr.get();
136    else
137      return InfoOrErr.takeError();
138  
139    // A null module means an error has already been reported. Return an empty
140    // result.
141    if (!Info)
142      return DIGlobal();
143  
144    // If the user is giving us relative addresses, add the preferred base of
145    // the object to the offset before we do the query. It's what DIContext
146    // expects.
147    if (Opts.RelativeAddresses)
148      ModuleOffset.Address += Info->getModulePreferredBase();
149  
150    DIGlobal Global = Info->symbolizeData(ModuleOffset);
151    if (Opts.Demangle)
152      Global.Name = DemangleName(Global.Name, Info);
153    return Global;
154  }
155  
156  Expected<std::vector<DILocal>>
157  LLVMSymbolizer::symbolizeFrame(const std::string &ModuleName,
158                                 object::SectionedAddress ModuleOffset) {
159    SymbolizableModule *Info;
160    if (auto InfoOrErr = getOrCreateModuleInfo(ModuleName))
161      Info = InfoOrErr.get();
162    else
163      return InfoOrErr.takeError();
164  
165    // A null module means an error has already been reported. Return an empty
166    // result.
167    if (!Info)
168      return std::vector<DILocal>();
169  
170    // If the user is giving us relative addresses, add the preferred base of
171    // the object to the offset before we do the query. It's what DIContext
172    // expects.
173    if (Opts.RelativeAddresses)
174      ModuleOffset.Address += Info->getModulePreferredBase();
175  
176    return Info->symbolizeFrame(ModuleOffset);
177  }
178  
179  void LLVMSymbolizer::flush() {
180    ObjectForUBPathAndArch.clear();
181    BinaryForPath.clear();
182    ObjectPairForPathArch.clear();
183    Modules.clear();
184  }
185  
186  namespace {
187  
188  // For Path="/path/to/foo" and Basename="foo" assume that debug info is in
189  // /path/to/foo.dSYM/Contents/Resources/DWARF/foo.
190  // For Path="/path/to/bar.dSYM" and Basename="foo" assume that debug info is in
191  // /path/to/bar.dSYM/Contents/Resources/DWARF/foo.
192  std::string getDarwinDWARFResourceForPath(
193      const std::string &Path, const std::string &Basename) {
194    SmallString<16> ResourceName = StringRef(Path);
195    if (sys::path::extension(Path) != ".dSYM") {
196      ResourceName += ".dSYM";
197    }
198    sys::path::append(ResourceName, "Contents", "Resources", "DWARF");
199    sys::path::append(ResourceName, Basename);
200    return ResourceName.str();
201  }
202  
203  bool checkFileCRC(StringRef Path, uint32_t CRCHash) {
204    ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
205        MemoryBuffer::getFileOrSTDIN(Path);
206    if (!MB)
207      return false;
208    return CRCHash == llvm::crc32(0, MB.get()->getBuffer());
209  }
210  
211  bool findDebugBinary(const std::string &OrigPath,
212                       const std::string &DebuglinkName, uint32_t CRCHash,
213                       const std::string &FallbackDebugPath,
214                       std::string &Result) {
215    SmallString<16> OrigDir(OrigPath);
216    llvm::sys::path::remove_filename(OrigDir);
217    SmallString<16> DebugPath = OrigDir;
218    // Try relative/path/to/original_binary/debuglink_name
219    llvm::sys::path::append(DebugPath, DebuglinkName);
220    if (checkFileCRC(DebugPath, CRCHash)) {
221      Result = DebugPath.str();
222      return true;
223    }
224    // Try relative/path/to/original_binary/.debug/debuglink_name
225    DebugPath = OrigDir;
226    llvm::sys::path::append(DebugPath, ".debug", DebuglinkName);
227    if (checkFileCRC(DebugPath, CRCHash)) {
228      Result = DebugPath.str();
229      return true;
230    }
231    // Make the path absolute so that lookups will go to
232    // "/usr/lib/debug/full/path/to/debug", not
233    // "/usr/lib/debug/to/debug"
234    llvm::sys::fs::make_absolute(OrigDir);
235    if (!FallbackDebugPath.empty()) {
236      // Try <FallbackDebugPath>/absolute/path/to/original_binary/debuglink_name
237      DebugPath = FallbackDebugPath;
238    } else {
239  #if defined(__NetBSD__)
240      // Try /usr/libdata/debug/absolute/path/to/original_binary/debuglink_name
241      DebugPath = "/usr/libdata/debug";
242  #else
243      // Try /usr/lib/debug/absolute/path/to/original_binary/debuglink_name
244      DebugPath = "/usr/lib/debug";
245  #endif
246    }
247    llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir),
248                            DebuglinkName);
249    if (checkFileCRC(DebugPath, CRCHash)) {
250      Result = DebugPath.str();
251      return true;
252    }
253    return false;
254  }
255  
256  bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName,
257                               uint32_t &CRCHash) {
258    if (!Obj)
259      return false;
260    for (const SectionRef &Section : Obj->sections()) {
261      StringRef Name;
262      Section.getName(Name);
263      Name = Name.substr(Name.find_first_not_of("._"));
264      if (Name == "gnu_debuglink") {
265        Expected<StringRef> ContentsOrErr = Section.getContents();
266        if (!ContentsOrErr) {
267          consumeError(ContentsOrErr.takeError());
268          return false;
269        }
270        DataExtractor DE(*ContentsOrErr, Obj->isLittleEndian(), 0);
271        uint32_t Offset = 0;
272        if (const char *DebugNameStr = DE.getCStr(&Offset)) {
273          // 4-byte align the offset.
274          Offset = (Offset + 3) & ~0x3;
275          if (DE.isValidOffsetForDataOfSize(Offset, 4)) {
276            DebugName = DebugNameStr;
277            CRCHash = DE.getU32(&Offset);
278            return true;
279          }
280        }
281        break;
282      }
283    }
284    return false;
285  }
286  
287  bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj,
288                               const MachOObjectFile *Obj) {
289    ArrayRef<uint8_t> dbg_uuid = DbgObj->getUuid();
290    ArrayRef<uint8_t> bin_uuid = Obj->getUuid();
291    if (dbg_uuid.empty() || bin_uuid.empty())
292      return false;
293    return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size());
294  }
295  
296  } // end anonymous namespace
297  
298  ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath,
299      const MachOObjectFile *MachExeObj, const std::string &ArchName) {
300    // On Darwin we may find DWARF in separate object file in
301    // resource directory.
302    std::vector<std::string> DsymPaths;
303    StringRef Filename = sys::path::filename(ExePath);
304    DsymPaths.push_back(getDarwinDWARFResourceForPath(ExePath, Filename));
305    for (const auto &Path : Opts.DsymHints) {
306      DsymPaths.push_back(getDarwinDWARFResourceForPath(Path, Filename));
307    }
308    for (const auto &Path : DsymPaths) {
309      auto DbgObjOrErr = getOrCreateObject(Path, ArchName);
310      if (!DbgObjOrErr) {
311        // Ignore errors, the file might not exist.
312        consumeError(DbgObjOrErr.takeError());
313        continue;
314      }
315      ObjectFile *DbgObj = DbgObjOrErr.get();
316      if (!DbgObj)
317        continue;
318      const MachOObjectFile *MachDbgObj = dyn_cast<const MachOObjectFile>(DbgObj);
319      if (!MachDbgObj)
320        continue;
321      if (darwinDsymMatchesBinary(MachDbgObj, MachExeObj))
322        return DbgObj;
323    }
324    return nullptr;
325  }
326  
327  ObjectFile *LLVMSymbolizer::lookUpDebuglinkObject(const std::string &Path,
328                                                    const ObjectFile *Obj,
329                                                    const std::string &ArchName) {
330    std::string DebuglinkName;
331    uint32_t CRCHash;
332    std::string DebugBinaryPath;
333    if (!getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash))
334      return nullptr;
335    if (!findDebugBinary(Path, DebuglinkName, CRCHash, Opts.FallbackDebugPath,
336                         DebugBinaryPath))
337      return nullptr;
338    auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName);
339    if (!DbgObjOrErr) {
340      // Ignore errors, the file might not exist.
341      consumeError(DbgObjOrErr.takeError());
342      return nullptr;
343    }
344    return DbgObjOrErr.get();
345  }
346  
347  Expected<LLVMSymbolizer::ObjectPair>
348  LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path,
349                                        const std::string &ArchName) {
350    auto I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName));
351    if (I != ObjectPairForPathArch.end())
352      return I->second;
353  
354    auto ObjOrErr = getOrCreateObject(Path, ArchName);
355    if (!ObjOrErr) {
356      ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName),
357                                    ObjectPair(nullptr, nullptr));
358      return ObjOrErr.takeError();
359    }
360  
361    ObjectFile *Obj = ObjOrErr.get();
362    assert(Obj != nullptr);
363    ObjectFile *DbgObj = nullptr;
364  
365    if (auto MachObj = dyn_cast<const MachOObjectFile>(Obj))
366      DbgObj = lookUpDsymFile(Path, MachObj, ArchName);
367    if (!DbgObj)
368      DbgObj = lookUpDebuglinkObject(Path, Obj, ArchName);
369    if (!DbgObj)
370      DbgObj = Obj;
371    ObjectPair Res = std::make_pair(Obj, DbgObj);
372    ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), Res);
373    return Res;
374  }
375  
376  Expected<ObjectFile *>
377  LLVMSymbolizer::getOrCreateObject(const std::string &Path,
378                                    const std::string &ArchName) {
379    Binary *Bin;
380    auto Pair = BinaryForPath.emplace(Path, OwningBinary<Binary>());
381    if (!Pair.second) {
382      Bin = Pair.first->second.getBinary();
383    } else {
384      Expected<OwningBinary<Binary>> BinOrErr = createBinary(Path);
385      if (!BinOrErr)
386        return BinOrErr.takeError();
387      Pair.first->second = std::move(BinOrErr.get());
388      Bin = Pair.first->second.getBinary();
389    }
390  
391    if (!Bin)
392      return static_cast<ObjectFile *>(nullptr);
393  
394    if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) {
395      auto I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName));
396      if (I != ObjectForUBPathAndArch.end())
397        return I->second.get();
398  
399      Expected<std::unique_ptr<ObjectFile>> ObjOrErr =
400          UB->getObjectForArch(ArchName);
401      if (!ObjOrErr) {
402        ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName),
403                                       std::unique_ptr<ObjectFile>());
404        return ObjOrErr.takeError();
405      }
406      ObjectFile *Res = ObjOrErr->get();
407      ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName),
408                                     std::move(ObjOrErr.get()));
409      return Res;
410    }
411    if (Bin->isObject()) {
412      return cast<ObjectFile>(Bin);
413    }
414    return errorCodeToError(object_error::arch_not_found);
415  }
416  
417  Expected<SymbolizableModule *>
418  LLVMSymbolizer::createModuleInfo(const ObjectFile *Obj,
419                                   std::unique_ptr<DIContext> Context,
420                                   StringRef ModuleName) {
421    auto InfoOrErr =
422        SymbolizableObjectFile::create(Obj, std::move(Context));
423    std::unique_ptr<SymbolizableModule> SymMod;
424    if (InfoOrErr)
425      SymMod = std::move(*InfoOrErr);
426    auto InsertResult =
427        Modules.insert(std::make_pair(ModuleName, std::move(SymMod)));
428    assert(InsertResult.second);
429    if (std::error_code EC = InfoOrErr.getError())
430      return errorCodeToError(EC);
431    return InsertResult.first->second.get();
432  }
433  
434  Expected<SymbolizableModule *>
435  LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) {
436    auto I = Modules.find(ModuleName);
437    if (I != Modules.end())
438      return I->second.get();
439  
440    std::string BinaryName = ModuleName;
441    std::string ArchName = Opts.DefaultArch;
442    size_t ColonPos = ModuleName.find_last_of(':');
443    // Verify that substring after colon form a valid arch name.
444    if (ColonPos != std::string::npos) {
445      std::string ArchStr = ModuleName.substr(ColonPos + 1);
446      if (Triple(ArchStr).getArch() != Triple::UnknownArch) {
447        BinaryName = ModuleName.substr(0, ColonPos);
448        ArchName = ArchStr;
449      }
450    }
451    auto ObjectsOrErr = getOrCreateObjectPair(BinaryName, ArchName);
452    if (!ObjectsOrErr) {
453      // Failed to find valid object file.
454      Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());
455      return ObjectsOrErr.takeError();
456    }
457    ObjectPair Objects = ObjectsOrErr.get();
458  
459    std::unique_ptr<DIContext> Context;
460    // If this is a COFF object containing PDB info, use a PDBContext to
461    // symbolize. Otherwise, use DWARF.
462    if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) {
463      const codeview::DebugInfo *DebugInfo;
464      StringRef PDBFileName;
465      auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName);
466      if (!EC && DebugInfo != nullptr && !PDBFileName.empty()) {
467  #if 0
468        using namespace pdb;
469        std::unique_ptr<IPDBSession> Session;
470        if (auto Err = loadDataForEXE(PDB_ReaderType::DIA,
471                                      Objects.first->getFileName(), Session)) {
472          Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());
473          // Return along the PDB filename to provide more context
474          return createFileError(PDBFileName, std::move(Err));
475        }
476        Context.reset(new PDBContext(*CoffObject, std::move(Session)));
477  #else
478        return make_error<StringError>(
479            "PDB support not compiled in",
480            std::make_error_code(std::errc::not_supported));
481  #endif
482      }
483    }
484    if (!Context)
485      Context =
486          DWARFContext::create(*Objects.second, nullptr,
487                               DWARFContext::defaultErrorHandler, Opts.DWPName);
488    return createModuleInfo(Objects.first, std::move(Context), ModuleName);
489  }
490  
491  namespace {
492  
493  // Undo these various manglings for Win32 extern "C" functions:
494  // cdecl       - _foo
495  // stdcall     - _foo@12
496  // fastcall    - @foo@12
497  // vectorcall  - foo@@12
498  // These are all different linkage names for 'foo'.
499  StringRef demanglePE32ExternCFunc(StringRef SymbolName) {
500    // Remove any '_' or '@' prefix.
501    char Front = SymbolName.empty() ? '\0' : SymbolName[0];
502    if (Front == '_' || Front == '@')
503      SymbolName = SymbolName.drop_front();
504  
505    // Remove any '@[0-9]+' suffix.
506    if (Front != '?') {
507      size_t AtPos = SymbolName.rfind('@');
508      if (AtPos != StringRef::npos &&
509          std::all_of(SymbolName.begin() + AtPos + 1, SymbolName.end(),
510                      [](char C) { return C >= '0' && C <= '9'; })) {
511        SymbolName = SymbolName.substr(0, AtPos);
512      }
513    }
514  
515    // Remove any ending '@' for vectorcall.
516    if (SymbolName.endswith("@"))
517      SymbolName = SymbolName.drop_back();
518  
519    return SymbolName;
520  }
521  
522  } // end anonymous namespace
523  
524  std::string
525  LLVMSymbolizer::DemangleName(const std::string &Name,
526                               const SymbolizableModule *DbiModuleDescriptor) {
527    // We can spoil names of symbols with C linkage, so use an heuristic
528    // approach to check if the name should be demangled.
529    if (Name.substr(0, 2) == "_Z") {
530      int status = 0;
531      char *DemangledName = itaniumDemangle(Name.c_str(), nullptr, nullptr, &status);
532      if (status != 0)
533        return Name;
534      std::string Result = DemangledName;
535      free(DemangledName);
536      return Result;
537    }
538  
539  #if defined(_MSC_VER)
540    if (!Name.empty() && Name.front() == '?') {
541      // Only do MSVC C++ demangling on symbols starting with '?'.
542      char DemangledName[1024] = {0};
543      DWORD result = ::UnDecorateSymbolName(
544          Name.c_str(), DemangledName, 1023,
545          UNDNAME_NO_ACCESS_SPECIFIERS |       // Strip public, private, protected
546              UNDNAME_NO_ALLOCATION_LANGUAGE | // Strip __thiscall, __stdcall, etc
547              UNDNAME_NO_THROW_SIGNATURES |    // Strip throw() specifications
548              UNDNAME_NO_MEMBER_TYPE | // Strip virtual, static, etc specifiers
549              UNDNAME_NO_MS_KEYWORDS | // Strip all MS extension keywords
550              UNDNAME_NO_FUNCTION_RETURNS); // Strip function return types
551      return (result == 0) ? Name : std::string(DemangledName);
552    }
553  #endif
554    if (DbiModuleDescriptor && DbiModuleDescriptor->isWin32Module())
555      return std::string(demanglePE32ExternCFunc(Name));
556    return Name;
557  }
558  
559  } // namespace symbolize
560  } // namespace llvm
561