1//===- llvm/Object/BuildID.cpp - Build ID ---------------------------------===//
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/// \file
10/// This file defines a library for handling Build IDs and using them to find
11/// debug info.
12///
13//===----------------------------------------------------------------------===//
14
15#include "llvm/Object/BuildID.h"
16
17#include "llvm/Object/ELFObjectFile.h"
18#include "llvm/Support/Error.h"
19#include "llvm/Support/FileSystem.h"
20#include "llvm/Support/Path.h"
21
22using namespace llvm;
23using namespace llvm::object;
24
25namespace {
26
27template <typename ELFT> BuildIDRef getBuildID(const ELFFile<ELFT> &Obj) {
28 auto findBuildID = [&Obj](const auto &ShdrOrPhdr,
29 uint64_t Alignment) -> std::optional<BuildIDRef> {
30 Error Err = Error::success();
31 for (auto N : Obj.notes(ShdrOrPhdr, Err))
32 if (N.getType() == ELF::NT_GNU_BUILD_ID &&
33 N.getName() == ELF::ELF_NOTE_GNU)
34 return N.getDesc(Alignment);
35 consumeError(Err: std::move(Err));
36 return std::nullopt;
37 };
38
39 auto Sections = cantFail(Obj.sections());
40 for (const auto &S : Sections) {
41 if (S.sh_type != ELF::SHT_NOTE)
42 continue;
43 if (std::optional<BuildIDRef> ShdrRes = findBuildID(S, S.sh_addralign))
44 return ShdrRes.value();
45 }
46 auto PhdrsOrErr = Obj.program_headers();
47 if (!PhdrsOrErr) {
48 consumeError(PhdrsOrErr.takeError());
49 return {};
50 }
51 for (const auto &P : *PhdrsOrErr) {
52 if (P.p_type != ELF::PT_NOTE)
53 continue;
54 if (std::optional<BuildIDRef> PhdrRes = findBuildID(P, P.p_align))
55 return PhdrRes.value();
56 }
57 return {};
58}
59
60} // namespace
61
62BuildID llvm::object::parseBuildID(StringRef Str) {
63 std::string Bytes;
64 if (!tryGetFromHex(Input: Str, Output&: Bytes))
65 return {};
66 ArrayRef<uint8_t> BuildID(reinterpret_cast<const uint8_t *>(Bytes.data()),
67 Bytes.size());
68 return SmallVector<uint8_t>(BuildID);
69}
70
71BuildIDRef llvm::object::getBuildID(const ObjectFile *Obj) {
72 if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Val: Obj))
73 return ::getBuildID(Obj: O->getELFFile());
74 if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Val: Obj))
75 return ::getBuildID(Obj: O->getELFFile());
76 if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Val: Obj))
77 return ::getBuildID(Obj: O->getELFFile());
78 if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Val: Obj))
79 return ::getBuildID(Obj: O->getELFFile());
80 return {};
81}
82
83Expected<std::string> BuildIDFetcher::fetch(BuildIDRef BuildID) const {
84 auto GetDebugPath = [&](StringRef Directory) {
85 SmallString<128> Path{Directory};
86 sys::path::append(path&: Path, a: ".build-id",
87 b: llvm::toHex(Input: BuildID[0], /*LowerCase=*/true),
88 c: llvm::toHex(Input: BuildID.slice(N: 1), /*LowerCase=*/true));
89 Path += ".debug";
90 return Path;
91 };
92 if (DebugFileDirectories.empty()) {
93 SmallString<128> Path = GetDebugPath(
94#if defined(__NetBSD__)
95 // Try /usr/libdata/debug/.build-id/../...
96 "/usr/libdata/debug"
97#else
98 // Try /usr/lib/debug/.build-id/../...
99 "/usr/lib/debug"
100#endif
101 );
102 if (llvm::sys::fs::exists(Path))
103 return std::string(Path);
104 } else {
105 for (const auto &Directory : DebugFileDirectories) {
106 // Try <debug-file-directory>/.build-id/../...
107 SmallString<128> Path = GetDebugPath(Directory);
108 if (llvm::sys::fs::exists(Path))
109 return std::string(Path);
110 }
111 }
112 return createStringError(
113 EC: make_error_code(e: std::errc::no_such_file_or_directory),
114 S: "could not find debug file for build ID '" +
115 llvm::toHex(Input: BuildID, /*LowerCase=*/true) + "'");
116}
117