ldc
ldc copied to clipboard
ELF: `__minfo` global variables may be garbage collected by ld.lld>=13.0.0 if built with llvm-project<13.0.0
I maintain lld/ELF nowadays. Sorry for your inconvenience but for the benefit of better garbage collection for metadata sections (PGO/sanitizer and more future instrumentation), I changed a garbage collection rule in LLD 13.0.0 (https://releases.llvm.org/13.0.0/tools/lld/docs/ReleaseNotes.html):
A __start___minfo reference from a live input section no longer keeps a __minfo section live.
(The topic is complex. You may read https://maskray.me/blog/2021-01-31-metadata-sections-comdat-and-shf-link-order#garbage-collection-on-metadata-sections for more information.)
FreeBSD is trying to upgrade its /usr/bin/ld to LLD 13.0.0. If ldc is built with llvm-project<13.0.0 when host ld.lld >= 13.0.0, there can be breakage:
- ld.lld>=13.0.0 defaults to
-z start-stop-gcand discards unreferenced__minfosections. - A
__minfosection will be retained if it has theSHF_GNU_RETAINflag. - Before llvm-project 13.0.0,
llvm.useddoes not setSHF_GNU_RETAINflag.
Some fixes:
- Detect host linker flavor and version. If
LDC_LLVM_VER < 1300 && ld.lld >= 13.0.0, pass-z nostart-stop-gc. This can be a CMake-time check. - FreeBSD uses llvm-project 13.0.0 to build ldc. Note: for LTO we really like the compiler and the linker to use the same LLVM version, though larger version linker usually works. IIUC FreeBSD's usage is not friendly to LTO.
- Somehow places
__minfoin a section group with aSHT_INIT_ARRAYsection. This is a hack which should retire ASAP. - Disable
--gc-sections - FreeBSD packager switches to a bundled ld.lld with a version matching llvm-project. This is the recommended way using LTO.
ld64 on Mach-O behaves similar to ld.lld -z nostart-stop-gc in the presence of -dead_strip.
- Detect host linker flavor and version. If
LDC_LLVM_VER < 1300 && ld.lld >= 13.0.0, pass-z nostart-stop-gc. This can be a CMake-time check.
Would it help if we always pass -z nostart-stop-gc to the linker when LDC_LLVM_VER < 1300 ?
The problem is that ld.lld errors for unknown -z options while GNU ld just warns.
% ld.bfd -z nostart-stop-gc a.o
ld.bfd: warning: cannot find entry symbol _start; defaulting to 0000000000401000
% ld.bfd -z unknown a.o
ld.bfd: warning: -z unknown ignored
ld.bfd: warning: cannot find entry symbol _start; defaulting to 0000000000401000
% ~/llvm-prebuilt/clang+llvm-11.0.0-x86_64-linux-gnu-ubuntu-20.04/bin/ld.lld -z nostart-stop-gc a.o
ld.lld: error: unknown -z value: nostart-stop-gc
Perhaps @lwhsu can tag the https://www.freshports.org/lang/ldc maintainer here.
I've seen these failures with lld v13; it's somehow fixed by declaring the start/stop symbols in one object file instead of each object file (and, FWIW, as IR constants if that makes a difference), in https://github.com/ldc-developers/ldc/pull/3850#issuecomment-952992369.