How Thread Local Storage works in WIN32 and in Visual C++

Jonathan Wilson jonwil at tpgi.com.au
Mon Aug 18 21:49:46 CDT 2003


This is information I have figured out and found out about how Thread Local 
Storage (that is, variables declared with __declspec(thread)) works in 
Win32 from the perspective of the exe file (as opposed to the perspective 
of the kernel).
I am posting this to the ReactOS list since it needs support for this in 
its kernel code.
I am posting this to the WINE list since it also should support this from a 
kernel point of view. Plus it should (ideally) support __declspec(thread) 
in WineGCC at some point if that is possible
I am posting this to the MingW list since MingW needs __declspec(thread) 
support.

First, TLS in visual C++ relies on:
1.the __declspec(thread) keyword & some special stuff the compiler does 
when accessing TLS variables
2.the .tls segment in the PE file
3.the IMAGE_TLS_DIRECTORY32 structure (pointed at by a field in the PE 
header and stored in the read only data segment) and the things it points 
at (specificly the TlsStart, TlsEnd and TlsIndex variables)
TlsIndex is stored in the read/write data segment
Tls
4.the tlssup.obj file (which is inside the Visual C++ runtime library .lib 
files like libc.lib and msvcrt.lib)
and 5.a linker feature that will combine segments in a special way if they 
are named right (for example, .tls will be written first, then anything 
labeled .tls$ then .tls$zzz. All of them will be combined into one segment 
labeled .tls)

Firstly, when you declare a variable as __declspec(thread), the compiler 
accesses it a special way.
It takes the current value in the TlsIndex variable.
Then it takes the value stored at offset 2C in the TEB (which contains the 
pointer to the TLS data area for that thread)
it then does TlsPointer + TlsIndex * 4. This is what it uses when it reads 
and writes from Thread Local Storage variables.
The first variable seems to be stored at (this address) + 4 then + 8 and so 
on. at (this address) is the _tls_start variable as explained below)
Also, when you declare a __declspec(thread) variable, its put into the obj 
file inside the .tls$ segment.
Most of the magic happens inside the linker.
First, lets explain tlssup.obj
This contains an item called __tls_used that resides in the read only data 
segment which then becomes the TLS directory pointed at by the PE header.
This points at __tls_start which is in the .tls segment, __tls_end which is 
in the .tls$zzz segment and __tls_index which is in the read write data segment
There is also ___xl_a and ___xl_z, ___xl_a is pointed to by the thread 
callbacks pointer.
So, what we have in the resulting exe file is:
the __tls_used variable in the read only data segment
the __tls_index variable in the read write data segment
and the .tls segment containing:
__tls_start
//all the user declared __declspec(thread) variables
__tls_end
And we also have ___xl_a followed by calls to the thread callback functions 
   folowed by ___xl_z

So, basicly, to implement TLS in compilers, we need to:
1.make __declspec(thread) point at the thread local storage and get the 
compiler to access these variables correctly.
2.make the variables all end up in the correct place in the exe file with 
the TLS directory pointing at the start and end of the list
3.write the needed support code for the RTL (as needed)
4.make the callbacks (if needed) get generated & output to the exe file 
properly
and 5.make sure that the PE header points to the TLS directory.

If anyone has anything to add or wants more details, do reply :)

As for kernel-mode support in ReactOS or whatever, that seems easy enough 
to implement, anyone wanna have a go?




More information about the wine-devel mailing list