在程序集中线程本地存储

时间:2012-11-12 20:20:34

标签: c gcc assembly

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

__thread unsigned val;
int main() {
  val = 0;
  asm("incl %gs:val");
  return 0;
}

1 个答案:

答案 0 :(得分:15)

如果您确实需要能够出于某种原因这样做,您应该通过在C中预加载其地址来从汇编语言访问线程局部变量,如下所示:

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

这是因为访问线程局部变量所需的代码序列对于GCC支持的几乎所有OS和CPU组合都是不同的,并且如果您正在编译共享库而不是可执行文件(即, -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

如果我为x86 / OSX编译了,那么所有四个例子都会有所不同,而x86 / Windows则再次有所不同。