Skip to content

Commit 0c75301

Browse files
author
Vasily Leonenko
committed
[BOLT] Support runtime library hook via DT_INIT_ARRAY
This commit follows implementation of instrumentation hook via DT_FINI_ARRAY (#67348) and extends it for BOLT runtime libraries (including instrumentation library) initialization hooking. Initialization has has differences compared to finalization: - Executables always use ELF entry point address. Update code checks it and updates init_array entry if ELF is shared library (have no interp entry) and have no DT_INIT entry. Also this commit introduces "runtime-lib-init-hook" option to select primary initialization hook (entry_point, init, init_array) with fall back to next available hook in input binary. e.g. in case of libc we can explicitly set it to init_array. - Shared library init_array entries relocations usually has R_AARCH64_ABS64 type on AArch64 binaries. We check relocation type and adjust methods for reading init_array relocations in discovery and update methods.
1 parent d4b41b9 commit 0c75301

File tree

4 files changed

+185
-4
lines changed

4 files changed

+185
-4
lines changed

bolt/docs/CommandLineArgumentReference.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,15 @@
811811

812812
Specify file name of the runtime instrumentation library
813813

814+
- `--runtime-lib-init-hook=<value>`
815+
816+
Primary target for hooking runtime library initialization, used in
817+
fallback order of availabiliy in input binary (entry-point -> init
818+
-> init_array) (default: entry_point)
819+
- `entry_point`: use ELF Header Entry Point
820+
- `init`: use ELF DT_INIT entry
821+
- `init_array`: use ELF 1st entry of .init_array
822+
814823
- `--sctc-mode=<value>`
815824

816825
Mode for simplify conditional tail calls

bolt/include/bolt/Core/BinaryContext.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,15 @@ class BinaryContext {
807807
/// the execution of the binary is completed.
808808
std::optional<uint64_t> FiniFunctionAddress;
809809

810+
/// DT_INIT.
811+
std::optional<uint64_t> InitAddress;
812+
813+
/// DT_INIT_ARRAY. Only used when DT_INIT is not set.
814+
std::optional<uint64_t> InitArrayAddress;
815+
816+
/// DT_INIT_ARRAYSZ. Only used when DT_INIT is not set.
817+
std::optional<uint64_t> InitArraySize;
818+
810819
/// DT_FINI.
811820
std::optional<uint64_t> FiniAddress;
812821

bolt/include/bolt/Rewrite/RewriteInstance.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,20 @@ class RewriteInstance {
9393
/// section allocations if found.
9494
void discoverBOLTReserved();
9595

96+
/// Check whether we should use DT_INIT or DT_INIT_ARRAY for instrumentation.
97+
/// DT_INIT is preferred; DT_INIT_ARRAY is only used when no DT_INIT entry was
98+
/// found.
99+
Error discoverRtInitAddress();
100+
96101
/// Check whether we should use DT_FINI or DT_FINI_ARRAY for instrumentation.
97102
/// DT_FINI is preferred; DT_FINI_ARRAY is only used when no DT_FINI entry was
98103
/// found.
99104
Error discoverRtFiniAddress();
100105

106+
/// If DT_INIT_ARRAY is used for instrumentation, update the relocation of its
107+
/// first entry to point to the instrumentation library's init address.
108+
Error updateRtInitReloc();
109+
101110
/// If DT_FINI_ARRAY is used for instrumentation, update the relocation of its
102111
/// first entry to point to the instrumentation library's fini address.
103112
void updateRtFiniReloc();

bolt/lib/Rewrite/RewriteInstance.cpp

Lines changed: 158 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,28 @@ cl::bits<GadgetScannerKind> GadgetScannersToRun(
292292
clEnumValN(GS_ALL, "all", "All implemented scanners")),
293293
cl::ZeroOrMore, cl::CommaSeparated, cl::cat(BinaryAnalysisCategory));
294294

295+
// Primary targets for hooking runtime library initialization hooking
296+
// with fallback to next item in case if current item is not available
297+
// in the input binary.
298+
enum RuntimeLibInitHookTarget : char {
299+
RLIH_ENTRY_POINT = 0, /// Use ELF Header Entry Point
300+
RLIH_INIT = 1, /// Use ELF DT_INIT entry
301+
RLIH_INIT_ARRAY = 2, /// Use ELF 1st entry of .init_array
302+
};
303+
304+
cl::opt<RuntimeLibInitHookTarget> RuntimeLibInitHook(
305+
"runtime-lib-init-hook",
306+
cl::desc("Primary target for hooking runtime library initialization, used "
307+
"in fallback order of availabiliy in input binary (entry-point -> "
308+
"init -> init_array) (default: entry_point)"),
309+
cl::init(RLIH_ENTRY_POINT),
310+
cl::values(clEnumValN(RLIH_ENTRY_POINT, "entry_point",
311+
"use ELF Header Entry Point"),
312+
clEnumValN(RLIH_INIT, "init", "use ELF DT_INIT entry"),
313+
clEnumValN(RLIH_INIT_ARRAY, "init_array",
314+
"use ELF 1st entry of .init_array")),
315+
cl::ZeroOrMore, cl::cat(BoltOptCategory));
316+
295317
} // namespace opts
296318

297319
// FIXME: implement a better way to mark sections for replacement.
@@ -737,9 +759,12 @@ Error RewriteInstance::run() {
737759
adjustCommandLineOptions();
738760
discoverFileObjects();
739761

740-
if (opts::Instrument && !BC->IsStaticExecutable)
762+
if (opts::Instrument && !BC->IsStaticExecutable) {
763+
if (Error E = discoverRtInitAddress())
764+
return E;
741765
if (Error E = discoverRtFiniAddress())
742766
return E;
767+
}
743768

744769
preprocessProfileData();
745770

@@ -781,8 +806,11 @@ Error RewriteInstance::run() {
781806

782807
updateMetadata();
783808

784-
if (opts::Instrument && !BC->IsStaticExecutable)
809+
if (opts::Instrument && !BC->IsStaticExecutable) {
810+
if (Error E = updateRtInitReloc())
811+
return E;
785812
updateRtFiniReloc();
813+
}
786814

787815
if (opts::OutputFilename == "/dev/null") {
788816
BC->outs() << "BOLT-INFO: skipping writing final binary to disk\n";
@@ -1407,6 +1435,60 @@ void RewriteInstance::discoverBOLTReserved() {
14071435
NextAvailableAddress = BC->BOLTReserved.start();
14081436
}
14091437

1438+
Error RewriteInstance::discoverRtInitAddress() {
1439+
if (BC->HasInterpHeader && opts::RuntimeLibInitHook == opts::RLIH_ENTRY_POINT)
1440+
return Error::success();
1441+
1442+
// Use DT_INIT if it's available.
1443+
if (BC->InitAddress && opts::RuntimeLibInitHook <= opts::RLIH_INIT) {
1444+
BC->StartFunctionAddress = BC->InitAddress;
1445+
return Error::success();
1446+
}
1447+
1448+
if (!BC->InitArrayAddress || !BC->InitArraySize) {
1449+
return createStringError(std::errc::not_supported,
1450+
"Instrumentation of shared library needs either "
1451+
"DT_INIT or DT_INIT_ARRAY");
1452+
}
1453+
1454+
if (*BC->InitArraySize < BC->AsmInfo->getCodePointerSize()) {
1455+
return createStringError(std::errc::not_supported,
1456+
"Need at least 1 DT_INIT_ARRAY slot");
1457+
}
1458+
1459+
ErrorOr<BinarySection &> InitArraySection =
1460+
BC->getSectionForAddress(*BC->InitArrayAddress);
1461+
if (auto EC = InitArraySection.getError())
1462+
return errorCodeToError(EC);
1463+
1464+
if (const Relocation *Reloc = InitArraySection->getDynamicRelocationAt(0)) {
1465+
if (Reloc->isRelative()) {
1466+
BC->StartFunctionAddress = Reloc->Addend;
1467+
} else {
1468+
MCSymbol *Sym = Reloc->Symbol;
1469+
if (!Sym)
1470+
return createStringError(
1471+
std::errc::not_supported,
1472+
"Failed to locate symbol for 0 entry of .init_array");
1473+
const BinaryFunction *BF = BC->getFunctionForSymbol(Sym);
1474+
if (!BF)
1475+
return createStringError(
1476+
std::errc::not_supported,
1477+
"Failed to locate binary function for 0 entry of .init_array");
1478+
BC->StartFunctionAddress = BF->getAddress() + Reloc->Addend;
1479+
}
1480+
return Error::success();
1481+
}
1482+
1483+
if (const Relocation *Reloc = InitArraySection->getRelocationAt(0)) {
1484+
BC->StartFunctionAddress = Reloc->Value;
1485+
return Error::success();
1486+
}
1487+
1488+
return createStringError(std::errc::not_supported,
1489+
"No relocation for first DT_INIT_ARRAY slot");
1490+
}
1491+
14101492
Error RewriteInstance::discoverRtFiniAddress() {
14111493
// Use DT_FINI if it's available.
14121494
if (BC->FiniAddress) {
@@ -1444,6 +1526,68 @@ Error RewriteInstance::discoverRtFiniAddress() {
14441526
"No relocation for first DT_FINI_ARRAY slot");
14451527
}
14461528

1529+
Error RewriteInstance::updateRtInitReloc() {
1530+
if (BC->HasInterpHeader && opts::RuntimeLibInitHook == opts::RLIH_ENTRY_POINT)
1531+
return Error::success();
1532+
1533+
// Updating DT_INIT is handled by patchELFDynamic.
1534+
if (BC->InitAddress && opts::RuntimeLibInitHook <= opts::RLIH_INIT)
1535+
return Error::success();
1536+
1537+
const RuntimeLibrary *RT = BC->getRuntimeLibrary();
1538+
if (!RT || !RT->getRuntimeStartAddress())
1539+
return Error::success();
1540+
1541+
if (!BC->InitArrayAddress)
1542+
return Error::success();
1543+
1544+
if (!BC->InitArrayAddress || !BC->InitArraySize)
1545+
return createStringError(std::errc::not_supported,
1546+
"inconsistent .init_array state");
1547+
1548+
ErrorOr<BinarySection &> InitArraySection =
1549+
BC->getSectionForAddress(*BC->InitArrayAddress);
1550+
if (!InitArraySection)
1551+
return createStringError(std::errc::not_supported, ".init_array removed");
1552+
1553+
if (std::optional<Relocation> Reloc =
1554+
InitArraySection->takeDynamicRelocationAt(0)) {
1555+
if (Reloc->isRelative()) {
1556+
if (Reloc->Addend != BC->StartFunctionAddress)
1557+
return createStringError(std::errc::not_supported,
1558+
"inconsistent .init_array dynamic relocation");
1559+
Reloc->Addend = RT->getRuntimeStartAddress();
1560+
InitArraySection->addDynamicRelocation(*Reloc);
1561+
} else {
1562+
MCSymbol *Sym = Reloc->Symbol;
1563+
if (!Sym)
1564+
return createStringError(
1565+
std::errc::not_supported,
1566+
"Failed to locate symbol for 0 entry of .init_array");
1567+
const BinaryFunction *BF = BC->getFunctionForSymbol(Sym);
1568+
if (!BF)
1569+
return createStringError(
1570+
std::errc::not_supported,
1571+
"Failed to locate binary function for 0 entry of .init_array");
1572+
if (BF->getAddress() + Reloc->Addend != BC->StartFunctionAddress)
1573+
return createStringError(std::errc::not_supported,
1574+
"inconsistent .init_array dynamic relocation");
1575+
InitArraySection->addDynamicRelocation(Relocation{
1576+
/*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ Relocation::getAbs64(),
1577+
/*Addend*/ RT->getRuntimeStartAddress(), /*Value*/ 0});
1578+
}
1579+
}
1580+
// Update the static relocation by adding a pending relocation which will get
1581+
// patched when flushPendingRelocations is called in rewriteFile. Note that
1582+
// flushPendingRelocations will calculate the value to patch as
1583+
// "Symbol + Addend". Since we don't have a symbol, just set the addend to the
1584+
// desired value.
1585+
InitArraySection->addPendingRelocation(Relocation{
1586+
/*Offset*/ 0, /*Symbol*/ nullptr, /*Type*/ Relocation::getAbs64(),
1587+
/*Addend*/ RT->getRuntimeStartAddress(), /*Value*/ 0});
1588+
return Error::success();
1589+
}
1590+
14471591
void RewriteInstance::updateRtFiniReloc() {
14481592
// Updating DT_FINI is handled by patchELFDynamic.
14491593
if (BC->FiniAddress)
@@ -4838,7 +4982,8 @@ void RewriteInstance::patchELFSectionHeaderTable(ELFObjectFile<ELFT> *File) {
48384982
ELFEhdrTy NewEhdr = Obj.getHeader();
48394983

48404984
if (BC->HasRelocations) {
4841-
if (RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary())
4985+
RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary();
4986+
if (RtLibrary && opts::RuntimeLibInitHook == opts::RLIH_ENTRY_POINT)
48424987
NewEhdr.e_entry = RtLibrary->getRuntimeStartAddress();
48434988
else
48444989
NewEhdr.e_entry = getNewFunctionAddress(NewEhdr.e_entry);
@@ -5684,7 +5829,9 @@ void RewriteInstance::patchELFDynamic(ELFObjectFile<ELFT> *File) {
56845829
if (uint64_t Addr = RtLibrary->getRuntimeFiniAddress())
56855830
NewDE.d_un.d_ptr = Addr;
56865831
}
5687-
if (RtLibrary && Dyn.getTag() == ELF::DT_INIT && !BC->HasInterpHeader) {
5832+
if (RtLibrary && Dyn.getTag() == ELF::DT_INIT &&
5833+
(!BC->HasInterpHeader ||
5834+
opts::RuntimeLibInitHook == opts::RLIH_INIT)) {
56885835
if (auto Addr = RtLibrary->getRuntimeStartAddress()) {
56895836
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Set DT_INIT to 0x"
56905837
<< Twine::utohexstr(Addr) << '\n');
@@ -5760,6 +5907,13 @@ Error RewriteInstance::readELFDynamic(ELFObjectFile<ELFT> *File) {
57605907
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Set start function address\n");
57615908
BC->StartFunctionAddress = Dyn.getPtr();
57625909
}
5910+
BC->InitAddress = Dyn.getPtr();
5911+
break;
5912+
case ELF::DT_INIT_ARRAY:
5913+
BC->InitArrayAddress = Dyn.getPtr();
5914+
break;
5915+
case ELF::DT_INIT_ARRAYSZ:
5916+
BC->InitArraySize = Dyn.getPtr();
57635917
break;
57645918
case ELF::DT_FINI:
57655919
BC->FiniAddress = Dyn.getPtr();

0 commit comments

Comments
 (0)