Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions bolt/docs/CommandLineArgumentReference.md
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,15 @@

Specify file name of the runtime instrumentation library

- `--runtime-lib-init-hook=<value>`

Primary target for hooking runtime library initialization, used in
fallback order of availabiliy in input binary (entry_point -> init
-> init_array) (default: entry_point)
- `entry_point`: use ELF Header Entry Point
- `init`: use ELF DT_INIT entry
- `init_array`: use ELF 1st entry of .init_array

- `--sctc-mode=<value>`

Mode for simplify conditional tail calls
Expand Down
9 changes: 9 additions & 0 deletions bolt/include/bolt/Core/BinaryContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -807,6 +807,15 @@ class BinaryContext {
/// the execution of the binary is completed.
std::optional<uint64_t> FiniFunctionAddress;

/// DT_INIT.
std::optional<uint64_t> InitAddress;

/// DT_INIT_ARRAY. Only used when DT_INIT is not set.
std::optional<uint64_t> InitArrayAddress;

/// DT_INIT_ARRAYSZ. Only used when DT_INIT is not set.
std::optional<uint64_t> InitArraySize;

/// DT_FINI.
std::optional<uint64_t> FiniAddress;

Expand Down
11 changes: 10 additions & 1 deletion bolt/include/bolt/Rewrite/RewriteInstance.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,23 @@ class RewriteInstance {
/// section allocations if found.
void discoverBOLTReserved();

/// Check whether we should use DT_INIT or DT_INIT_ARRAY for instrumentation.
/// DT_INIT is preferred; DT_INIT_ARRAY is only used when no DT_INIT entry was
/// found.
Error discoverRtInitAddress();

/// Check whether we should use DT_FINI or DT_FINI_ARRAY for instrumentation.
/// DT_FINI is preferred; DT_FINI_ARRAY is only used when no DT_FINI entry was
/// found.
Error discoverRtFiniAddress();

/// If DT_INIT_ARRAY is used for instrumentation, update the relocation of its
/// first entry to point to the instrumentation library's init address.
Error updateRtInitReloc();

/// If DT_FINI_ARRAY is used for instrumentation, update the relocation of its
/// first entry to point to the instrumentation library's fini address.
void updateRtFiniReloc();
Error updateRtFiniReloc();

/// Create and initialize metadata rewriters for this instance.
void initializeMetadataManager();
Expand Down
219 changes: 202 additions & 17 deletions bolt/lib/Rewrite/RewriteInstance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ namespace opts {
extern cl::list<std::string> HotTextMoveSections;
extern cl::opt<bool> Hugify;
extern cl::opt<bool> Instrument;
extern cl::opt<uint32_t> InstrumentationSleepTime;
extern cl::opt<bool> KeepNops;
extern cl::opt<bool> Lite;
extern cl::list<std::string> PrintOnly;
Expand Down Expand Up @@ -294,6 +295,28 @@ cl::bits<GadgetScannerKind> GadgetScannersToRun(
clEnumValN(GS_ALL, "all", "All implemented scanners")),
cl::ZeroOrMore, cl::CommaSeparated, cl::cat(BinaryAnalysisCategory));

// Primary targets for hooking runtime library initialization hooking
// with fallback to next item in case if current item is not available
// in the input binary.
enum RuntimeLibInitHookTarget : char {
RLIH_ENTRY_POINT = 0, /// Use ELF Header Entry Point
RLIH_INIT = 1, /// Use ELF DT_INIT entry
RLIH_INIT_ARRAY = 2, /// Use ELF 1st entry of .init_array
};

cl::opt<RuntimeLibInitHookTarget> RuntimeLibInitHook(
"runtime-lib-init-hook",
cl::desc("Primary target for hooking runtime library initialization, used "
"in fallback order of availabiliy in input binary (entry_point -> "
"init -> init_array) (default: entry_point)"),
cl::init(RLIH_ENTRY_POINT),
cl::values(clEnumValN(RLIH_ENTRY_POINT, "entry_point",
"use ELF Header Entry Point"),
clEnumValN(RLIH_INIT, "init", "use ELF DT_INIT entry"),
clEnumValN(RLIH_INIT_ARRAY, "init_array",
"use ELF 1st entry of .init_array")),
cl::ZeroOrMore, cl::cat(BoltOptCategory));

} // namespace opts

// FIXME: implement a better way to mark sections for replacement.
Expand Down Expand Up @@ -741,9 +764,12 @@ Error RewriteInstance::run() {
adjustCommandLineOptions();
discoverFileObjects();

if (opts::Instrument && !BC->IsStaticExecutable)
if (opts::Instrument && !BC->IsStaticExecutable) {
if (Error E = discoverRtInitAddress())
return E;
if (Error E = discoverRtFiniAddress())
return E;
}

preprocessProfileData();

Expand Down Expand Up @@ -785,8 +811,12 @@ Error RewriteInstance::run() {

updateMetadata();

if (opts::Instrument && !BC->IsStaticExecutable)
updateRtFiniReloc();
if (opts::Instrument && !BC->IsStaticExecutable) {
if (Error E = updateRtInitReloc())
return E;
if (Error E = updateRtFiniReloc())
return E;
}

if (opts::OutputFilename == "/dev/null") {
BC->outs() << "BOLT-INFO: skipping writing final binary to disk\n";
Expand Down Expand Up @@ -1411,6 +1441,60 @@ void RewriteInstance::discoverBOLTReserved() {
NextAvailableAddress = BC->BOLTReserved.start();
}

Error RewriteInstance::discoverRtInitAddress() {
if (BC->HasInterpHeader && opts::RuntimeLibInitHook == opts::RLIH_ENTRY_POINT)
return Error::success();

// Use DT_INIT if it's available.
if (BC->InitAddress && opts::RuntimeLibInitHook <= opts::RLIH_INIT) {
BC->StartFunctionAddress = BC->InitAddress;
return Error::success();
}

if (!BC->InitArrayAddress || !BC->InitArraySize) {
return createStringError(std::errc::not_supported,
"Instrumentation of shared library needs either "
"DT_INIT or DT_INIT_ARRAY");
}

if (*BC->InitArraySize < BC->AsmInfo->getCodePointerSize()) {
return createStringError(std::errc::not_supported,
"Need at least 1 DT_INIT_ARRAY slot");
}

ErrorOr<BinarySection &> InitArraySection =
BC->getSectionForAddress(*BC->InitArrayAddress);
if (auto EC = InitArraySection.getError())
return errorCodeToError(EC);

if (const Relocation *Reloc = InitArraySection->getDynamicRelocationAt(0)) {
if (Reloc->isRelative()) {
BC->StartFunctionAddress = Reloc->Addend;
} else {
MCSymbol *Sym = Reloc->Symbol;
if (!Sym)
return createStringError(
std::errc::not_supported,
"Failed to locate symbol for 0 entry of .init_array");
const BinaryFunction *BF = BC->getFunctionForSymbol(Sym);
if (!BF)
return createStringError(
std::errc::not_supported,
"Failed to locate binary function for 0 entry of .init_array");
BC->StartFunctionAddress = BF->getAddress() + Reloc->Addend;
}
return Error::success();
}

if (const Relocation *Reloc = InitArraySection->getRelocationAt(0)) {
BC->StartFunctionAddress = Reloc->Value;
return Error::success();
}

return createStringError(std::errc::not_supported,
"No relocation for first DT_INIT_ARRAY slot");
}

Error RewriteInstance::discoverRtFiniAddress() {
// Use DT_FINI if it's available.
if (BC->FiniAddress) {
Expand All @@ -1419,6 +1503,10 @@ Error RewriteInstance::discoverRtFiniAddress() {
}

if (!BC->FiniArrayAddress || !BC->FiniArraySize) {
// It is still possible to generate profile without fini hook if
// InstrumentationSleepTime is set
if (opts::InstrumentationSleepTime > 0)
return Error::success();
return createStringError(
std::errc::not_supported,
"Instrumentation needs either DT_FINI or DT_FINI_ARRAY");
Expand Down Expand Up @@ -1448,26 +1536,101 @@ Error RewriteInstance::discoverRtFiniAddress() {
"No relocation for first DT_FINI_ARRAY slot");
}

void RewriteInstance::updateRtFiniReloc() {
Error RewriteInstance::updateRtInitReloc() {
if (BC->HasInterpHeader && opts::RuntimeLibInitHook == opts::RLIH_ENTRY_POINT)
return Error::success();

// Updating DT_INIT is handled by patchELFDynamic.
if (BC->InitAddress && opts::RuntimeLibInitHook <= opts::RLIH_INIT)
return Error::success();

const RuntimeLibrary *RT = BC->getRuntimeLibrary();
if (!RT || !RT->getRuntimeStartAddress())
return Error::success();

if (!BC->InitArrayAddress)
return Error::success();

if (!BC->InitArrayAddress || !BC->InitArraySize)
return createStringError(std::errc::not_supported,
"inconsistent .init_array state");

ErrorOr<BinarySection &> InitArraySection =
BC->getSectionForAddress(*BC->InitArrayAddress);
if (!InitArraySection)
return createStringError(std::errc::not_supported, ".init_array removed");

if (std::optional<Relocation> Reloc =
InitArraySection->takeDynamicRelocationAt(0)) {
if (Reloc->isRelative()) {
if (Reloc->Addend != BC->StartFunctionAddress)
return createStringError(std::errc::not_supported,
"inconsistent .init_array dynamic relocation");
Reloc->Addend = RT->getRuntimeStartAddress();
InitArraySection->addDynamicRelocation(*Reloc);
} else {
MCSymbol *Sym = Reloc->Symbol;
if (!Sym)
return createStringError(
std::errc::not_supported,
"Failed to locate symbol for 0 entry of .init_array");
const BinaryFunction *BF = BC->getFunctionForSymbol(Sym);
if (!BF)
return createStringError(
std::errc::not_supported,
"Failed to locate binary function for 0 entry of .init_array");
if (BF->getAddress() + Reloc->Addend != BC->StartFunctionAddress)
return createStringError(std::errc::not_supported,
"inconsistent .init_array dynamic relocation");
InitArraySection->addDynamicRelocation(Relocation{
/*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ Relocation::getAbs64(),
/*Addend*/ RT->getRuntimeStartAddress(), /*Value*/ 0});
}
}
// Update the static relocation by adding a pending relocation which will get
// patched when flushPendingRelocations is called in rewriteFile. Note that
// flushPendingRelocations will calculate the value to patch as
// "Symbol + Addend". Since we don't have a symbol, just set the addend to the
// desired value.
InitArraySection->addPendingRelocation(Relocation{
/*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ Relocation::getAbs64(),
/*Addend*/ RT->getRuntimeStartAddress(), /*Value*/ 0});
outs() << "BOLT-INFO: Runtime library initialization was hooked via 1st "
"entry of .init_array, set to "
<< Twine::utohexstr(RT->getRuntimeStartAddress()) << "\n";
return Error::success();
}

Error RewriteInstance::updateRtFiniReloc() {
// Updating DT_FINI is handled by patchELFDynamic.
if (BC->FiniAddress)
return;
return Error::success();

const RuntimeLibrary *RT = BC->getRuntimeLibrary();
if (!RT || !RT->getRuntimeFiniAddress())
return;
return Error::success();

assert(BC->FiniArrayAddress && BC->FiniArraySize &&
"inconsistent .fini_array state");
// It is still possible to generate profile without fini hook if
// InstrumentationSleepTime is set
if ((!BC->FiniArrayAddress || !BC->FiniArraySize) &&
opts::InstrumentationSleepTime > 0) {
return Error::success();
}

if (!BC->FiniArrayAddress || !BC->FiniArraySize)
return createStringError(std::errc::not_supported,
"inconsistent .fini_array state");

ErrorOr<BinarySection &> FiniArraySection =
BC->getSectionForAddress(*BC->FiniArrayAddress);
assert(FiniArraySection && ".fini_array removed");
if (!FiniArraySection)
return createStringError(std::errc::not_supported, ".fini_array removed");

if (std::optional<Relocation> Reloc =
FiniArraySection->takeDynamicRelocationAt(0)) {
assert(Reloc->Addend == BC->FiniFunctionAddress &&
"inconsistent .fini_array dynamic relocation");
if (Reloc->Addend != BC->FiniFunctionAddress)
return createStringError(std::errc::not_supported,
"inconsistent .fini_array dynamic relocation");
Reloc->Addend = RT->getRuntimeFiniAddress();
FiniArraySection->addDynamicRelocation(*Reloc);
}
Expand All @@ -1480,6 +1643,10 @@ void RewriteInstance::updateRtFiniReloc() {
FiniArraySection->addPendingRelocation(Relocation{
/*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ Relocation::getAbs64(),
/*Addend*/ RT->getRuntimeFiniAddress(), /*Value*/ 0});
outs() << "BOLT-INFO: Runtime library finalization was hooked via 1st entry "
"of .fini_array, set to "
<< Twine::utohexstr(RT->getRuntimeFiniAddress()) << "\n";
return Error::success();
}

void RewriteInstance::registerFragments() {
Expand Down Expand Up @@ -4849,9 +5016,13 @@ void RewriteInstance::patchELFSectionHeaderTable(ELFObjectFile<ELFT> *File) {
ELFEhdrTy NewEhdr = Obj.getHeader();

if (BC->HasRelocations) {
if (RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary())
RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary();
if (RtLibrary && opts::RuntimeLibInitHook == opts::RLIH_ENTRY_POINT) {
NewEhdr.e_entry = RtLibrary->getRuntimeStartAddress();
else
outs() << "BOLT-INFO: Runtime library initialization was hooked via ELF "
"Header Entry Point, set to "
<< Twine::utohexstr(NewEhdr.e_entry) << "\n";
} else
NewEhdr.e_entry = getNewFunctionAddress(NewEhdr.e_entry);
assert((NewEhdr.e_entry || !Obj.getHeader().e_entry) &&
"cannot find new address for entry point");
Expand Down Expand Up @@ -5692,14 +5863,21 @@ void RewriteInstance::patchELFDynamic(ELFObjectFile<ELFT> *File) {
}
RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary();
if (RtLibrary && Dyn.getTag() == ELF::DT_FINI) {
if (uint64_t Addr = RtLibrary->getRuntimeFiniAddress())
if (uint64_t Addr = RtLibrary->getRuntimeFiniAddress()) {
NewDE.d_un.d_ptr = Addr;
outs() << "BOLT-INFO: Runtime library finalization was hooked via "
"DT_FINI, set to "
<< Twine::utohexstr(Addr) << "\n";
}
}
if (RtLibrary && Dyn.getTag() == ELF::DT_INIT && !BC->HasInterpHeader) {
if (RtLibrary && Dyn.getTag() == ELF::DT_INIT &&
(!BC->HasInterpHeader ||
opts::RuntimeLibInitHook == opts::RLIH_INIT)) {
if (auto Addr = RtLibrary->getRuntimeStartAddress()) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Set DT_INIT to 0x"
<< Twine::utohexstr(Addr) << '\n');
NewDE.d_un.d_ptr = Addr;
outs() << "BOLT-INFO: Runtime library initialization was hooked via "
"DT_INIT, set to "
<< Twine::utohexstr(Addr) << "\n";
}
}
break;
Expand Down Expand Up @@ -5771,6 +5949,13 @@ Error RewriteInstance::readELFDynamic(ELFObjectFile<ELFT> *File) {
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Set start function address\n");
BC->StartFunctionAddress = Dyn.getPtr();
}
BC->InitAddress = Dyn.getPtr();
break;
case ELF::DT_INIT_ARRAY:
BC->InitArrayAddress = Dyn.getPtr();
break;
case ELF::DT_INIT_ARRAYSZ:
BC->InitArraySize = Dyn.getPtr();
break;
case ELF::DT_FINI:
BC->FiniAddress = Dyn.getPtr();
Expand Down
Loading
Loading