1 //===- bolt/Rewrite/BuildIDRewriter.cpp -----------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Read and update build ID stored in ELF note section.
11 //===----------------------------------------------------------------------===//
13 #include "bolt/Rewrite/MetadataRewriter.h"
14 #include "bolt/Rewrite/MetadataRewriters.h"
15 #include "llvm/Support/Errc.h"
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
) {
26 raw_string_ostream
OS(Str
);
27 for (const char &Char
: BuildID
)
28 OS
<< format("%.2x", static_cast<unsigned char>(Char
));
33 class BuildIDRewriter final
: public MetadataRewriter
{
35 /// Information about binary build ID.
36 ErrorOr
<BinarySection
&> BuildIDSection
{std::errc::bad_address
};
38 std::optional
<uint64_t> BuildIDOffset
;
39 std::optional
<uint64_t> BuildIDSize
;
42 BuildIDRewriter(StringRef Name
, BinaryContext
&BC
)
43 : MetadataRewriter(Name
, BC
) {}
45 Error
sectionInitializer() override
;
47 Error
postEmitFinalizer() override
;
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())
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
);
68 NameSz
? Buf
.slice(Cursor
.tell(), Cursor
.tell() + NameSz
) : "<empty>";
69 Cursor
.seek(alignTo(Cursor
.tell() + NameSz
, 4));
71 const uint64_t DescOffset
= Cursor
.tell();
73 DescSz
? Buf
.slice(DescOffset
, DescOffset
+ DescSz
) : "<empty>";
74 Cursor
.seek(alignTo(DescOffset
+ DescSz
, 4));
77 return createStringError(errc::executable_format_error
,
78 "out of bounds while reading note section: %s",
79 toString(Cursor
.takeError()).c_str());
81 if (Type
== ELF::NT_GNU_BUILD_ID
&& Name
.substr(0, 3) == "GNU" &&
83 BuildIDSection
= NoteSection
;
85 BC
.setFileBuildID(getPrintableBuildID(Desc
));
86 BuildIDOffset
= DescOffset
;
89 return Error::success();
94 return Error::success();
97 Error
BuildIDRewriter::postEmitFinalizer() {
98 if (!BuildIDSection
|| !BuildIDOffset
)
99 return Error::success();
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";
106 return Error::success();
110 std::unique_ptr
<MetadataRewriter
>
111 llvm::bolt::createBuildIDRewriter(BinaryContext
&BC
) {
112 return std::make_unique
<BuildIDRewriter
>("build-id-rewriter", BC
);