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