Search code examples
assemblylinkergdbglibcthread-local-storage

TLS Data Address the Same in All Threads


I traced glibc-2.27 using GDB. At line 178 in sysdeps/unix/sysv/linux/getsysstats.c, there exists a thread local storage access, as shown below:

while (l < re && isspace (*l))

IIUC, isspace() seems to access a table mapping ASCII characters to symbol type to, quickly, determine if the current character is space or not. This table seems to be a TLS. The relevant disassembly is as follows:

0x7f8f9ef480de <__GI___get_nprocs+318>  mov    0x2cbd1b(%rip),%rax        # 0x7f8f9f213e00                                                                        
0x7f8f9ef480e5 <__GI___get_nprocs+325>  mov    %fs:(%rax),%rdi                                                                                                    

rax contains 0xffffffffffffff98, which, IIUC, means that the address of the table, for each thread, is calculated using the following equation:$fs_base + 0xffffffffffffff98. When I use this equation to find the table address for each thread, they all return the same value, 0x00007f8f9732b82c. This is shown below:

(gdb) thread apply all x/2x $fs_base + 0xffffffffffffff98

Thread 47 (Thread 22457.22471):
0x7f8f75dfc698: 0x9732b82c  0x00007f8f

Thread 46 (Thread 22457.22470):
0x7f8f768fd698: 0x9732b82c  0x00007f8f

Thread 45 (Thread 22457.22469):
0x7f8f773fe698: 0x9732b82c  0x00007f8f

Thread 44 (Thread 22457.22468):
0x7f8f77eff698: 0x9732b82c  0x00007f8f

Thread 43 (Thread 22457.22467):
0x7f8f80a53698: 0x9732b82c  0x00007f8f

Thread 37 (Thread 22457.22465):
0x7f8f81c55698: 0x9732b82c  0x00007f8f

Thread 36 (Thread 22457.22464):
0x7f8f82456698: 0x9732b82c  0x00007f8f

Thread 35 (Thread 22457.22463):
0x7f8f8e6b0698: 0x9732b82c  0x00007f8f

Thread 34 (Thread 22457.22461):
0x7f8f8f480698: 0x9732b82c  0x00007f8f

Thread 33 (Thread 22457.22460):
0x7f8f94824698: 0x9732b82c  0x00007f8f

Thread 32 (Thread 22457.22459):
0x7f8f9649f698: 0x9732b82c  0x00007f8f

Thread 31 (Thread 22457.22458):
0x7f8f96ea0698: 0x9732b82c  0x00007f8f

Thread 30 (Thread 22457.22457):
0x7f8fa2570a18: 0x9732b82c  0x00007f8f

Thread 29 (Thread 22457.22466):
0x7f8f81454698: 0x9732b82c  0x00007f8f

I thought that TLS is exclusive to each thread, but, here, all threads use the same variable at 0x00007f8f9732b82c. Why is this the case? It seems that the linker recognizes the variable is read-only and saves some space?


Solution

  • You've shown that each thread has a different pointer variable, in TLS storage at
    0x7f8f75dfc698 vs.
    0x7f8f768fd698 etc.

    The fact that they're all pointing to the same table is completely normal, unless you've used uselocale(3) to have different locales in different threads.

    I think glibc has static constant (.section .rodata) char-map tables for different locales, and it sets a pointer to the right table based on the locale. Duplicating the whole table for every thread would be very inefficient, wasting more L3 cache footprint. If it was going to do that, you'd expect the whole table to be right there without a level of indirection.

    Have a look at how glibc implements functions like isupper() - by indexing an array of character-attribute flags, and checking a certain bit in that array element. (i.e. every array element is a bitmap of flags).

    https://code.woboq.org/userspace/glibc/ctype/ctype.h.html and related .c files in the same directory.