tls

tls

线程本地存储

Linux的glibc使用GS寄存器来访问TLS,GS指向TEB,gs寄存器在线程切换时并不改变,而是采用更改相应偏移中内容,来切换。

另外对于线程来说,他的栈是有限的,是用过mmap创建的。

这是我修改过用来测试的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <stdlib.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <pthread.h>
__thread int var = 0; // var定义为线程变量,每个数线拥有一份,这玩意不初始化在tbss,初始化在tdata

void* worker(void* arg);

int main(){
pthread_t pid1,pid2;

pthread_create(&pid1,NULL,worker,(void *)0);
pthread_create(&pid2,NULL,worker,(void *)1);

pthread_join(pid1,NULL);
pthread_join(pid2,NULL);

return 0;
}
void* worker(void* arg){
int idx = (int)arg;
int i=0;
int j=0;
int e=0;
while(1) {
i++;
j++;

e++;
var++;
asm volatile("mov %fs:0,%rax;");
}
}

TLS如下

1
2
3
4
5
6
7
8
9
10
11
线程2的tls如下 
0x7ffff77ef700是fs:[0]可以看到他指向自己,在栈底(这个偏移是固定的,如果找不到地址,那么从栈底往上找一下就好)
0x7ffff77ef6f0: 0x0000000000000000 0x0003cb0300000000
0x7ffff77ef700: 0x00007ffff77ef700 0x0000000000602020
0x7ffff77ef710: 0x00007ffff77ef700 0x0000000000000001
0x7ffff77ef720: 0x0000000000000000 0x3388f717cab24000
0x7ffff77ef730: 0x86ad80d7b8682fb4 0x0000000000000000
0x7ffff77ef740: 0x0000000000000000 0x0000000000000000
0x7ffff77ef750: 0x0000000000000000 0x0000000000000000
0x7ffff77ef760: 0x0000000000000000 0x0000000000000000
0x7ffff77ef770: 0x0000000000000000 0x0000000000000000

创建线程后的栈

1
2
3
4
5
6
7
8
主线程
RBP 0x7fffffffded0 —▸ 0x4007c0 (__libc_csu_init) ◂— push r15
RSP 0x7fffffffdeb0 —▸ 0x4007c0 (__libc_csu_init) ◂— push r15


thread1
*RBP 0x7ffff77eef50 ◂— 0x0
*RSP 0x7ffff77eef50 ◂— 0x0

内存如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
         0x400000           0x401000 r-xp     1000 0      /home/j/2
0x600000 0x601000 r--p 1000 0 /home/j/2
0x601000 0x602000 rw-p 1000 1000 /home/j/2
0x602000 0x623000 rw-p 21000 0 [heap]
0x7ffff6fef000 0x7ffff6ff0000 ---p 1000 0
0x7ffff6ff0000 0x7ffff77f0000 rw-p 800000 0
0x7ffff77f0000 0x7ffff79b0000 r-xp 1c0000 0 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff79b0000 0x7ffff7bb0000 ---p 200000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7bb0000 0x7ffff7bb4000 r--p 4000 1c0000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7bb4000 0x7ffff7bb6000 rw-p 2000 1c4000 /lib/x86_64-linux-gnu/libc-2.23.so
0x7ffff7bb6000 0x7ffff7bba000 rw-p 4000 0
0x7ffff7bba000 0x7ffff7bd2000 r-xp 18000 0 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7ffff7bd2000 0x7ffff7dd1000 ---p 1ff000 18000 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7ffff7dd1000 0x7ffff7dd2000 r--p 1000 17000 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7ffff7dd2000 0x7ffff7dd3000 rw-p 1000 18000 /lib/x86_64-linux-gnu/libpthread-2.23.so
0x7ffff7dd3000 0x7ffff7dd7000 rw-p 4000 0
0x7ffff7dd7000 0x7ffff7dfd000 r-xp 26000 0 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7fdb000 0x7ffff7fdf000 rw-p 4000 0
0x7ffff7ff8000 0x7ffff7ffa000 r--p 2000 0 [vvar]
0x7ffff7ffa000 0x7ffff7ffc000 r-xp 2000 0 [vdso]
0x7ffff7ffc000 0x7ffff7ffd000 r--p 1000 25000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ffd000 0x7ffff7ffe000 rw-p 1000 26000 /lib/x86_64-linux-gnu/ld-2.23.so
0x7ffff7ffe000 0x7ffff7fff000 rw-p 1000 0
0x7ffffffde000 0x7ffffffff000 rw-p 21000 0 [stack]
0xffffffffff600000 0xffffffffff601000 r-xp 1000 0 [vsyscall]

worker代码,可以看到,对于thread类的变量,使用fs来存储的,

1
2
3
mov    eax, dword ptr fs:[0xfffffffffffffffc]
► 0x4007e4 <worker+36> add eax, 1
0x4007e7 <worker+39> mov dword ptr fs:[0xfffffffffffffffc], eax