SPRAB89A September 2011 – March 2014
In some dynamic linking models, including Linux, a module can be loaded during run-time using dlopen(). The TLS block from the dlopened module is dynamically allocated and so cannot be allocated at a fixed offset from the TP for all the threads. Hence the access to a thread-local variable is by reference using the module identifier and the offset of the thread-local variable in the module’s TLS block.
Figure 7-1 shows the C6x Linux TLS run-time representation. Each thread has an instance of this run-time TLS structure.
For each thread, the Thread Pointer (TP) points to the Thread Control Block (TCB). The executable’s TLS block, if it exists, is placed after the TCB after adjusting for the alignment. TLS blocks from other non-dynamic modules are placed subsequently honoring their alignment requirement. The TCB and the TLS blocks that follow for the static modules constitute the program’s static TLS. The static TLS for a thread is created as part of the thread creation.
The TCB is 64 bits wide. The first 32 bits point to the Dynamic Thread Vector (dtv). The remaining 32 bits are reserved.
The dtv pointed to by the TCB is a vector of 32-bit elements. The dtv[0] element is the generation ID, which is used to manage the dynamic growth of the dtv as dlopened modules are loaded. The dtv[n] elements, where n != 0, are 32-bit pointers to the TLS block for module n. When a module with TLS data is loaded, a module ID is assigned to that module. This module ID is process-specific. A dynamic shared library that is shared by multiple processes can have different module IDs in each process. Module ID 1 is always assigned to the executable.
The main thread is created by the dynamic loader, and subsequent threads are created by the thread library. When the main thread is created, the dtv array needs to contain only pointers to the initially loaded modules.
When a thread dlopens a new module, the module's TLS block should be allocated for all threads in the process. This is needed in case the other threads access this new module’s thread-local data. However, allocating the TLS block of the dlopened module can be deferred until the first time the storage it is accessed. This can be done by initializing the appropriate dtv[module-id] to TLS_DTV_UNALLOCATED. The __tls_get_addr() function can check to see if dtv[module_id] is TLS_DTV_UNALLOCATED; if so, it allocates and initializes the TLS block for the current thread.