[llvm-dev] ld.lld "Don't let __start_/__stop_ retain C identifier name sections" && Swift
Fangrui Song via llvm-dev
llvm-dev at lists.llvm.org
Fri Feb 19 16:01:26 PST 2021
tl;dr With --gc-sections, I think the rule "__start_foo/__stop_foo references from live sections retains all non-SHF_LINK_ORDER input sections foo" does not cary its weight, so I'd like to drop it entirely in https://reviews.llvm.org/D96914
I have done a large-scale internal test with huge amount of OSS usage and spotted two issues:
(1) Linking systemd. https://github.com/systemd/systemd/blob/main/src/libsystemd/sd-bus/bus-error.h#L33 there will be an `undefined symbol: __start_SYSTEMD_BUS_ERROR_MAP` error. Supposedly it can be trivially fixed by using undefined weak symbols on __start_/__stop_.
(2) Linking Swift. There will be errors like `undefined hidden symbol: __start_swift5_protocols`.
https://github.com/apple/swift/blob/main/stdlib/public/runtime/SwiftRT-ELF.cpp
It seems that trivially making `extern const char __start_##name` does not work.
The code relies on some `swift5_*` input sections being GC root.
(If someone can file an issue to Swift, I'd appreciate that.)
(If Swift folks can fix it, I'll give my big thanks:)
This can still potentially break some propritary code so I am sending this heads-up.
I'll place rationale below (it is complicated).
The current rule is:
__start_/__stop_ references retains all non-SHF_LINK_ORDER C identifier name sections.
After https://reviews.llvm.org/D96753 , it will become
__start_/__stop_ references retains all non-SHF_LINK_ORDER non-SHF_GROUP C identifier name sections.
(The section group special case is to allow garbage collecting __llvm_prf_* sections for -fprofile-generate/-fprofile-instr-generate. The saving is huge.)
Personally I'd drop the rule entirely (D96914) (get support from jhenderson and phosek), i.e.
__start_/__stop_ references do not retain C identifier name sections.
and hope folks can fix Swift/systemd to not rely on the original rule.
---
I have placed more details in https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order
discussing why the rule gets in the away and why SHF_LINK_ORDER is not a solution.
(Section groups have size overhead for single metadata section.)
I'll paste the relevant paragraph here for your convenience.
(I may edit my article to make it clear)
This is a common usage of metadata sections: each text section references a metadata section.
The metadata sections have a C identifier name to allow the runtime to collect them via `__start_`/`__stop_` symbols.
Since `__start_`/`__stop_` references are always present from live sections, the C identifier name sections appear like GC roots, which means they cannot be discarded by `ld --gc-sections`.
For users who want to keep GC for these metadata sections, they can set the `SHF_LINK_ORDER` flag or make the metadata section a member of a section group.
(GNU ld does not implement the `SHF_LINK_ORDER` rule yet. <https://sourceware.org/bugzilla/show_bug.cgi?id=27259>)
(In LLD, some folks have concluded that this rule does not cary its weight, so possibly it would be nice it we can drop it ([D96914](https://reviews.llvm.org/D96914)).)
Now, let's walk through an `SHF_LINK_ORDER` example when inlining can cause problems.
```asm
# Monolithic meta.
.globl _start
_start:
leaq __start_meta(%rip), %rdi
leaq __stop_meta(%rip), %rsi
.section .text.foo,"ax", at progbits
.globl foo
foo:
leaq .Lmeta.foo(%rip), %rax
ret
.section .text.bar,"ax", at progbits
.globl bar
bar:
call foo
leaq .Lmeta.bar(%rip), %rax
ret
.section meta,"a", at progbits
.Lmeta.foo:
.byte 0
.Lmeta.bar:
.byte 1
```
The monolithic `meta` does not enable precise garbage collection.
It may be attempting to make `meta` separate and add the `SHF_LINK_ORDER` flag (to defeat the "C identifier name sections appear like GC roots" rule):
```asm
.section meta,"ao", at progbits,foo
.Lmeta.foo:
.byte 0
.section meta,"ao", at progbits,bar
.Lmeta.bar:
.byte 1
```
However, due to inlining (foo into bar), the `meta` for `.text.foo` may now get a reference from another text section `.text.bar`, breaking an implicit assumption of `SHF_LINK_ORDER`: such a section can only be referenced from its linked-to section.
```asm
# Both .text.foo and .text.bar reference meta.
.section .text.foo,"ax", at progbits
.globl foo
foo:
leaq .Lmeta.foo(%rip), %rax
ret
.section .text.bar,"ax", at progbits
.globl bar
bar:
leaq .Lmeta.foo(%rip), %rax
leaq .Lmeta.bar(%rip), %rax
ret
```
If `.text.bar` (caller) is retained while `.text.foo` (callee) is discarded, the `meta` for `foo` will link to a discarded section: an invalid state.
LLD has an error: `{{.*}}:(meta): sh_link points to discarded section {{.*}}:(.text.foo)`.
More information about the llvm-dev
mailing list