-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[BOLT] Support runtime library hook via DT_INIT_ARRAY #167467
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
453d3a5
4bb0ac0
9ec352a
ec1ea84
9e3d027
d941a64
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -294,6 +294,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 .init_array entry | ||
| }; | ||
|
|
||
| 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::Hidden, 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 .init_array entry")), | ||
| cl::ZeroOrMore, cl::cat(BoltOptCategory)); | ||
|
|
||
| } // namespace opts | ||
|
|
||
| // FIXME: implement a better way to mark sections for replacement. | ||
|
|
@@ -741,9 +763,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(); | ||
|
|
||
|
|
@@ -785,8 +810,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"; | ||
|
|
@@ -1411,6 +1440,65 @@ 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 (InitArraySection->getAddress() != *BC->InitArrayAddress) { | ||
| return createStringError(std::errc::not_supported, | ||
| "Inconsistent address of .init_array section"); | ||
| } | ||
|
|
||
| if (const Relocation *Reloc = InitArraySection->getDynamicRelocationAt(0)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should allow
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will probably require saving the offset in the init_array/fini_array used for hooking into the extra BinaryContext variable. Additionally, I am not sure how to generate such a binary, except perhaps by manually removing relocations for first init_array/fini_array entry in ELF and then using obj2yaml/yaml2obj. Any suggestions on this would be greatly appreciated.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. While in theory,
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What kind of additional explicit validation you have in mind?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Check for
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. Added as extra commit, because same check should be present for fini_array for consistency. |
||
| 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)) { | ||
maksfb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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) { | ||
|
|
@@ -1434,6 +1522,11 @@ Error RewriteInstance::discoverRtFiniAddress() { | |
| if (auto EC = FiniArraySection.getError()) | ||
| return errorCodeToError(EC); | ||
|
|
||
| if (FiniArraySection->getAddress() != *BC->FiniArrayAddress) { | ||
| return createStringError(std::errc::not_supported, | ||
| "Inconsistent address of .fini_array section"); | ||
| } | ||
|
|
||
| if (const Relocation *Reloc = FiniArraySection->getDynamicRelocationAt(0)) { | ||
| BC->FiniFunctionAddress = Reloc->Addend; | ||
| return Error::success(); | ||
|
|
@@ -1448,26 +1541,95 @@ 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)) { | ||
maksfb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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}); | ||
| BC->outs() | ||
| << "BOLT-INFO: runtime library initialization was hooked via .init_array " | ||
| "entry, set to 0x" | ||
| << 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"); | ||
| 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); | ||
| } | ||
|
|
@@ -1480,6 +1642,10 @@ void RewriteInstance::updateRtFiniReloc() { | |
| FiniArraySection->addPendingRelocation(Relocation{ | ||
| /*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ Relocation::getAbs64(), | ||
| /*Addend*/ RT->getRuntimeFiniAddress(), /*Value*/ 0}); | ||
| BC->outs() << "BOLT-INFO: runtime library finalization was hooked via " | ||
| ".fini_array entry, set to 0x" | ||
| << Twine::utohexstr(RT->getRuntimeFiniAddress()) << "\n"; | ||
| return Error::success(); | ||
| } | ||
|
|
||
| void RewriteInstance::registerFragments() { | ||
|
|
@@ -2178,6 +2344,14 @@ void RewriteInstance::adjustCommandLineOptions() { | |
| exit(1); | ||
| } | ||
|
|
||
| if (opts::Instrument && opts::RuntimeLibInitHook == opts::RLIH_ENTRY_POINT && | ||
| !BC->HasInterpHeader) { | ||
| BC->errs() | ||
| << "BOLT-WARNING: adjusted runtime-lib-init-hook to 'init' due to " | ||
| "absence of INTERP header\n"; | ||
| opts::RuntimeLibInitHook = opts::RLIH_INIT; | ||
| } | ||
|
|
||
| if (opts::HotText && opts::HotTextMoveSections.getNumOccurrences() == 0) { | ||
| opts::HotTextMoveSections.addValue(".stub"); | ||
| opts::HotTextMoveSections.addValue(".mover"); | ||
|
|
@@ -4849,9 +5023,14 @@ 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) { | ||
vleonen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| NewEhdr.e_entry = RtLibrary->getRuntimeStartAddress(); | ||
| else | ||
| BC->outs() | ||
| << "BOLT-INFO: runtime library initialization was hooked via ELF " | ||
| "Header Entry Point, set to 0x" | ||
| << 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"); | ||
|
|
@@ -5692,14 +5871,23 @@ 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; | ||
| BC->outs() | ||
| << "BOLT-INFO: runtime library finalization was hooked via " | ||
| "DT_FINI, set to 0x" | ||
| << 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; | ||
| BC->outs() | ||
| << "BOLT-INFO: runtime library initialization was hooked via " | ||
| "DT_INIT, set to 0x" | ||
| << Twine::utohexstr(Addr) << "\n"; | ||
| } | ||
| } | ||
| break; | ||
|
|
@@ -5767,10 +5955,13 @@ Error RewriteInstance::readELFDynamic(ELFObjectFile<ELFT> *File) { | |
| for (const Elf_Dyn &Dyn : DynamicEntries) { | ||
| switch (Dyn.d_tag) { | ||
| case ELF::DT_INIT: | ||
| if (!BC->HasInterpHeader) { | ||
| 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(); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.