Loading... ##### **通过一个几行代码的示例来浅层地观察一下glibc的TLS实现中的结构体:** 代码: ``` #include<stdio.h> #include<pthread.h> #include<string.h> #include<sys/types.h> #include<unistd.h> pthread_t ntid; void *thr_fn(void *args) { char a; printf("new thread:\n"); scanf("%c",&a); } int main() { int err; err = pthread_create(&ntid,NULL,thr_fn,NULL); pthread_join(ntid,NULL); printf("main thread:\n"); char b; scanf(" %c",&b); return 0; } ``` #### **首先在main下断点,进入主线程:** ![](https://z3.ax1x.com/2021/05/08/gGx5VJ.png) 在 `X86`中 `TLS`由 `FS`寄存器(通常由运行时代码或线程库管理)指向,`TLS`在 `glibc`中的实现为 `tcbhead_t(TCB)`结构体,其定义如下: ``` typedef struct { void *tcb; /* Pointer to the TCB. Not necessarily the thread descriptor used by libpthread. */ dtv_t *dtv; void *self; /* Pointer to the thread descriptor. */ int multiple_threads; int gscope_flag; uintptr_t sysinfo; uintptr_t stack_guard; //储存canary的值 uintptr_t pointer_guard; unsigned long int vgetcpu_cache[2]; /* Bit 0: X86_FEATURE_1_IBT. Bit 1: X86_FEATURE_1_SHSTK. */ unsigned int feature_1; int __glibc_unused1; /* Reservation of some values for the TM ABI. */ void *__private_tm[4]; /* GCC split stack support. */ void *__private_ss; /* The lowest address of shadow stack, */ unsigned long long int ssp_base; /* Must be kept even if it is no longer used by glibc since programs, like AddressSanitizer, depend on the size of tcbhead_t. */ __128bits __glibc_unused2[8][4] __attribute__ ((aligned (32))); void *__padding[8]; } tcbhead_t; ``` 查看主线程的 `$fs_base`(在保护模式下 `$fs`为空值,需查看每个线程的 `$fs_base`)`=0x7ffff7d9f740` ![](https://z3.ax1x.com/2021/05/08/gGx0bQ.png) 动态链接器完成内存映射后将调用 `init_tls`等一系列函数来初始化主线程的静态 `TLS`和 `DTV`,由上面 `vmmap`的结果可知此时 `TLS`结构体 `(TCB)`被分配在 `mmap`段。 其中 `dtv`部分指向一个类型为 `dtv_t`的数组的第二个元素,该结构体的定义为: ``` typedef union dtv { size_t counter; struct { void* val; bool is_static; } pointer; } dtv_t; ``` `dtv`数组前两个元素具有特定功能,如下: ``` dtv[0].counter; /* Pro tip: The length of this dtv array */ dtv[1].counter; /* Generation counter for the DTV in this thread */ dtv[2].pointer; /* Pointer to the main executable TLS block in this thread */ ``` 查看主线程的 `dtv`: ![](https://z3.ax1x.com/2021/05/08/gGxYCt.png) 可见主线程 `dtv`数组大小为 `0xf`,`TLS`块的数量为1,第三个元素 `0x7ffff7d9f6b0`指向 `TLS`块 `(0x7ffff7d9f740)-0x90`的位置 #### **接着在子线程的执行scanf前下了断点,进入子线程查看:** ![](https://z3.ax1x.com/2021/05/08/gJSNnS.png) ![](https://z3.ax1x.com/2021/05/08/gJSa7Q.png) 可以看出子线程的栈和 `TCB`都被分配在 `mmap`段(具体过程我还不太清楚,需要读 `pthread_creat`源码),此处由 `pthread_creat`源码中的 `__pthread_create_2_1->allocate_stack->_dl_allocate_tls`函数知子线程的 `dtv`由 `calloc`分配,如下图: ![](https://z3.ax1x.com/2021/05/08/gJ9RY9.png) 大概了解到这里,后续再学习。 > **参考文章:**[**A Deep dive into (implicit) Thread Local Storage (chao-tic.github.io)**](https://chao-tic.github.io/blog/2018/12/25/tls)**(写得很好,推荐阅读)** > > [**pthread_create源码分析_conansonic的博客-CSDN博客**](https://blog.csdn.net/conansonic/article/details/77487925) > > [https://code.woboq.org/userspace/glibc](https://code.woboq.org/userspace/glibc) 最后修改:2021 年 11 月 30 日 02 : 07 PM © 允许规范转载
15 条评论
看的我热血沸腾啊www.jiwenlaw.com
想想你的文章写的特别好https://www.ea55.com/
不错不错,我喜欢看
555
555
555
555
1
1
1
1
1
1
1
1