TLS变量访问模型

TLS变量的4种访问模型

  ELF中对TLS(Thread Local Storage)变量的4种访问模型,参见:tls.pdf

gcc中使用的TLS变量访问模型

  gcc中关于TLS变量访问模型的控制,可以通过-ftls-model=model [global-dynamic / local-dynamic / initial-exec / local-exec]来指定。当使用-fPIC编译时,默认是使用了"global-dynamic"模型,而当不使用-fPIC编译时,默认是使用了"initial-exec"模型,参见:gcc.pdf
  我们将通过如下的例子展示gcc中的TLS变量访问模型。
  对于如下test_tls.c文件:

#include <errno.h>
#include <stdio.h>

int print_date();
__thread int myno = -1;

int print_date()
{
    int y = 2021;
    int m = 12;
    int d = 15;
    int ret = printf("today is %04d-%02d-%02d !\n", y, m, d);

    if(ret > 0)
      myno = ret;

    return 0;
}

  当我们使用如下编译命令编译时:

gcc test_tls.c -c -fPIC -o tls.o

  查看tls.o中的重定位信息:

readelf -r tls.o :

Relocation section '.rela.text' at offset 0x2f8 contains 6 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
......
000000000046  000a00000013 R_X86_64_TLSGD    0000000000000000 myno - 4
00000000004e  000e00000004 R_X86_64_PLT32    0000000000000000 __tls_get_addr - 4
000000000060  000a00000013 R_X86_64_TLSGD    0000000000000000 myno - 4
000000000068  000e00000004 R_X86_64_PLT32    0000000000000000 __tls_get_addr - 4

  可以看到,myno的重定位类型,为R_X86_64_TLSGD,且使用了__tls_get_addr,属于global-dynamic访问模型。而当我们将其编译链接成动态库时:

gcc -shared -o libtls.so tls.o

  查看libtls.so中的信息:

readelf -r libtls.so :

Relocation section '.rela.dyn' at offset 0x550 contains 10 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
......
000000200fe0  001000000010 R_X86_64_DTPMOD64 0000000000000000 myno + 0
000000200fe8  001000000011 R_X86_64_DTPOFF64 0000000000000000 myno + 0
......

  而当我们使用如下命令编译时:

gcc test_tls.c -c -o tls.o

  查看tls.o中的重定位信息:

readelf -r tls.o :

Relocation section '.rela.text' at offset 0x2c0 contains 4 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
......
000000000047  000a00000017 R_X86_64_TPOFF32  0000000000000000 myno + 0
000000000051  000a00000017 R_X86_64_TPOFF32  0000000000000000 myno + 0

  可以看到,myno的重定位类型,为R_X86_64_TPOFF32,属于local-exec访问模型,这与gcc手册中描述的默认值不相同。但同时,gcc手册中还有一句话:

  Note that the choice is subject to optimization: the compiler may use a more efficient model for symbols not visible outside of the translation unit, or if ‘-fpic’ is not given on the command line.

  也就是说,当gcc在编译的时候,如果发现这个这个TLS变量就是在编译单元内的,没发现它是个外部符号,并且没有-fPIC编译选项的时候,就会做优化。

  如果此时,我们有一个主程序以extern外部变量的方式引用myno,那么这个重定位类型如何?

  对于这样一段main函数代码test_main.c:

#include "test_tls.c"
extern __thread int myno;

int main()
{
    print_date();
    if(myno > 0)
        return 0;
    
    return -1;
}

  使用如下编译命令(为了保留tls_main中的重定位信息,使用 -Wl,-q 链接选项)

gcc test_main.c -I./ -L./ -ltls -o tls_main -Wl,-q

  再查看tls_main中对myno的重定位类型:

readelf -r tls_main :

Relocation section '.rela.text' at offset 0x19a0 contains 25 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
......
000000400599  004400000016 R_X86_64_GOTTPOFF 0000000000000000 myno - 4
0000004005a8  004400000016 R_X86_64_GOTTPOFF 0000000000000000 myno - 4
......
0000004005cb  004400000016 R_X86_64_GOTTPOFF 0000000000000000 myno - 4
......

  可以看到,myno的重定位类型为R_X86_64_GOTTPOFF,即是initial-exec访问模型。

修改TLS变量访问模型

  经测试发现:

  当编译选项不含-fPIC时,编译单元内的TLS变量的重定位类型会被统一优化为local-exec访问模型,且无法通过-ftls-model选项达到修改目的。如果引用外部TLS变量,该重定位类型为initial-exec访问模型。

  当编译选项含有-fPIC时,可通过-ftls-model任意切换TLS变量访问模型,例如将global-dynamic切换到initial-exec:

gcc test_tls.c -c -fPIC -ftls-model=initial-exec -o tls.o

  查看tls.o中的重定位信息:

readelf -r tls.o :

Relocation section '.rela.text' at offset 0x2c8 contains 4 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
......
000000000045  000a00000016 R_X86_64_GOTTPOFF 0000000000000000 myno - 4
000000000054  000a00000016 R_X86_64_GOTTPOFF 0000000000000000 myno - 4

  此时,将其编译链接为动态库:

gcc -shared -o libtls.so tls.o

  查看libtls.so中的信息:

readelf -r libtls.so :

Relocation section '.rela.dyn' at offset 0x4e8 contains 9 entries:
  Offset          Info           Type           Sym. Value    Sym. Name + Addend
......
000000200fe8  000f00000012 R_X86_64_TPOFF64  0000000000000000 myno + 0
......
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值