#include <stdio.h> #include <stdlib.h> void hook() { puts("hello"); } int main() { atexit(hook); } // Require -z start-stop-gc, which is available in trunk ld.lld (https://reviews.llvm.org/D96914) and GNU ld newer than 2021-03-01 (https://sourceware.org/bugzilla/show_bug.cgi?id=27451) % gcc -static a.c -Wl,--gc-sections -z start-stop-gc % ./a.out hello This appears to work. However, if you use the approach in https://sourceware.org/pipermail/binutils/2021-February/115561.html to compare -z nostart-stop-gc and -z start-stop-gc output, you should notice a difference % diff 0 1 16a17 > /home/ray/Dev/binutils-gdb/Debug/ld/ld-new: removing unused section '__libc_atexit' in file 'usr/lib/x86_64-linux-gnu/libc.a(genops.o)' I don't know whether the difference matters. In 2010, this difference caused bug 11133. A GNU ld workaround was installed. Then in 2015-10, the "__start_xx reference from a live input section retain all xx sections" rule was extended to all translation units. This is unfortunate because many modern metadata section usage cannot be GCed. (See "Metadata sections referenced by text sections" in http://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order ) If __libc_atexit wants to be a GC root, use SHF_GNU_RETAIN; otherwise, use an artificial relocation like R_X86_64_NONE (see https://lwn.net/Articles/741494/)
I expect that genops.c needs __libc_atexit to flush stdio streams in the statically linked case. We could likely handle this differently, e.g. call a weak function symbol in the process shutdown sequence if it is defined. At present, the weak symbol wouldn't matter because we have a sequence vSDO lookup → dynamic linker → malloc → vfprintf → libio which pulls in most of libio anyway.
The quickest fix is probably - *(__libc_atexit) + KEEP(*(__libc_atexit)) A better fix is to add a R_*_NONE relocation (`.reloc ., R_X86_64_NONE, target`) from the use site to a symbol in the __libc_atexit section. Unfortunately the .reloc approach needs to hard code the relocation name on various targets.
Fixed by https://sourceware.org/git/?p=glibc.git;a=commit;h=cd6ae7ea5431c2b8f16201fb0e2c413bf8d2df06 commit cd6ae7ea5431c2b8f16201fb0e2c413bf8d2df06 Author: Fangrui Song <maskray@google.com> Date: Fri Apr 16 11:26:39 2021 Set the retain attribute on _elf_set_element if CC supports [BZ #27492] So that text_set_element/data_set_element/bss_set_element defined variables will be retained by the linker. Note: 'used' and 'retain' are orthogonal: 'used' makes sure the variable will not be optimized out; 'retain' prevents section garbage collection if the linker support SHF_GNU_RETAIN. GNU ld 2.37 and LLD 13 will support -z start-stop-gc which allow C identifier name sections to be GCed even if there are live __start_/__stop_ references. Without the change, there are some static linking problems, e.g. _IO_cleanup (libio/genops.c) may be discarded by ld --gc-sections, so stdout is not flushed on exit. Note: GCC may warning 'retain' attribute ignored while __has_attribute(retain) is 1 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99587). Reviewed-by: H.J. Lu <hjl.tools@gmail.com>