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 
21 using namespace llvm;
22 using namespace llvm::object;
23 
24 namespace {
25 
26 template <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(std::move(Err));
41   }
42   return {};
43 }
44 
45 } // namespace
46 
47 BuildID llvm::object::parseBuildID(StringRef Str) {
48   std::string Bytes;
49   if (!tryGetFromHex(Str, 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 
56 BuildIDRef llvm::object::getBuildID(const ObjectFile *Obj) {
57   if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(Obj))
58     return ::getBuildID(O->getELFFile());
59   if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(Obj))
60     return ::getBuildID(O->getELFFile());
61   if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(Obj))
62     return ::getBuildID(O->getELFFile());
63   if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Obj))
64     return ::getBuildID(O->getELFFile());
65   return std::nullopt;
66 }
67 
68 std::optional<std::string> BuildIDFetcher::fetch(BuildIDRef BuildID) const {
69   auto GetDebugPath = [&](StringRef Directory) {
70     SmallString<128> Path{Directory};
71     sys::path::append(Path, ".build-id",
72                       llvm::toHex(BuildID[0], /*LowerCase=*/true),
73                       llvm::toHex(BuildID.slice(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