diff --git a/bolt/lib/Rewrite/RewriteInstance.cpp b/bolt/lib/Rewrite/RewriteInstance.cpp index 7059a3dd23109..3b8baa3821459 100644 --- a/bolt/lib/Rewrite/RewriteInstance.cpp +++ b/bolt/lib/Rewrite/RewriteInstance.cpp @@ -2927,6 +2927,23 @@ void RewriteInstance::handleRelocation(const SectionRef &RelocatedSection, LLVM_DEBUG(dbgs() << "BOLT-DEBUG: ignoring relocation from data to data\n"); } +static BinaryFunction *getInitFunctionIfStaticBinary(BinaryContext &BC) { + // Workaround for https://github.com/llvm/llvm-project/issues/100096 + // ("[BOLT] GOT array pointer incorrectly rewritten"). In aarch64 + // static glibc binaries, the .init section's _init function pointer can + // alias with a data pointer for the end of an array. GOT rewriting + // currently can't detect this and updates the data pointer to the + // moved _init, causing a runtime crash. Skipping _init on the other + // hand should be harmless. + if (!BC.IsStaticExecutable) + return nullptr; + const BinaryData *BD = BC.getBinaryDataByName("_init"); + if (!BD || BD->getSectionName() != ".init") + return nullptr; + LLVM_DEBUG(dbgs() << "BOLT-DEBUG: skip _init in for GOT workaround.\n"); + return BC.getBinaryFunctionAtAddress(BD->getAddress()); +} + void RewriteInstance::selectFunctionsToProcess() { // Extend the list of functions to process or skip from a file. auto populateFunctionNames = [](cl::opt &FunctionNamesFile, @@ -3047,6 +3064,9 @@ void RewriteInstance::selectFunctionsToProcess() { return true; }; + if (BinaryFunction *Init = getInitFunctionIfStaticBinary(*BC)) + Init->setIgnored(); + for (auto &BFI : BC->getBinaryFunctions()) { BinaryFunction &Function = BFI.second; diff --git a/bolt/test/AArch64/check-init-not-moved.s b/bolt/test/AArch64/check-init-not-moved.s new file mode 100644 index 0000000000000..ad4b80d2e60e2 --- /dev/null +++ b/bolt/test/AArch64/check-init-not-moved.s @@ -0,0 +1,43 @@ +# Regression test for https://github.com/llvm/llvm-project/issues/100096 +# static glibc binaries crash on startup because _init is moved and +# shares its address with an array end pointer. The GOT rewriting can't +# tell the two pointers apart and incorrectly updates the _array_end +# address. Test checks that _init is not moved. + +# RUN: llvm-mc -filetype=obj -triple aarch64-unknown-unknown %s -o %t.o +# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -static -Wl,--section-start=.data=0x1000 -Wl,--section-start=.init=0x1004 +# RUN: llvm-bolt %t.exe -o %t.bolt +# RUN: llvm-nm %t.exe | FileCheck --check-prefix=CHECK-ORIGINAL %s +# RUN: llvm-nm %t.bolt | FileCheck --check-prefix=CHECK-BOLTED %s + +.section .data +.globl _array_end +_array_start: + .word 0x0 + +_array_end: +.section .init,"ax",@progbits +.globl _init + +# Check that bolt doesn't move _init. +# +# CHECK-ORIGINAL: 0000000000001004 T _init +# CHECK-BOLTED: 0000000000001004 T _init +_init: + ret + +.section .text,"ax",@progbits +.globl _start + +# Check that bolt is moving some other functions. +# +# CHECK-ORIGINAL: 0000000000001008 T _start +# CHECK-BOLTED-NOT: 0000000000001008 T _start +_start: + bl _init + adrp x0, #:got:_array_end + ldr x0, [x0, #:gotpage_lo15:_array_end] + adrp x0, #:got:_init + ldr x0, [x0, #:gotpage_lo15:_init] + ret +