[PATCH v3] ld: Prevent `_tls_used` and `_load_config_used` from being garbage-collected
LIU Hao
lh_mouse@126.com
Fri Jul 31 11:23:25 GMT 2026
在 2026-7-31 18:16, Jan Beulich 写道:
> One small nit on the description: I don't think "Earlier today" can be quite
> correct anymore; it certainly won't be by the time the change is getting
> committed. Hence why it's best to avoid such wording from the beginning.
>
> And then see my more general remark in the reply to v1: That aspect imo at
> least wants mentioning briefly in the description, so that people finding
> an issue with this behavior can - upon doing archeology - can figure that
> this change was made despite understanding that it may not be correct in
> all cases.
I agree. In the attached patch, I have rewritten the first paragraph completely.
> As to your use of bfd_get_symbol_leading_char(): I would have hoped you'd
> do this more similarly to what is done in bfd/peXXigen.c. In particular I
> didn't expect a dynamic allocation to be used here. Yet now that I look I
> see that lang_add_gc_name() doesn't make a copy of the name. So all fine
I'm a bit wondering how memory should be managed here; isn't this a deliberate memory leak? I would like
to avoid unnecessary allocations.
> there. Nevertheless I would prefer if the if/else was avoided, again like
> bfd/peXXigen.c manages to do:
>
> /* The RVAs of these symbols will be written into the PE header, so they
> must not be collected. */
> char sym_prefix = bfd_get_symbol_leading_char (link_info.output_bfd);
> char *sym = xstrdup ("__tls_used");
> sym[0] = sym_prefix;
> lang_add_gc_name (sym + !!sym[0]);
> sym = xstrdup ("__load_config_used");
> sym[0] = sym_prefix;
> lang_add_gc_name (sym + !!sym[0]);
>
> Would you be amenable to switching to this approach? (Whether it's !!sym[0]
> or !!sym_prefix is of course secondary.)
`!!sym[0]` would be a bug. The other piece of code in bfd/peXXigen.c means to skip a non-zero prefix, and
to stop on a zero prefix which would be overwritten. Your suggestion is to skip (drop) a non-zero prefix
which is obviously not correct, and when the prefix is zero the argument for `lang_add_gc_name()` would
be an empty string.
I notice that `bfd_get_symbol_leading_char()` is an inline function in bfd-in2.h, so there's no need to
keep a local variable.
--
Best regards,
LIU Hao
-------------- next part --------------
From eb92d69bdbf50d8fde51ec048b5dad9bf3f5051e Mon Sep 17 00:00:00 2001
From: LIU Hao <lh_mouse@126.com>
Date: Fri, 31 Jul 2026 19:10:00 +0800
Subject: [PATCH] ld: Prevent `_tls_used` and `_load_config_used` from being
garbage-collected
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 entrypoint
function reference TLS initialization callbacks through function pointers as
tentative definitions, and the object files where TLS initialization callbacks
are defined should ensure `_tls_used` is linked, by referencing its address in
file-scope static pointers.
The issue here is that data sections of those object files are not referenced
otherwise. During linking, if LD is passed `--gc-sections`, it garbage-collects
such sections along with `_tls_used`, leaving a symbol of value zero, which
results 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 | 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)
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. */
+ char *sym = xstrdup ("__tls_used");
+ sym[0] = bfd_get_symbol_leading_char (link_info.output_bfd);
+ lang_add_gc_name (sym + !sym[0]);
+
+ sym = xstrdup ("__load_config_used");
+ sym[0] = 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);
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)
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. */
+ char *sym = xstrdup ("__tls_used");
+ sym[0] = bfd_get_symbol_leading_char (link_info.output_bfd);
+ lang_add_gc_name (sym + !sym[0]);
+
+ sym = xstrdup ("__load_config_used");
+ sym[0] = 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);
--
2.55.0
-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_signature.asc
Type: application/pgp-signature
Size: 840 bytes
Desc: OpenPGP digital signature
URL: <https://sourceware.org/pipermail/binutils/attachments/20260731/488fe823/attachment.sig>
More information about the Binutils
mailing list