Files
Stefan Gränitz d0487fec8e Reland [llvm] Errorize DebuginfodFetcher for inspection at call-sites (#194872)
Failure to fetch debuginfod is rarely an error, but there are case where
we want to distinguish error reasons down the line, for example in order
to test connection timeouts.
2026-07-09 11:18:27 +02:00

117 lines
3.7 KiB
C++

//===- llvm/Object/BuildID.cpp - Build ID ---------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
/// \file
/// This file defines a library for handling Build IDs and using them to find
/// debug info.
///
//===----------------------------------------------------------------------===//
#include "llvm/Object/BuildID.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
using namespace llvm;
using namespace llvm::object;
namespace {
template <typename ELFT> BuildIDRef getBuildID(const ELFFile<ELFT> &Obj) {
auto findBuildID = [&Obj](const auto &ShdrOrPhdr,
uint64_t Alignment) -> std::optional<BuildIDRef> {
Error Err = Error::success();
for (auto N : Obj.notes(ShdrOrPhdr, Err))
if (N.getType() == ELF::NT_GNU_BUILD_ID &&
N.getName() == ELF::ELF_NOTE_GNU)
return N.getDesc(Alignment);
consumeError(std::move(Err));
return std::nullopt;
};
auto Sections = cantFail(Obj.sections());
for (const auto &S : Sections) {
if (S.sh_type != ELF::SHT_NOTE)
continue;
if (std::optional<BuildIDRef> ShdrRes = findBuildID(S, S.sh_addralign))
return ShdrRes.value();
}
auto PhdrsOrErr = Obj.program_headers();
if (!PhdrsOrErr) {
consumeError(PhdrsOrErr.takeError());
return {};
}
for (const auto &P : *PhdrsOrErr) {
if (P.p_type != ELF::PT_NOTE)
continue;
if (std::optional<BuildIDRef> PhdrRes = findBuildID(P, P.p_align))
return PhdrRes.value();
}
return {};
}
} // namespace
BuildID llvm::object::parseBuildID(StringRef Str) {
std::string Bytes;
if (!tryGetFromHex(Str, Bytes))
return {};
ArrayRef<uint8_t> BuildID(reinterpret_cast<const uint8_t *>(Bytes.data()),
Bytes.size());
return SmallVector<uint8_t>(BuildID);
}
BuildIDRef llvm::object::getBuildID(const ObjectFile *Obj) {
if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Obj))
return ::getBuildID(O->getELFFile());
if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Obj))
return ::getBuildID(O->getELFFile());
if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Obj))
return ::getBuildID(O->getELFFile());
if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Obj))
return ::getBuildID(O->getELFFile());
return {};
}
Expected<std::string> BuildIDFetcher::fetch(BuildIDRef BuildID) const {
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 (DebugFileDirectories.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))
return std::string(Path);
} else {
for (const auto &Directory : DebugFileDirectories) {
// Try <debug-file-directory>/.build-id/../...
SmallString<128> Path = GetDebugPath(Directory);
if (llvm::sys::fs::exists(Path))
return std::string(Path);
}
}
return createStringError(
make_error_code(std::errc::no_such_file_or_directory),
"could not find debug file for build ID '" +
llvm::toHex(BuildID, /*LowerCase=*/true) + "'");
}