【问题标题】:thread local storage in assembly程序集中的线程本地存储
【发布时间】:2012-11-12 20:20:34
【问题描述】:

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

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

【问题讨论】:

  • 1) 为什么你不能写成'val += 1;'呢? 2) 写那个,用-O2 -S 编译它,然后检查汇编输出;你会发现你搞错了如何访问__thread变量。
  • @Zack 你能写一个关于这个的答案吗?
  • val++ 转换为 movl $0x1,%gs:0xfffffffc,但是当我手动执行 asm("movl $1, %gs:val") 时,它会转换为 movl $0x1,%gs:0x8049f14。如何在我的程序中获取地址 0xfffffffc。
  • @user1428099 你应该查看gcc -S 输出来了解你需要写什么,而不是objdump -d 输出。但实际上你应该按照我刚才在回答中描述的那样去做。

标签: c gcc assembly


【解决方案1】:

如果出于某种原因你真的需要这样做,你应该通过在 C 中预加载它的地址来从汇编语言中访问一个线程局部变量,如下所示:

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

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

# -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 则又有所不同。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2023-04-08
  • 2010-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-16
  • 1970-01-01
相关资源
最近更新 更多