乌尔里希·德雷珀的论文on thread-local storage 概述了几种不同 cpu 架构的 TLS ABI,但我发现它不足以作为实现 TLS 的基础,原因有两个:
- 它省略了许多重要的体系结构,如 ARM、MIPS 等(同时包括一堆完全不相关的体系结构,如安腾)
- 更重要的是,它将很多实现细节与ABI混合在一起,因此很难分辨哪些属性是互操作性所必需的,哪些只是他实现的方面。
例如,i386 的唯一实际 ABI 要求是:
-
%gs:0
指向指向自身的指针。
- 主可执行文件的 TLS 段(如果有)必须位于距此地址的固定(由链接器确定为负)偏移处。
- 初始加载的库的所有其他 TLS 段必须具有相对于该地址的运行时常量(即每个线程相同,但在不同程序运行之间不一定相同)偏移量(并且动态链接器必须能够使用以下命令填充重定位):这些偏移量)。
-
___tls_get_addr
and __tls_get_addr
函数必须具有正确的语义才能查找任意 TLS 段。
特别是,DTV 的存在或布局是notABI 的一部分,主程序之外的 TLS 段的排序/布局也不是。
似乎任何使用“TLS 变体 II”的架构都大致具有上述 ABI 要求。但我根本不太理解“TLS 变体 I”的要求,而且从阅读源(uClibc 和 glibc 中)看来,“变体 I”甚至可能有几个变体。
我是否应该查看更好的文档,或者熟悉 TLS 工作原理的人可以向我解释 ABI 要求吗?
到目前为止我能收集到的最好的信息是:
对于任一 TLS 变体,__tls_get_addr
或其他特定于架构的函数必须存在并且具有用于查找任何 TLS 对象的正确语义,并且任何两个 TLS 段之间的相对偏移量必须是运行时常量(每个线程的偏移量相同)。
对于 TLS 变体 II(i386 等),“线程指针寄存器”(实际上可能不是寄存器,但可能是某种机制,例如%gs:0
甚至是进入内核空间的陷阱;为了简单起见,我们将其称为寄存器)点刚刚过去主可执行文件的 TLS 段,其中“刚刚过去”包括向上舍入到 TLS 段对齐的下一个倍数。
对于 TLS 变体 I,“线程指针寄存器”指向距开始主要可执行文件的 TLS 段。此偏移量因牙弓而异。 (在一些丑陋的 RISC 架构上选择它是为了最大化通过签名 16 位偏移量访问的 TLS 数量,这让我觉得非常无用,因为编译器无法知道重定位的偏移量是否适合 16 位,因此必须始终使用加载上部/添加指令生成较慢、较大的 32 位偏移代码)。
据我所知,关于 TCB、DTV 等的任何内容都不是 ABI 的一部分,因为不允许应用程序访问这些结构,除了主可执行文件部分之外的任何 TLS 段的位置也不是 ABI 的一部分。 ABI。在变体 I 和 II 中,将线程的实现内部信息存储在距“线程指针寄存器”的固定偏移处是有意义的,无论哪种方式都可以安全地避免重叠 TLS 段。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)