在程序集中线程本地存储

Yog*_*ogi 6 c assembly gcc

我想在汇编中增加TLS变量,但在汇编代码中给出了分段错误.我不想让编译器更改任何其他寄存器或内存.有没有办法在不使用gcc输入和输出语法的情况下执行此操作?

__thread unsigned val;
int main() {
  val = 0;
  asm("incl %gs:val");
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

zwo*_*wol 15

如果由于某种原因确实需要能够执行此操作,则应通过在C中预加载其地址来从汇编语言访问线程局部变量,如下所示:

__thread unsigned val;
void incval(void)
{
  unsigned *vp = &val;
  asm ("incl\t%0" : "+m" (*vp));
}
Run Code Online (Sandbox Code Playgroud)

这是因为访问线程局部变量所需的代码序列对于GCC支持的几乎所有OS和CPU组合都是不同的,并且如果您正在编译共享库而不是可执行文件(即with -fPIC),则也会有所不同.上面的结构允许编译器为您发出正确的代码序列.如果可以在没有任何额外指令的情况下访问线程局部变量,则地址生成将被折叠到汇编操作中.举例来说,这里是x86/Linux的gcc 4.7如何在几种不同的可能模式下编译上面的内容(为了清楚起见,我在所有情况下都删除了一堆汇编指令)...

# -S -O2 -m32 -fomit-frame-pointer
incval:
        incl    %gs:val@ntpoff
        ret

# -S -O2 -m64
incval:
        incl    %fs:val@tpoff
        ret

# -S -O2 -m32 -fomit-frame-pointer -fpic
incval:
        pushl   %ebx
        call    __x86.get_pc_thunk.bx
        addl    $_GLOBAL_OFFSET_TABLE_, %ebx
        leal    val@tlsgd(,%ebx,1), %eax
        call    ___tls_get_addr@PLT
        incl    (%eax)
        popl    %ebx
        ret

# -S -O2 -m64 -fpic
incval:
        .byte   0x66
        leaq    val@tlsgd(%rip), %rdi
        .value  0x6666
        rex64
        call    __tls_get_addr@PLT
        incl    (%rax)
        ret
Run Code Online (Sandbox Code Playgroud)

请注意,如果我为x86/OSX编译,那么所有四个示例都会有所不同,而对于x86/Windows则再次有所不同.

  • @Jester他们要为链接器提供一些额外的空间,这样它就可以用更有效的序列替换你看到的指令(但是由更长的指令组成),如果可能的话.有关详细信息,请参阅http://people.redhat.com/drepper/tls.pdf和http://www.x86-64.org/pipermail/discuss/2002-September/002829.html. (2认同)