Skip to content

Commit 1b2f3b3

Browse files
Vasily Leonenkovleonen
authored andcommitted
[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 d44d329 commit 1b2f3b3

File tree

4 files changed

+185
-8
lines changed

4 files changed

+185
-8
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 availability 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 & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,28 @@ cl::bits<GadgetScannerKind> GadgetScannersToRun(
294294
clEnumValN(GS_ALL, "all", "All implemented scanners")),
295295
cl::ZeroOrMore, cl::CommaSeparated, cl::cat(BinaryAnalysisCategory));
296296

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

299321
// FIXME: implement a better way to mark sections for replacement.
@@ -741,9 +763,12 @@ Error RewriteInstance::run() {
741763
adjustCommandLineOptions();
742764
discoverFileObjects();
743765

744-
if (opts::Instrument && !BC->IsStaticExecutable)
766+
if (opts::Instrument && !BC->IsStaticExecutable) {
767+
if (Error E = discoverRtInitAddress())
768+
return E;
745769
if (Error E = discoverRtFiniAddress())
746770
return E;
771+
}
747772

748773
preprocessProfileData();
749774

@@ -785,8 +810,11 @@ Error RewriteInstance::run() {
785810

786811
updateMetadata();
787812

788-
if (opts::Instrument && !BC->IsStaticExecutable)
813+
if (opts::Instrument && !BC->IsStaticExecutable) {
814+
if (Error E = updateRtInitReloc())
815+
return E;
789816
updateRtFiniReloc();
817+
}
790818

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

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

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

48514995
if (BC->HasRelocations) {
4852-
if (RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary())
4996+
RuntimeLibrary *RtLibrary = BC->getRuntimeLibrary();
4997+
if (RtLibrary && opts::RuntimeLibInitHook == opts::RLIH_ENTRY_POINT)
48534998
NewEhdr.e_entry = RtLibrary->getRuntimeStartAddress();
48544999
else
48555000
NewEhdr.e_entry = getNewFunctionAddress(NewEhdr.e_entry);
@@ -5695,7 +5840,9 @@ void RewriteInstance::patchELFDynamic(ELFObjectFile<ELFT> *File) {
56955840
if (uint64_t Addr = RtLibrary->getRuntimeFiniAddress())
56965841
NewDE.d_un.d_ptr = Addr;
56975842
}
5698-
if (RtLibrary && Dyn.getTag() == ELF::DT_INIT && !BC->HasInterpHeader) {
5843+
if (RtLibrary && Dyn.getTag() == ELF::DT_INIT &&
5844+
(!BC->HasInterpHeader ||
5845+
opts::RuntimeLibInitHook == opts::RLIH_INIT)) {
56995846
if (auto Addr = RtLibrary->getRuntimeStartAddress()) {
57005847
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Set DT_INIT to 0x"
57015848
<< Twine::utohexstr(Addr) << '\n');
@@ -5767,10 +5914,13 @@ Error RewriteInstance::readELFDynamic(ELFObjectFile<ELFT> *File) {
57675914
for (const Elf_Dyn &Dyn : DynamicEntries) {
57685915
switch (Dyn.d_tag) {
57695916
case ELF::DT_INIT:
5770-
if (!BC->HasInterpHeader) {
5771-
LLVM_DEBUG(dbgs() << "BOLT-DEBUG: Set start function address\n");
5772-
BC->StartFunctionAddress = Dyn.getPtr();
5773-
}
5917+
BC->InitAddress = Dyn.getPtr();
5918+
break;
5919+
case ELF::DT_INIT_ARRAY:
5920+
BC->InitArrayAddress = Dyn.getPtr();
5921+
break;
5922+
case ELF::DT_INIT_ARRAYSZ:
5923+
BC->InitArraySize = Dyn.getPtr();
57745924
break;
57755925
case ELF::DT_FINI:
57765926
BC->FiniAddress = Dyn.getPtr();

0 commit comments

Comments
 (0)