diff --git a/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h b/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h --- a/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h @@ -16,6 +16,7 @@ #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" #include "llvm/Object/Binary.h" #include "llvm/Object/ObjectFile.h" +#include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Error.h" #include #include @@ -44,6 +45,7 @@ std::vector DsymHints; std::string FallbackDebugPath; std::string DWPName; + std::vector DebugFileDirectory; }; LLVMSymbolizer() = default; @@ -98,6 +100,9 @@ ObjectFile *lookUpDebuglinkObject(const std::string &Path, const ObjectFile *Obj, const std::string &ArchName); + ObjectFile *lookUpBuildIDObject(const std::string &Path, + const ELFObjectFileBase *Obj, + const std::string &ArchName); /// Returns pair of pointers to object and debug object. Expected getOrCreateObjectPair(const std::string &Path, diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp --- a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp +++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -284,6 +284,79 @@ return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size()); } +template +Optional> getBuildID(const ELFFile *Obj) { + if (!Obj) + return {}; + auto PhdrsOrErr = Obj->program_headers(); + if (!PhdrsOrErr) { + consumeError(PhdrsOrErr.takeError()); + return {}; + } + for (const auto &P : *PhdrsOrErr) { + if (P.p_type != ELF::PT_NOTE) + continue; + Error Err = Error::success(); + for (const auto &N : Obj->notes(P, Err)) + if (N.getType() == ELF::NT_GNU_BUILD_ID && N.getName() == ELF::ELF_NOTE_GNU) + return N.getDesc(); + } + return {}; +} + +Optional> getBuildID(const ELFObjectFileBase *Obj) { + Optional> BuildID; + if (auto *O = dyn_cast>(Obj)) + BuildID = getBuildID(O->getELFFile()); + else if (auto *O = dyn_cast>(Obj)) + BuildID = getBuildID(O->getELFFile()); + else if (auto *O = dyn_cast>(Obj)) + BuildID = getBuildID(O->getELFFile()); + else if (auto *O = dyn_cast>(Obj)) + BuildID = getBuildID(O->getELFFile()); + else + llvm_unreachable("unsupported file format"); + return BuildID; +} + +bool findDebugBinary(const std::vector &DebugFileDirectory, + const ArrayRef BuildID, + std::string &Result) { + auto getDebugPath = [&](StringRef Directory) { + SmallString<128> Path{Directory}; + sys::path::append(Path, ".build-id", + llvm::toHex(BuildID[0], /*LowerCase=*/true), + llvm::toHex(BuildID.slice(1), /*LowerCase=*/true)); + Path += ".debug"; + return Path; + }; + if (DebugFileDirectory.empty()) { + SmallString<128> Path = getDebugPath( +#if defined(__NetBSD__) + // Try /usr/libdata/debug/.build-id/../... + "/usr/libdata/debug" +#else + // Try /usr/lib/debug/.build-id/../... + "/usr/lib/debug" +#endif + ); + if (llvm::sys::fs::exists(Path)) { + Result = Path.str(); + return true; + } + } else { + for (const auto &Directory : DebugFileDirectory) { + // Try /.build-id/../... + SmallString<128> Path = getDebugPath(Directory); + if (llvm::sys::fs::exists(Path)) { + Result = Path.str(); + return true; + } + } + } + return false; +} + } // end anonymous namespace ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, @@ -335,6 +408,25 @@ return DbgObjOrErr.get(); } +ObjectFile *LLVMSymbolizer::lookUpBuildIDObject(const std::string &Path, + const ELFObjectFileBase *Obj, + const std::string &ArchName) { + auto BuildID = getBuildID(Obj); + if (!BuildID) + return nullptr; + if (BuildID->size() < 2) + return nullptr; + std::string DebugBinaryPath; + if (!findDebugBinary(Opts.DebugFileDirectory, *BuildID, DebugBinaryPath)) + return nullptr; + auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); + if (!DbgObjOrErr) { + consumeError(DbgObjOrErr.takeError()); + return nullptr; + } + return DbgObjOrErr.get(); +} + Expected LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path, const std::string &ArchName) { @@ -355,6 +447,8 @@ if (auto MachObj = dyn_cast(Obj)) DbgObj = lookUpDsymFile(Path, MachObj, ArchName); + else if (auto ELFObj = dyn_cast(Obj)) + DbgObj = lookUpBuildIDObject(Path, ELFObj, ArchName); if (!DbgObj) DbgObj = lookUpDebuglinkObject(Path, Obj, ArchName); if (!DbgObj) diff --git a/llvm/test/DebugInfo/Inputs/.build-id/ab/b50d82b6bdc861.debug b/llvm/test/DebugInfo/Inputs/.build-id/ab/b50d82b6bdc861.debug new file mode 100755 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ + ClDebugFileDirectory("debug-file-directory", cl::ZeroOrMore, + cl::value_desc("dir"), + cl::desc("Path to directory where to look for debug " + "files.")); + static cl::opt ClOutputStyle("output-style", cl::init(DIPrinter::OutputStyle::LLVM), cl::desc("Specify print style"), @@ -299,6 +305,7 @@ Opts.DefaultArch = ClDefaultArch; Opts.FallbackDebugPath = ClFallbackDebugPath; Opts.DWPName = ClDwpName; + Opts.DebugFileDirectory = ClDebugFileDirectory; for (const auto &hint : ClDsymHint) { if (sys::path::extension(hint) == ".dSYM") {