9

Ulrich Drepper关于线程本地存储的论文概述了几种不同 cpu 架构的 TLS ABI,但我发现它不足以作为实现 TLS 的基础,原因有两个:

  1. 它省略了一些重要的拱门,如 ARM、MIPS 等(同时包括一堆完全不相关的拱门,如安腾)
  2. 更重要的是,它将很多实现细节与 ABI 混合在一起,因此很难区分互操作性需要哪些属性,哪些只是他实现的方面。

例如,i386 的唯一实际 ABI 要求是:

  • %gs:0指向一个指向自身的指针。
  • 主可执行文件的 TLS 段(如果有)必须位于距此地址的固定(通过链接器,为负)偏移处。
  • 初始加载的库的所有其他 TLS 段必须有一个运行时常量(即每个线程相同,但在不同的程序运行中不一定相同)相对于该地址的偏移量(并且动态链接器必须能够用这些偏移量)。
  • ___tls_get_addr并且__tls_get_addr函数必须以正确的语义存在,以查找任意 TLS 段。

特别是,DTV 的存在或布局不是ABI 的一部分,除主程序之外的 TLS 段的排序/布局也不是。

似乎任何使用“TLS 变体 II”的拱门都大致具有上述 ABI 要求。但是我根本不太了解“TLS 变体 I”的要求,而且从阅读资料(在 uClibc 和 glibc 中)看来,甚至可能有几个“变体 I”的变体。

有没有更好的文档我应该查看,或者熟悉 TLS 工作原理的人可以向我解释 ABI 要求吗?

4

1 回答 1

3

到目前为止,我能收集到的最好的是:

对于 TLS 变体__tls_get_addr或其他特定于架构的函数,必须存在并具有查找任何 TLS 对象的正确语义,并且任何两个 TLS 段之间的相对偏移量必须是运行时常量(每个线程的偏移量相同)。

对于 TLS 变体 II(i386 等),“线程指针寄存器”(它实际上可能不是寄存器,但可能是某种机制,例如%gs:0,甚至是内核空间的陷阱;为简单起见,尽管我们称之为寄存器)只是指向超过主可执行文件的 TLS 段的结尾,其中“刚刚超过结尾”包括四舍五入到 TLS 段对齐的下一个倍数。

对于 TLS 变体 I,“线程指针寄存器”指向主可执行文件的 TLS 段开头的某个固定偏移量。此偏移量因拱门而异。(在一些丑陋的 RISC 拱门上选择了它来最大化通过有符号 16 位偏移量可访问的 TLS 数量,这让我觉得非常无用,因为编译器无法知道重定位的偏移量是否适合 16 位,因此必须总是使用 load-upper/add 指令生成更慢、更大的 32 位偏移代码)。

据我所知,TCB、DTV 等都不是 ABI 的一部分,因为应用程序不允许访问这些结构,除了主可执行文件的部分之外,任何 TLS 段的位置也不允许ABI。在变体 I 和 II 中,将线程的实现内部信息存储在距“线程指针寄存器”的固定偏移量处是有意义的,无论哪种方式都可以安全地避免重叠 TLS 段。

于 2012-10-15T01:03:50.377 回答