[BOLT] Add update-build-id option, on by default

Summary:
The build-id is used by tools to uniquely identify binaries. Update
the output binary build-id with a different number to make it
distinguishable from the input binary. This implementation just flips
the last build-id bit.

(cherry picked from FBD9235336)
This commit is contained in:
Rafael Auler
2018-08-08 17:55:24 -07:00
committed by Maksim Panchenko
parent 510a8c4bbe
commit 9c4fcafa37
3 changed files with 135 additions and 102 deletions

View File

@@ -172,7 +172,7 @@ ProfileWriter::writeProfile(const RewriteInstance &RI) {
BP.Header.Version = 1;
auto FileName = RI.getInputFileName();
BP.Header.FileName = FileName ? *FileName : "<unknown>";
auto BuildID = RI.getBuildID();
auto BuildID = RI.getPrintableBuildID();
BP.Header.Id = BuildID ? *BuildID : "<unknown>";
if (RI.getDataAggregator().started()) {

View File

@@ -358,13 +358,6 @@ Verbosity("v",
cl::ZeroOrMore,
cl::cat(BoltCategory));
static cl::opt<bool>
AddBoltInfo("add-bolt-info",
cl::desc("add BOLT version and command line argument information to "
"processed binaries"),
cl::init(true),
cl::cat(BoltCategory));
cl::opt<bool>
AggregateOnly("aggregate-only",
cl::desc("exit after writing aggregated data file"),
@@ -908,58 +901,77 @@ void RewriteInstance::discoverStorage() {
BC->LayoutStartAddress = NextAvailableAddress;
}
Optional<std::string>
RewriteInstance::getBuildID() const {
for (auto &Section : InputFile->sections()) {
StringRef SectionName;
Section.getName(SectionName);
void RewriteInstance::parseBuildID() {
if (!BuildIDSection)
return;
if (SectionName != ".note.gnu.build-id")
continue;
StringRef Buf = BuildIDSection->getContents();
StringRef SectionContents;
Section.getContents(SectionContents);
// Reading notes section (see Portable Formats Specification, Version 1.1,
// pg 2-5, section "Note Section").
DataExtractor DE = DataExtractor(Buf, true, 8);
uint32_t Offset = 0;
if (!DE.isValidOffset(Offset))
return;
uint32_t NameSz = DE.getU32(&Offset);
if (!DE.isValidOffset(Offset))
return;
uint32_t DescSz = DE.getU32(&Offset);
if (!DE.isValidOffset(Offset))
return;
uint32_t Type = DE.getU32(&Offset);
// Reading notes section (see Portable Formats Specification, Version 1.1,
// pg 2-5, section "Note Section").
DataExtractor DE = DataExtractor(SectionContents, true, 8);
uint32_t Offset = 0;
if (!DE.isValidOffset(Offset))
return NoneType();
uint32_t NameSz = DE.getU32(&Offset);
if (!DE.isValidOffset(Offset))
return NoneType();
uint32_t DescSz = DE.getU32(&Offset);
if (!DE.isValidOffset(Offset))
return NoneType();
uint32_t Type = DE.getU32(&Offset);
DEBUG(dbgs() << "NameSz = " << NameSz << "; DescSz = " << DescSz
<< "; Type = " << Type << "\n");
DEBUG(dbgs() << "NameSz = " << NameSz << "; DescSz = " << DescSz
<< "; Type = " << Type << "\n");
// Type 3 is a GNU build-id note section
if (Type != 3)
return;
// Type 3 is a GNU build-id note section
if (Type != 3)
return NoneType();
StringRef Name = Buf.slice(Offset, Offset + NameSz);
Offset = alignTo(Offset + NameSz, 4);
if (Name.substr(0, 3) != "GNU")
return;
StringRef Name = SectionContents.slice(Offset, Offset + NameSz);
Offset = alignTo(Offset + NameSz, 4);
StringRef BinaryBuildID = SectionContents.slice(Offset, Offset + DescSz);
if (Name.substr(0, 3) != "GNU")
return NoneType();
BuildID = Buf.slice(Offset, Offset + DescSz);
}
std::string Str;
raw_string_ostream OS(Str);
auto CharIter = BinaryBuildID.bytes_begin();
while (CharIter != BinaryBuildID.bytes_end()) {
if (*CharIter < 0x10)
OS << "0";
OS << Twine::utohexstr(*CharIter);
++CharIter;
}
outs() << "BOLT-INFO: binary build-id is: " << OS.str() << "\n";
return OS.str();
Optional<std::string> RewriteInstance::getPrintableBuildID() const {
if (BuildID.empty())
return NoneType();
std::string Str;
raw_string_ostream OS(Str);
auto CharIter = BuildID.bytes_begin();
while (CharIter != BuildID.bytes_end()) {
if (*CharIter < 0x10)
OS << "0";
OS << Twine::utohexstr(*CharIter);
++CharIter;
}
return NoneType();
return OS.str();
}
void RewriteInstance::patchBuildID() {
auto &OS = Out->os();
if (BuildID.empty())
return;
size_t IDOffset = BuildIDSection->getContents().rfind(BuildID);
assert(IDOffset != StringRef::npos && "failed to patch build-id");
auto FileOffset = getFileOffsetForAddress(BuildIDSection->getAddress());
if (!FileOffset) {
errs() << "BOLT-WARNING: Non-allocatable build-id will not be updated.\n";
return;
}
char LastIDByte = BuildID[BuildID.size() - 1];
LastIDByte ^= 1;
OS.pwrite(&LastIDByte, 1, FileOffset + IDOffset + BuildID.size() - 1);
outs() << "BOLT-INFO: patched build-id (flipped last bit)\n";
}
void RewriteInstance::run() {
@@ -995,15 +1007,6 @@ void RewriteInstance::run() {
(llvm::Triple::ArchType)InputFile->getArch())
<< "\n";
if (DA.started()) {
if (auto FileBuildID = getBuildID()) {
DA.processFileBuildID(*FileBuildID);
} else {
errs() << "BOLT-WARNING: build-id will not be checked because we could "
"not read one from input binary\n";
}
}
unsigned PassNumber = 1;
executeRewritePass({});
if (opts::AggregateOnly || opts::DiffOnly)
@@ -1738,6 +1741,7 @@ void RewriteInstance::readSpecialSections() {
GOTPLTSection = BC->getUniqueSectionByName(".got.plt");
PLTGOTSection = BC->getUniqueSectionByName(".plt.got");
RelaPLTSection = BC->getUniqueSectionByName(".rela.plt");
BuildIDSection = BC->getUniqueSectionByName(".note.gnu.build-id");
if (opts::PrintSections) {
outs() << "BOLT-INFO: Sections from original binary:\n";
@@ -1768,6 +1772,18 @@ void RewriteInstance::readSpecialSections() {
EHFrame->dump(outs(), &*BC->MRI, NoneType());
}
CFIRdWrt.reset(new CFIReaderWriter(*EHFrame));
// Parse build-id
parseBuildID();
if (DA.started()) {
if (auto FileBuildID = getPrintableBuildID()) {
outs() << "BOLT-INFO: binary build-id is: " << *FileBuildID << "\n";
DA.processFileBuildID(*FileBuildID);
} else {
errs() << "BOLT-WARNING: build-id will not be checked because we could "
"not read one from input binary\n";
}
}
}
void RewriteInstance::adjustCommandLineOptions() {
@@ -1846,7 +1862,6 @@ bool RewriteInstance::analyzeRelocation(const RelocationRef &Rel,
return false;
const bool IsAArch64 = BC->isAArch64();
const bool IsFromCode = RelocatedSection.isText();
// For value extraction.
StringRef RelocatedSectionContents;
@@ -3601,45 +3616,46 @@ void RewriteInstance::finalizeSectionStringTable(ELFObjectFile<ELFT> *File) {
ELF::SHT_STRTAB);
}
void RewriteInstance::addBoltInfoSection() {
if (opts::AddBoltInfo) {
std::string DescStr;
raw_string_ostream DescOS(DescStr);
namespace {
DescOS << "BOLT revision: " << BoltRevision << ", " << "command line:";
for (auto I = 0; I < Argc; ++I) {
DescOS << " " << Argv[I];
}
DescOS.flush();
std::string Str;
raw_string_ostream OS(Str);
std::string NameStr = "GNU";
const uint32_t NameSz = NameStr.size() + 1;
const uint32_t DescSz = DescStr.size();
const uint32_t Type = 4; // NT_GNU_GOLD_VERSION (gold version)
OS.write(reinterpret_cast<const char*>(&(NameSz)), 4);
OS.write(reinterpret_cast<const char*>(&(DescSz)), 4);
OS.write(reinterpret_cast<const char*>(&(Type)), 4);
OS << NameStr;
for (uint64_t I = NameStr.size();
I < alignTo(NameStr.size(), 4); ++I) {
OS << '\0';
}
OS << DescStr;
for (uint64_t I = DescStr.size();
I < alignTo(DescStr.size(), 4); ++I) {
OS << '\0';
}
const auto BoltInfo = OS.str();
BC->registerOrUpdateNoteSection(".note.bolt_info",
copyByteArray(BoltInfo),
BoltInfo.size(),
/*Alignment=*/1,
/*IsReadOnly=*/true,
ELF::SHT_NOTE);
std::string encodeELFNote(StringRef NameStr, StringRef DescStr, uint32_t Type) {
std::string Str;
raw_string_ostream OS(Str);
const uint32_t NameSz = NameStr.size() + 1;
const uint32_t DescSz = DescStr.size();
OS.write(reinterpret_cast<const char *>(&(NameSz)), 4);
OS.write(reinterpret_cast<const char *>(&(DescSz)), 4);
OS.write(reinterpret_cast<const char *>(&(Type)), 4);
OS << NameStr;
for (uint64_t I = NameStr.size(); I < alignTo(NameStr.size(), 4); ++I) {
OS << '\0';
}
OS << DescStr;
for (uint64_t I = DescStr.size(); I < alignTo(DescStr.size(), 4); ++I) {
OS << '\0';
}
return OS.str();
}
}
void RewriteInstance::addBoltInfoSection() {
std::string DescStr;
raw_string_ostream DescOS(DescStr);
DescOS << "BOLT revision: " << BoltRevision << ", "
<< "command line:";
for (auto I = 0; I < Argc; ++I) {
DescOS << " " << Argv[I];
}
DescOS.flush();
const auto BoltInfo =
encodeELFNote("GNU", DescStr, 4 /*NT_GNU_GOLD_VERSION*/);
BC->registerOrUpdateNoteSection(".note.bolt_info", copyByteArray(BoltInfo),
BoltInfo.size(),
/*Alignment=*/1,
/*IsReadOnly=*/true, ELF::SHT_NOTE);
}
// Provide a mapping of the existing input binary sections to the output binary
@@ -4543,6 +4559,8 @@ void RewriteInstance::rewriteFile() {
// Update symbol tables.
patchELFSymTabs();
patchBuildID();
// Copy non-allocatable sections once allocatable part is finished.
rewriteNoteSections();

View File

@@ -326,6 +326,11 @@ private:
/// Add a notes section containing the BOLT revision and command line options.
void addBoltInfoSection();
/// Update the ELF note section containing the binary build-id to reflect
/// a new build-id, so tools can differentiate between the old and the
/// rewritten binary.
void patchBuildID();
/// Computes output .debug_line line table offsets for each compile unit,
/// and updates stmt_list for a corresponding compile unit.
void updateLineTableOffsets();
@@ -502,6 +507,12 @@ private:
/// .gdb_index section.
ErrorOr<BinarySection &> GdbIndexSection{std::errc::bad_address};
/// .note.gnu.build-id section.
ErrorOr<BinarySection &> BuildIDSection{std::errc::bad_address};
/// A reference to the build-id bytes in the original binary
StringRef BuildID;
uint64_t NewSymTabOffset{0};
/// Keep track of functions we fail to write in the binary. We need to avoid
@@ -554,9 +565,13 @@ public:
return NoneType();
}
/// Read binary sections and find a gnu note section with the build-id
/// of the input file.
Optional<std::string> getBuildID() const;
/// Set the build-id string if we did not fail to parse the contents of the
/// ELF note section containing build-id information.
void parseBuildID();
/// The build-id is typically a stream of 20 bytes. Return these bytes in
/// printable hexadecimal form if they are available, or NoneType otherwise.
Optional<std::string> getPrintableBuildID() const;
/// Provide an access to the profile data aggregator.
const DataAggregator &getDataAggregator() const {