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 | |
17 | using namespace llvm; |
18 | using namespace bolt; |
19 | |
20 | namespace { |
21 | |
22 | /// The build-id is typically a stream of 20 bytes. Return these bytes in |
23 | /// printable hexadecimal form. |
24 | std::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 | |
33 | class 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 | |
41 | public: |
42 | BuildIDRewriter(StringRef Name, BinaryContext &BC) |
43 | : MetadataRewriter(Name, BC) {} |
44 | |
45 | Error sectionInitializer() override; |
46 | |
47 | Error postEmitFinalizer() override; |
48 | }; |
49 | |
50 | Error 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 | |
96 | Error 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 | |
109 | std::unique_ptr<MetadataRewriter> |
110 | llvm::bolt::createBuildIDRewriter(BinaryContext &BC) { |
111 | return std::make_unique<BuildIDRewriter>(args: "build-id-rewriter" , args&: BC); |
112 | } |
113 | |