[binutils-gdb] ld: Prevent `_tls_used` and `_load_config_used` from being garbage-collected

Jan Beulich via Binutils-cvs <[email protected]> Fri, 31 Jul 2026 11:54:55 +0000 (GMT)
Newsgroups gmane.comp.gnu.binutils.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=3Dbinutils-gdb.git;h=3Dee7506d8ffbe=
c39df2262f08a348346914d71efd

commit ee7506d8ffbec39df2262f08a348346914d71efd
Author: LIU Hao <[email protected]>
Date:   Fri Jul 31 13:52:14 2026 +0200

    ld: Prevent `_tls_used` and `_load_config_used` from being garbage-coll=
ected
   =20
    In mingw-w64 there's an ongoing effort to make the TLS directory of an =
image
    optional and only linked on demand. The approach is to have the entrypo=
int
    function reference TLS initialization callbacks through function pointe=
rs as
    tentative definitions, and the object files where TLS initialization ca=
llbacks
    are defined should ensure `_tls_used` is linked, by referencing its add=
ress in
    file-scope static pointers.
   =20
    The issue here is that data sections of those object files are not refe=
renced
    otherwise. During linking, if LD is passed `--gc-sections`, it garbage-=
collects
    such sections along with `_tls_used`, leaving a symbol of value zero, w=
hich
    results in a broken executable:
   =20
       $ objdump -p bin/test_thread_id_cpp.exe | grep -F .tls
       Entry 9 ffffffffc0000000 00000028 Thread Storage Directory [.tls]
   =20
    This patch prevents `_tls_used` from being garbage-collected, and likew=
ise for
    `_load_config_used`.
   =20
    Signed-off-by: LIU Hao <[email protected]>

Diff:
---
 ld/emultempl/pe.em  | 10 ++++++++++
 ld/emultempl/pep.em | 10 ++++++++++
 2 files changed, 20 insertions(+)

diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em
index 07ef2ca5953..1d02a86be96 100644
--- a/ld/emultempl/pe.em
+++ b/ld/emultempl/pe.em
@@ -1573,6 +1573,16 @@ gld${EMULATION_NAME}_after_open (void)
=20
   pe_output_file_set_long_section_names (link_info.output_bfd);
=20
+  /* The RVAs of these symbols will be written into the PE header, so they
+     must not be collected.  */
+  char *sym =3D xstrdup ("__tls_used");
+  sym[0] =3D bfd_get_symbol_leading_char (link_info.output_bfd);
+  lang_add_gc_name (sym + !sym[0]);
+
+  sym =3D xstrdup ("__load_config_used");
+  sym[0] =3D bfd_get_symbol_leading_char (link_info.output_bfd);
+  lang_add_gc_name (sym + !sym[0]);
+
 #ifdef DLL_SUPPORT
   pe_process_import_defs (link_info.output_bfd, &link_info);
=20
diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em
index 25ce3963b36..3ba29401821 100644
--- a/ld/emultempl/pep.em
+++ b/ld/emultempl/pep.em
@@ -1582,6 +1582,16 @@ gld${EMULATION_NAME}_after_open (void)
=20
   pep_output_file_set_long_section_names (link_info.output_bfd);
=20
+  /* The RVAs of these symbols will be written into the PE header, so they
+     must not be collected.  */
+  char *sym =3D xstrdup ("__tls_used");
+  sym[0] =3D bfd_get_symbol_leading_char (link_info.output_bfd);
+  lang_add_gc_name (sym + !sym[0]);
+
+  sym =3D xstrdup ("__load_config_used");
+  sym[0] =3D bfd_get_symbol_leading_char (link_info.output_bfd);
+  lang_add_gc_name (sym + !sym[0]);
+
 #ifdef DLL_SUPPORT
   pep_process_import_defs (link_info.output_bfd, &link_info);