1//===- bolt/Rewrite/BuildIDRewriter.cpp -----------------------------------===//
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// Read and update build ID stored in ELF note section.
10//
11//===----------------------------------------------------------------------===//
12
13#include "bolt/Rewrite/MetadataRewriter.h"
14#include "bolt/Rewrite/MetadataRewriters.h"
15#include "llvm/Support/Errc.h"
16
17using namespace llvm;
18using namespace bolt;
19
20namespace {
21
22/// The build-id is typically a stream of 20 bytes. Return these bytes in
23/// printable hexadecimal form.
24std::string getPrintableBuildID(StringRef BuildID) {
25 std::string Str;
26 raw_string_ostream OS(Str);
27 for (const char &Char : BuildID)
28 OS << format(Fmt: "%.2x", Vals: static_cast<unsigned char>(Char));
29
30 return OS.str();
31}
32
33class BuildIDRewriter final : public MetadataRewriter {
34
35 /// Information about binary build ID.
36 ErrorOr<BinarySection &> BuildIDSection{std::errc::bad_address};
37 StringRef BuildID;
38 std::optional<uint64_t> BuildIDOffset;
39 std::optional<uint64_t> BuildIDSize;
40
41public:
42 BuildIDRewriter(StringRef Name, BinaryContext &BC)
43 : MetadataRewriter(Name, BC) {}
44
45 Error sectionInitializer() override;
46
47 Error postEmitFinalizer() override;
48};
49
50Error BuildIDRewriter::sectionInitializer() {
51 // Typically, build ID will reside in .note.gnu.build-id section. Howerver,
52 // a linker script can change the section name and such is the case with
53 // the Linux kernel. Hence, we iterate over all note sections.
54 for (BinarySection &NoteSection : BC.sections()) {
55 if (!NoteSection.isNote())
56 continue;
57
58 StringRef Buf = NoteSection.getContents();
59 DataExtractor DE = DataExtractor(Buf, BC.AsmInfo->isLittleEndian(),
60 BC.AsmInfo->getCodePointerSize());
61 DataExtractor::Cursor Cursor(0);
62 while (Cursor && !DE.eof(C: Cursor)) {
63 const uint32_t NameSz = DE.getU32(C&: Cursor);
64 const uint32_t DescSz = DE.getU32(C&: Cursor);
65 const uint32_t Type = DE.getU32(C&: Cursor);
66
67 StringRef Name =
68 NameSz ? Buf.slice(Start: Cursor.tell(), End: Cursor.tell() + NameSz) : "<empty>";
69 Cursor.seek(NewOffSet: alignTo(Value: Cursor.tell() + NameSz, Align: 4));
70
71 const uint64_t DescOffset = Cursor.tell();
72 StringRef Desc =
73 DescSz ? Buf.slice(Start: DescOffset, End: DescOffset + DescSz) : "<empty>";
74 Cursor.seek(NewOffSet: alignTo(Value: DescOffset + DescSz, Align: 4));
75
76 if (!Cursor)
77 return createStringError(EC: errc::executable_format_error,
78 Fmt: "out of bounds while reading note section: %s",
79 Vals: toString(E: Cursor.takeError()).c_str());
80
81 if (Type == ELF::NT_GNU_BUILD_ID && Name.starts_with(Prefix: "GNU") && DescSz) {
82 BuildIDSection = NoteSection;
83 BuildID = Desc;
84 BC.setFileBuildID(getPrintableBuildID(BuildID: Desc));
85 BuildIDOffset = DescOffset;
86 BuildIDSize = DescSz;
87
88 return Error::success();
89 }
90 }
91 }
92
93 return Error::success();
94}
95
96Error BuildIDRewriter::postEmitFinalizer() {
97 if (!BuildIDSection || !BuildIDOffset)
98 return Error::success();
99
100 const uint8_t LastByte = BuildID[BuildID.size() - 1];
101 SmallVector<char, 1> Patch = {static_cast<char>(LastByte ^ 1)};
102 BuildIDSection->addPatch(Offset: *BuildIDOffset + BuildID.size() - 1, Bytes: Patch);
103 BC.outs() << "BOLT-INFO: patched build-id (flipped last bit)\n";
104
105 return Error::success();
106}
107} // namespace
108
109std::unique_ptr<MetadataRewriter>
110llvm::bolt::createBuildIDRewriter(BinaryContext &BC) {
111 return std::make_unique<BuildIDRewriter>(args: "build-id-rewriter", args&: BC);
112}
113

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of bolt/lib/Rewrite/BuildIDRewriter.cpp