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