Summary
Buster's ELF writers that synthesize an entry stub call constructors and destructors directly, and take the two array sections off the object first (link_initializer_plan_build). Any symbol defined inside those arrays keeps its section and offset but loses its storage. Code that reads the entry through that symbol gets a wrong value, with exit 0. GNU ld keeps the array and the read is correct.
Since buster14a/buster#1716 (#1276), __attribute__((section(".init_array"))) places C variables there, so ordinary C source reaches this directly.
Reproduction
At PR head cc3fd4d, on x86-64 Linux (Ubuntu 24.04), with a Release ide built with Clang 18.1.3 under the hosted bootstrap exception:
#include <stdio.h>
static int ran;
static void early(void) { ran = 1; }
__attribute__((section(".init_array"), used)) void (*early_ptr)(void) = early;
int main(void) { printf("ran=%d ptr_ok=%d\n", ran, early_ptr == early); return 0; }
ide cc iav2.c -o a && ./a # ran=1 ptr_ok=0
ide cc -c iav2.c -o iav2.o && gcc -no-pie iav2.o -o b && ./b # ran=1 ptr_ok=1
The constructor itself runs in both cases. Only the read through early_ptr diverges.
First divergence
link_initializer_plan_build (link.c) copies the object with sections[INIT_ARRAY]/[FINI_ARRAY] emptied (data = {0}, virtual_size = 0), and drops their relocations.
- Symbols whose section is one of those kinds are left untouched. Their value is an offset into a section that is no longer laid out, so the writers resolve them to an address that holds something else.
- The same applies to foreign objects that define a symbol inside an initializer array. That is plausible for hand-written assembly or linker-set-style code, but I have not reproduced it.
Repair options (undecided)
- Keep the array bytes as ordinary relocated read-only or writable data whenever any symbol is defined inside them (or always), and still call the entries from the stub.
- Or refuse the link with a diagnostic naming the symbol. That is not a silent wrong value, but it is less compatible.
Completion criteria
Summary
Buster's ELF writers that synthesize an entry stub call constructors and destructors directly, and take the two array sections off the object first (
link_initializer_plan_build). Any symbol defined inside those arrays keeps its section and offset but loses its storage. Code that reads the entry through that symbol gets a wrong value, with exit 0. GNU ld keeps the array and the read is correct.Since buster14a/buster#1716 (#1276),
__attribute__((section(".init_array")))places C variables there, so ordinary C source reaches this directly.Reproduction
At PR head
cc3fd4d, on x86-64 Linux (Ubuntu 24.04), with a Releaseidebuilt with Clang 18.1.3 under the hosted bootstrap exception:The constructor itself runs in both cases. Only the read through
early_ptrdiverges.First divergence
link_initializer_plan_build(link.c) copies the object withsections[INIT_ARRAY]/[FINI_ARRAY]emptied (data = {0},virtual_size = 0), and drops their relocations.Repair options (undecided)
Completion criteria
ptr_ok=1, or the link is refused with a diagnostic namingearly_ptr..fini_arrayand for a foreign ELF object that defines a symbol in its.init_array.test_allpasses.