Bug 27492 - Make static linking friendly with {ld.bfd,ld.lld} -z start-stop-gc: removing unused section '__libc_atexit'
Summary: Make static linking friendly with {ld.bfd,ld.lld} -z start-stop-gc: removing ...
Alias: None
Product: glibc
Classification: Unclassified
Component: libc (show other bugs)
Version: 2.33
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
Depends on:
Reported: 2021-03-01 08:37 UTC by Fangrui Song
Modified: 2021-04-17 03:09 UTC (History)
2 users (show)

See Also:
Last reconfirmed:
fweimer: security-


Note You need to log in before you can comment on or make changes to this bug.
Description Fangrui Song 2021-03-01 08:37:27 UTC
#include <stdio.h>
#include <stdlib.h>

void hook() {
int main() {

// 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

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
> /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/)
Comment 1 Florian Weimer 2021-03-01 13:54:29 UTC
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.
Comment 2 Fangrui Song 2021-03-02 05:16:37 UTC
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.
Comment 3 Fangrui Song 2021-04-17 03:09:11 UTC
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>