Re: [PING]: [PATCH] ld: Account for .tbss size on ARM EABI targets

Torbjorn SVENSSON <[email protected]>
Newsgroups gmane.comp.gnu.binutils
Message-ID <[email protected]>
Hi Alan,

Thanks for the reply.

On 2026-09-01 01:33, Alan Modra wrote:
> On Mon, Aug 31, 2026 at 10:21:16AM +0200, Torbjorn SVENSSON wrote:
>>
>>
>> On 2026-08-30 11:49, Alan Modra wrote:
>>> On Sat, Aug 29, 2026 at 09:46:23AM +0200, Torbjorn SVENSSON wrote:
>>>>
>>>>
>>>> On 2026-08-29 09:35, Alan Modra wrote:
>>>>> On Fri, Aug 28, 2026 at 03:10:09PM +0200, Torbjorn SVENSSON wrote:
>>>>>>> Bare-metal ARM EABI programs allocate TLS storage directly, so if .tdata
>>>>>>> consumses VMA, then .tbss must also do so.  Otherwise, a following
>>>>>>> section such as .bss can overlap it.
>>>>>
>>>>> In other words, the loader is broken for TLS.  Why break the linker as
>>>>> well?
>>>>>
>>>>
>>>> What loader?
>>>> I'm taking about bare-metal, i.e. there is nothing doing any loading.
>>>> The CPU is executing straight from flash without any ram copy of the
>>>> program or similar loader activities that normally exist on a pc.
>>>
>>> Yes, TLS is not supported there.  How do you expect it is supposed to
>>> work?
>>>
>>
>> Arm decided to enable TLS for their arm-none-eabi toolchain in this commit:
>> https://gitlab.arm.com/tooling/gnu-devtools-for-arm/-/commit/f6a8633309c850d62817859986a165c94f11babf
>>
>> To my understanding, the local-exec TLS mode should be supported for
>> bare-metal targets.  In this mode, the memory block for TLS would be
>> allocated while linking the application and thus, when the application
>> boots, it will only need to do the initialization of .tbss and .tdata,
>> just like .bss and .data, for the initial thread.  Any additional thread
>> would allocate it's own block of memory and again initialize the values
>> from what was stored in flash / clear the region that should be cleared.
>>
>> Does this make sense or am I totally missing something here?
> 
> .tdata is not supposed to be used directly by the first thread.  It is
> for initialisation of each thread's TLS and must be kept pristine.
> That's why it doesn't make much sense for the first thread to use it
> directly, or to allocate vma space for .tbss.

Please consider this simplified version of the linker script that we use
for our STM32F407VG device that we manufacture.

MEMORY
{
   FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 1024K
   RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 128K
}

SECTIONS
{
   .text :
   {
     *(.text)
     ...
   } >FLASH

   _sidata = LOADADDR(.data);
   .data : ALIGN(4)
   {
     _sdata = .;
     *(.data)
     ...
     _edata = .;
   } >RAM AT> FLASH

   _sitdata = LOADADDR(.tdata);
   .tdata : ALIGN(4)
   {
     _stdata = .;
     *(.tdata)
     ...
     _etdata = .;
   } >RAM AT> FLASH

   .tbss (NOLOAD) : ALIGN(4)
   {
     _stbss = .;
     *(.tbss)
     ...
     _etbss = .;
   } >RAM

   .bss (NOLOAD) : ALIGN(4)
   {
     _sbss = .;
     *(.bss)
     ...
     _ebss = .;
   } >RAM
}

As you can see, we have a pristine copy of .tdata in FLASH and another
copy in RAM that the program is working on. Whenever there would be a
second thread, the data would be copied from flash again and that
would ensure that the initial values are correct.
There needs to be no special treatment for .tbss, since the only thing
required are the lengths (to know how much RAM to clear).

> 
> I doubt very much your target systems are running out of flash for
> very long.  Even back when I was designing hardware, support chips in
> the mid 80s were starting to provide RAM shadowing of ROM.  The system
> only ran out of ROM until it was copied to RAM, at which point the RAM
> shadow took over.  ROM was *much* slower, not only due to a slower
> access time but also due to the support chips allowing it to be 8-bit
> memory when main memory was 32-bit.  I haven't been in the hardware
> game for a long time, but I expect the speed difference between ROM
> and RAM still holds.  Which means you wouldn't want to access ROM for
> a pristine copy of .tdata when setting up the second thread (and you
> might not even be able to access ROM again without a reboot).

In the Arm Cortex-M world, there are speed differences between RAM and
ROM, just like you explain, but the pipeline limits the impact of it.
RAM is far more expensive than FLASH(ROM). As you can see in the linker
script above, flash is about 10 times bigger than RAM. To allow the
entire application to fit in RAM, plus the working data itself, then,
RAM would always need to be bigger than FLASH. Building such a device
would be possible, but very expensive.

Generally speaking, the wear on flash for reading is low and I've not
heard of anyone having issues running straight from flash for extensive
amount of time. What does kill an embedded devices is reprogramming the
flash.

> 
> The only case I can see where using .tdata/.tbss directly makes sense
> is when you in fact have only one thread, but you stupidly enabled
> threads or your software accidentally pulls in some library with
> thread support, *and* your bootup code doesn't understand TLS.  In
> that case I think you could link with this little extra script
> 
> SECTIONS {
>    .bogustls : { *(.tdata) *(.tbss) LONG (0); }
> }
> 
> which would more or less do the same as your patch.

Not sure if this helps you, but you can see how picolibc decided
to implement TLS support here:
https://github.com/picolibc/picolibc/blob/main/doc/tls.md

Here is also my patch to newlib, where I request to import this
implementation into newlib itself:
https://sourceware.org/pipermail/newlib/2026/022606.html

What is important for embedded, is to avoid malloc for the initial
thread if possible. The reason is that malloc is usually not supposed
to exist in embedded applications for security purposes.

It's also pointed out in the commit message from Arm here:
https://gitlab.arm.com/tooling/gnu-devtools-for-arm/-/commit/f6a8633309c850d62817859986a165c94f11babf

   Remove --disable-tls so that GCC emits ELF TLS relocations instead of
   using the emulated TLS runtime. This avoids malloc-based emulation and
   makes _Thread_local usable in bare-metal environments.


>> Note, LLVM linker already has this behavior.
> 
> That is likely just a bug, I think.
> 
>>   From some testing, it looks
>> like LLVM linker has the same output, regardless if linking for
>> bare-metal or PC.  I think that the solution that binutils has is
>> correct for PC, but not for bare-metal, hence my suggestion for special
>> handling.
>>
>> Kind regards,
>> Torbjörn
> 

Kind regards,
Torbjörn
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.