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