113 lines · cpp
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-exception6//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 in23/// 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("%.2x", 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. However,52 // a linker script can change the section name and such is the case with53 // 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.starts_with("GNU") && DescSz) {82 BuildIDSection = NoteSection;83 BuildID = Desc;84 BC.setFileBuildID(getPrintableBuildID(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(*BuildIDOffset + BuildID.size() - 1, Patch);103 BC.outs() << "BOLT-INFO: patched build-id (flipped last bit)\n";104 105 return Error::success();106}107} // namespace108 109std::unique_ptr<MetadataRewriter>110llvm::bolt::createBuildIDRewriter(BinaryContext &BC) {111 return std::make_unique<BuildIDRewriter>("build-id-rewriter", BC);112}113