xref: /llvm-project/bolt/lib/Rewrite/BuildIDRewriter.cpp (revision 8ea59ec6077e85c457b27b406a679ab9d5827387)
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.
getPrintableBuildID(StringRef BuildID)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("%.2x", 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:
BuildIDRewriter(StringRef Name,BinaryContext & BC)42   BuildIDRewriter(StringRef Name, BinaryContext &BC)
43       : MetadataRewriter(Name, BC) {}
44 
45   Error sectionInitializer() override;
46 
47   Error postEmitFinalizer() override;
48 };
49 
sectionInitializer()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(Cursor)) {
63       const uint32_t NameSz = DE.getU32(Cursor);
64       const uint32_t DescSz = DE.getU32(Cursor);
65       const uint32_t Type = DE.getU32(Cursor);
66 
67       StringRef Name =
68           NameSz ? Buf.slice(Cursor.tell(), Cursor.tell() + NameSz) : "<empty>";
69       Cursor.seek(alignTo(Cursor.tell() + NameSz, 4));
70 
71       const uint64_t DescOffset = Cursor.tell();
72       StringRef Desc =
73           DescSz ? Buf.slice(DescOffset, DescOffset + DescSz) : "<empty>";
74       Cursor.seek(alignTo(DescOffset + DescSz, 4));
75 
76       if (!Cursor)
77         return createStringError(errc::executable_format_error,
78                                  "out of bounds while reading note section: %s",
79                                  toString(Cursor.takeError()).c_str());
80 
81       if (Type == ELF::NT_GNU_BUILD_ID && Name.substr(0, 3) == "GNU" &&
82           DescSz) {
83         BuildIDSection = NoteSection;
84         BuildID = Desc;
85         BC.setFileBuildID(getPrintableBuildID(Desc));
86         BuildIDOffset = DescOffset;
87         BuildIDSize = DescSz;
88 
89         return Error::success();
90       }
91     }
92   }
93 
94   return Error::success();
95 }
96 
postEmitFinalizer()97 Error BuildIDRewriter::postEmitFinalizer() {
98   if (!BuildIDSection || !BuildIDOffset)
99     return Error::success();
100 
101   const uint8_t LastByte = BuildID[BuildID.size() - 1];
102   SmallVector<char, 1> Patch = {static_cast<char>(LastByte ^ 1)};
103   BuildIDSection->addPatch(*BuildIDOffset + BuildID.size() - 1, Patch);
104   BC.outs() << "BOLT-INFO: patched build-id (flipped last bit)\n";
105 
106   return Error::success();
107 }
108 } // namespace
109 
110 std::unique_ptr<MetadataRewriter>
createBuildIDRewriter(BinaryContext & BC)111 llvm::bolt::createBuildIDRewriter(BinaryContext &BC) {
112   return std::make_unique<BuildIDRewriter>("build-id-rewriter", BC);
113 }
114