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 @@ -83,7 +83,13 @@ DemangleName(const std::string &Name, const SymbolizableModule *DbiModuleDescriptor); + bool findDebugBinary(const ArrayRef BuildID, std::string &Result); + private: + bool findDebugBinary(const std::string &OrigPath, + const std::string &DebuglinkName, uint32_t CRCHash, + std::string &Result); + // Bundles together object file with code/data and object file with // corresponding debug info. These objects can be the same. using ObjectPair = std::pair; 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 @@ -230,51 +230,6 @@ return CRCHash == llvm::crc32(arrayRefFromStringRef(MB.get()->getBuffer())); } -bool findDebugBinary(const std::string &OrigPath, - const std::string &DebuglinkName, uint32_t CRCHash, - const std::string &FallbackDebugPath, - std::string &Result) { - SmallString<16> OrigDir(OrigPath); - llvm::sys::path::remove_filename(OrigDir); - SmallString<16> DebugPath = OrigDir; - // Try relative/path/to/original_binary/debuglink_name - llvm::sys::path::append(DebugPath, DebuglinkName); - if (checkFileCRC(DebugPath, CRCHash)) { - Result = std::string(DebugPath.str()); - return true; - } - // Try relative/path/to/original_binary/.debug/debuglink_name - DebugPath = OrigDir; - llvm::sys::path::append(DebugPath, ".debug", DebuglinkName); - if (checkFileCRC(DebugPath, CRCHash)) { - Result = std::string(DebugPath.str()); - return true; - } - // Make the path absolute so that lookups will go to - // "/usr/lib/debug/full/path/to/debug", not - // "/usr/lib/debug/to/debug" - llvm::sys::fs::make_absolute(OrigDir); - if (!FallbackDebugPath.empty()) { - // Try /absolute/path/to/original_binary/debuglink_name - DebugPath = FallbackDebugPath; - } else { -#if defined(__NetBSD__) - // Try /usr/libdata/debug/absolute/path/to/original_binary/debuglink_name - DebugPath = "/usr/libdata/debug"; -#else - // Try /usr/lib/debug/absolute/path/to/original_binary/debuglink_name - DebugPath = "/usr/lib/debug"; -#endif - } - llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir), - DebuglinkName); - if (checkFileCRC(DebugPath, CRCHash)) { - Result = std::string(DebugPath.str()); - return true; - } - return false; -} - bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName, uint32_t &CRCHash) { if (!Obj) @@ -351,8 +306,10 @@ return BuildID; } -bool findDebugBinary(const std::vector &DebugFileDirectory, - const ArrayRef BuildID, std::string &Result) { +} // end anonymous namespace + +bool LLVMSymbolizer::findDebugBinary(const ArrayRef BuildID, + std::string &Result) { auto getDebugPath = [&](StringRef Directory) { SmallString<128> Path{Directory}; sys::path::append(Path, ".build-id", @@ -361,7 +318,7 @@ Path += ".debug"; return Path; }; - if (DebugFileDirectory.empty()) { + if (Opts.DebugFileDirectory.empty()) { SmallString<128> Path = getDebugPath( #if defined(__NetBSD__) // Try /usr/libdata/debug/.build-id/../... @@ -376,7 +333,7 @@ return true; } } else { - for (const auto &Directory : DebugFileDirectory) { + for (const auto &Directory : Opts.DebugFileDirectory) { // Try /.build-id/../... SmallString<128> Path = getDebugPath(Directory); if (llvm::sys::fs::exists(Path)) { @@ -395,7 +352,49 @@ return true; } -} // end anonymous namespace +bool LLVMSymbolizer::findDebugBinary(const std::string &OrigPath, + const std::string &DebuglinkName, + uint32_t CRCHash, std::string &Result) { + SmallString<16> OrigDir(OrigPath); + llvm::sys::path::remove_filename(OrigDir); + SmallString<16> DebugPath = OrigDir; + // Try relative/path/to/original_binary/debuglink_name + llvm::sys::path::append(DebugPath, DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = std::string(DebugPath.str()); + return true; + } + // Try relative/path/to/original_binary/.debug/debuglink_name + DebugPath = OrigDir; + llvm::sys::path::append(DebugPath, ".debug", DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = std::string(DebugPath.str()); + return true; + } + // Make the path absolute so that lookups will go to + // "/usr/lib/debug/full/path/to/debug", not + // "/usr/lib/debug/to/debug" + llvm::sys::fs::make_absolute(OrigDir); + if (!Opts.FallbackDebugPath.empty()) { + // Try /absolute/path/to/original_binary/debuglink_name + DebugPath = Opts.FallbackDebugPath; + } else { +#if defined(__NetBSD__) + // Try /usr/libdata/debug/absolute/path/to/original_binary/debuglink_name + DebugPath = "/usr/libdata/debug"; +#else + // Try /usr/lib/debug/absolute/path/to/original_binary/debuglink_name + DebugPath = "/usr/lib/debug"; +#endif + } + llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir), + DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = std::string(DebugPath.str()); + return true; + } + return false; +} ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, const MachOObjectFile *MachExeObj, @@ -437,8 +436,7 @@ std::string DebugBinaryPath; if (!getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash)) return nullptr; - if (!findDebugBinary(Path, DebuglinkName, CRCHash, Opts.FallbackDebugPath, - DebugBinaryPath)) + if (!findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) return nullptr; auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); if (!DbgObjOrErr) { @@ -458,7 +456,7 @@ if (BuildID->size() < 2) return nullptr; std::string DebugBinaryPath; - if (!findDebugBinary(Opts.DebugFileDirectory, *BuildID, DebugBinaryPath)) + if (!findDebugBinary(*BuildID, DebugBinaryPath)) return nullptr; auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); if (!DbgObjOrErr) { diff --git a/llvm/test/tools/llvm-symbolizer/debuginfod.test b/llvm/test/tools/llvm-symbolizer/debuginfod.test --- a/llvm/test/tools/llvm-symbolizer/debuginfod.test +++ b/llvm/test/tools/llvm-symbolizer/debuginfod.test @@ -25,3 +25,8 @@ RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer \ RUN: --obj=%t/addr.exe 0x40054d | FileCheck %s --check-prefix=FOUND FOUND: {{[/\]+}}tmp{{[/\]+}}x.c:14:0 + +# This should also work if the Build ID is provided. +RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer \ +RUN: --build-id=127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d | \ +RUN: FileCheck %s --check-prefix=FOUND diff --git a/llvm/tools/llvm-symbolizer/Opts.td b/llvm/tools/llvm-symbolizer/Opts.td --- a/llvm/tools/llvm-symbolizer/Opts.td +++ b/llvm/tools/llvm-symbolizer/Opts.td @@ -21,6 +21,7 @@ : Eq<"adjust-vma", "Add specified offset to object file addresses">, MetaVarName<"">; def basenames : Flag<["--"], "basenames">, HelpText<"Strip directory names from paths">; +defm build_id : Eq<"build-id", "Build ID used to look up the object file">; defm debug_file_directory : Eq<"debug-file-directory", "Path to directory where to look for debug files">, MetaVarName<"">; defm default_arch : Eq<"default-arch", "Default architecture (for multi-arch objects)">, diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp --- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -15,6 +15,7 @@ //===----------------------------------------------------------------------===// #include "Opts.inc" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Config/config.h" #include "llvm/DebugInfo/Symbolize/DIPrinter.h" @@ -148,15 +149,15 @@ return !Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset); } -static void symbolizeInput(const opt::InputArgList &Args, uint64_t AdjustVMA, - bool IsAddr2Line, OutputStyle Style, - StringRef InputString, LLVMSymbolizer &Symbolizer, - DIPrinter &Printer) { +static void symbolizeInput(const opt::InputArgList &Args, StringRef BinaryName, + uint64_t AdjustVMA, bool IsAddr2Line, + OutputStyle Style, StringRef InputString, + LLVMSymbolizer &Symbolizer, DIPrinter &Printer) { Command Cmd; std::string ModuleName; uint64_t Offset = 0; - if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line, - StringRef(InputString), Cmd, ModuleName, Offset)) { + if (!parseCommand(BinaryName, IsAddr2Line, StringRef(InputString), Cmd, + ModuleName, Offset)) { Printer.printInvalidCommand({ModuleName, None}, InputString); return; } @@ -260,6 +261,30 @@ return IsAddr2Line ? FunctionNameKind::None : FunctionNameKind::LinkageName; } +SmallVector parseBuildID(StringRef BuildIDStr) { + std::string Bytes; + if (!tryGetFromHex(BuildIDStr, Bytes)) + return {}; + + ArrayRef BuildID(reinterpret_cast(Bytes.data()), + Bytes.size()); + return SmallVector(BuildID.begin(), BuildID.end()); +} + +SmallVector parseBuildIDArg(const opt::InputArgList &Args, int ID) { + if (const opt::Arg *A = Args.getLastArg(ID)) { + StringRef V(A->getValue()); + SmallVector BuildID = parseBuildID(V); + if (BuildID.empty()) { + errs() << A->getSpelling() + ": expected a build ID, but got '" + V + + "'\n"; + exit(1); + } + return BuildID; + } + return {}; +} + int main(int argc, char **argv) { InitLLVM X(argc, argv); // The HTTPClient must be initialized for use by the debuginfod client. @@ -338,6 +363,15 @@ else Printer = std::make_unique(outs(), errs(), Config); + std::string BinaryName = Args.getLastArgValue(OPT_obj_EQ).str(); + SmallVector BuildID = parseBuildIDArg(Args, OPT_build_id_EQ); + if (BinaryName.empty() && !BuildID.empty()) { + if (!Symbolizer.findDebugBinary(BuildID, BinaryName)) { + errs() << "Could not find build ID: '" << toHex(BuildID) << "'\n"; + exit(1); + } + } + std::vector InputAddresses = Args.getAllArgValues(OPT_INPUT); if (InputAddresses.empty()) { const int kMaxInputStringLength = 1024; @@ -348,15 +382,15 @@ std::string StrippedInputString(InputString); llvm::erase_if(StrippedInputString, [](char c) { return c == '\r' || c == '\n'; }); - symbolizeInput(Args, AdjustVMA, IsAddr2Line, Style, StrippedInputString, - Symbolizer, *Printer); + symbolizeInput(Args, BinaryName, AdjustVMA, IsAddr2Line, Style, + StrippedInputString, Symbolizer, *Printer); outs().flush(); } } else { Printer->listBegin(); for (StringRef Address : InputAddresses) - symbolizeInput(Args, AdjustVMA, IsAddr2Line, Style, Address, Symbolizer, - *Printer); + symbolizeInput(Args, BinaryName, AdjustVMA, IsAddr2Line, Style, Address, + Symbolizer, *Printer); Printer->listEnd(); }