binutils Newspaper
JULY 29, 2026
ld Proposed Sentiment 8 / 10

ld: Prevent TLS and load config symbols from GC

Patch prevents linker garbage collection of `_tls_used` and `_load_config_used` symbols for mingw-w64 PE targets.

This patch addresses an issue where the linker (ld) with --gc-sections would garbage-collect symbols like _tls_used and _load_config_used on mingw-w64 PE targets. These symbols are necessary for the PE header at runtime and must not be collected. The change adds these symbols to the list of names that should not be garbage-collected, ensuring executables remain functional.

In the Thread 3 participants
  1. LIU Hao <lh_mouse@126.com> proposer

    Submits a patch to prevent `_tls_used` and `_load_config_used` symbols from being garbage-collected by the linker on mingw-w64 PE targets, as they are referenced indirectly and would otherwise be removed.

    “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 Ent…”
  2. Jan Beulich <jbeulich@suse.com> reviewer

    Acknowledged the patch and its description of how `_tls_used` could be removed due to `--gc-sections`.

    “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 bro…”
  3. LIU Hao <lh_mouse@126.com> proposer

    Responded to Jan Beulich's comment with a truncated diff.

    “在 2026-7-27 23:55, Jan Beulich 写道: >> --- 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…”

Technical Tradeoffs

  • Preventing garbage collection of these symbols ensures correctness for PE executables but may slightly increase the size of executables that do not use TLS or specific load configurations.
  • Correctly handling symbol garbage collection in conjunction with indirect references and specific file formats is complex.

In Details

Recent changes in mingw-w64 startup code have made _tls_used and _load_config_used symbols implicitly referenced via tentative definitions. When the binutils linker (ld) is invoked with --gc-sections, it performs garbage collection of unreferenced sections. Since these symbols are no longer strongly referenced by the startup code, ld incorrectly removes them, breaking the PE executable's Thread Local Storage (TLS) and load configuration structures. This patch prevents their garbage collection by explicitly registering them using lang_add_gc_name.

For Context
ld
The GNU linker, part of the GNU Binutils package, responsible for combining object files into executables.
--gc-sections
A linker option that enables garbage collection of unused sections in the output file, reducing executable size.
mingw-w64
A widely used fork of MinGW which provides a build environment for native MS Windows applications using GCC.
PE
Portable Executable format, the file format used for executables, DLLs, and object files on Windows operating systems.
_tls_used
A symbol often used in C/C++ runtimes to indicate that Thread Local Storage (TLS) is in use by the program.
_load_config_used
A symbol related to the PE loader configuration directory, indicating that specific loader features are required.
tentative definition
In C/C++, a declaration of a variable without an initializer. If no definition with an initializer is seen, it is assumed to have a default initializer (e.g., zero).
Filed Under: linkermingwpetlsgc-sections