ld: Prevent `_tls_used` and `_load_config_used` from being garbage-collected

Message ID 8eb0155d-cbb3-4780-a81f-c360aa27bdde@126.com
State New
Headers
Series ld: Prevent `_tls_used` and `_load_config_used` from being garbage-collected |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm fail Patch failed to apply
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 fail Patch failed to apply

Commit Message

LIU Hao July 27, 2026, 3:38 p.m. UTC
  Hello,

Below is a patch that is required for LD to work with `--gc-sections` with recent mingw-w64, tested on 
x86_64-w64-mingw32 and i686-w64-mingw32.

I don't have access to binutils Git so please commit it for me. Also please consider backporting.



 From 9a98b90d43739f5979a999acda3beb21b17442b2 Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse@126.com>
Date: Mon, 27 Jul 2026 23:11:54 +0800
Subject: [PATCH] ld: Prevent `_tls_used` and `_load_config_used` from being
  garbage-collected

Earlier today I pushed some patches to mingw-w64 to make `_tls_used` only
linked on demand, by referencing it indirectly through tentative definitions.
However, since the startup code no longer has strong references to `_tls_used`,
if LD is passed `--gc-sections`, it garbage-collects `_tls_used`, resulting in
a broken executable:

    $ objdump -p bin/test_thread_id_cpp.exe | grep -F .tls
    Entry 9 ffffffffc0000000 00000028 Thread Storage Directory [.tls]

This patch prevents `_tls_used` from being garbage-collected, and likewise for
`_load_config_used`.

Signed-off-by: LIU Hao <lh_mouse@126.com>
---
  ld/emultempl/pe.em  | 10 ++++++++++
  ld/emultempl/pep.em |  5 +++++
  2 files changed, 15 insertions(+)

-- 
2.55.0
From 9a98b90d43739f5979a999acda3beb21b17442b2 Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse@126.com>
Date: Mon, 27 Jul 2026 23:11:54 +0800
Subject: [PATCH] ld: Prevent `_tls_used` and `_load_config_used` from being
 garbage-collected

Earlier today I pushed some patches to mingw-w64 to make `_tls_used` only
linked on demand, by referencing it indirectly through tentative definitions.
However, since the startup code no longer has strong references to `_tls_used`,
if LD is passed `--gc-sections`, it garbage-collects `_tls_used`, resulting in
a broken executable:

   $ objdump -p bin/test_thread_id_cpp.exe | grep -F .tls
   Entry 9 ffffffffc0000000 00000028 Thread Storage Directory [.tls]

This patch prevents `_tls_used` from being garbage-collected, and likewise for
`_load_config_used`.

Signed-off-by: LIU Hao <lh_mouse@126.com>
---
 ld/emultempl/pe.em  | 10 ++++++++++
 ld/emultempl/pep.em |  5 +++++
 2 files changed, 15 insertions(+)

diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em
index 07ef2ca5953..475e0349ff8 100644
--- a/ld/emultempl/pe.em
+++ b/ld/emultempl/pe.em
@@ -1573,6 +1573,16 @@ gld${EMULATION_NAME}_after_open (void)
 
   pe_output_file_set_long_section_names (link_info.output_bfd);
 
+  /* The RVAs of these symbols will be written into the PE header, so they
+     must not be collected.  */
+#if defined (TARGET_IS_i386pe)
+  lang_add_gc_name ("__tls_used");
+  lang_add_gc_name ("__load_config_used");
+#else
+  lang_add_gc_name ("_tls_used");
+  lang_add_gc_name ("_load_config_used");
+#endif
+
 #ifdef DLL_SUPPORT
   pe_process_import_defs (link_info.output_bfd, &link_info);
 
diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em
index 14be8435685..1ab5c4d88f7 100644
--- a/ld/emultempl/pep.em
+++ b/ld/emultempl/pep.em
@@ -1624,6 +1624,11 @@ gld${EMULATION_NAME}_after_open (void)
 
   pep_output_file_set_long_section_names (link_info.output_bfd);
 
+  /* The RVAs of these symbols will be written into the PE header, so they
+     must not be collected.  */
+  lang_add_gc_name ("_tls_used");
+  lang_add_gc_name ("_load_config_used");
+
 #ifdef DLL_SUPPORT
   pep_process_import_defs (link_info.output_bfd, &link_info);
  

Comments

Jan Beulich July 27, 2026, 3:55 p.m. UTC | #1
On 27.07.2026 17:38, LIU Hao wrote:
> From: LIU Hao <lh_mouse@126.com>
> 
> Earlier today I pushed some patches to mingw-w64 to make `_tls_used` only
> linked on demand, by referencing it indirectly through tentative definitions.
> However, since the startup code no longer has strong references to `_tls_used`,
> if LD is passed `--gc-sections`, it garbage-collects `_tls_used`, resulting in
> a broken executable:
> 
>     $ objdump -p bin/test_thread_id_cpp.exe | grep -F .tls
>     Entry 9 ffffffffc0000000 00000028 Thread Storage Directory [.tls]
> 
> This patch prevents `_tls_used` from being garbage-collected, and likewise for
> `_load_config_used`.

But it does so not knowing what environment the executable targets. Aiui
both names aren't exactly "reserved" in the PE world, they're more like
reserved in combination with certain C or other runtime libraries, I
suppose. In an EFI application, for example, they may exist but have an
entirely different purpose.

Then again I realize that bfd/peXXigen.c already makes a similar
assumption.

> --- a/ld/emultempl/pe.em
> +++ b/ld/emultempl/pe.em
> @@ -1573,6 +1573,16 @@ gld${EMULATION_NAME}_after_open (void)
> 
>     pe_output_file_set_long_section_names (link_info.output_bfd);
> 
> +  /* The RVAs of these symbols will be written into the PE header, so they
> +     must not be collected.  */
> +#if defined (TARGET_IS_i386pe)
> +  lang_add_gc_name ("__tls_used");
> +  lang_add_gc_name ("__load_config_used");
> +#else
> +  lang_add_gc_name ("_tls_used");
> +  lang_add_gc_name ("_load_config_used");
> +#endif

The #ifdef here likely wants replacing by appropriate use of
bfd_get_symbol_leading_char(). To play safe towards future uses, the
same code could then also be used in pep.em.

Jan
  

Patch

diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em
index 07ef2ca5953..475e0349ff8 100644
--- a/ld/emultempl/pe.em
+++ b/ld/emultempl/pe.em
@@ -1573,6 +1573,16 @@  gld${EMULATION_NAME}_after_open (void)

    pe_output_file_set_long_section_names (link_info.output_bfd);

+  /* The RVAs of these symbols will be written into the PE header, so they
+     must not be collected.  */
+#if defined (TARGET_IS_i386pe)
+  lang_add_gc_name ("__tls_used");
+  lang_add_gc_name ("__load_config_used");
+#else
+  lang_add_gc_name ("_tls_used");
+  lang_add_gc_name ("_load_config_used");
+#endif
+
  #ifdef DLL_SUPPORT
    pe_process_import_defs (link_info.output_bfd, &link_info);

diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em
index 14be8435685..1ab5c4d88f7 100644
--- a/ld/emultempl/pep.em
+++ b/ld/emultempl/pep.em
@@ -1624,6 +1624,11 @@  gld${EMULATION_NAME}_after_open (void)

    pep_output_file_set_long_section_names (link_info.output_bfd);

+  /* The RVAs of these symbols will be written into the PE header, so they
+     must not be collected.  */
+  lang_add_gc_name ("_tls_used");
+  lang_add_gc_name ("_load_config_used");
+
  #ifdef DLL_SUPPORT
    pep_process_import_defs (link_info.output_bfd, &link_info);