我想在汇编中增加一个 TLS 变量,但在汇编代码中给出了一个段错误。我不想让编译器更改任何其他寄存器或内存。有没有办法不使用 gcc 输入和输出语法来做到这一点?
__thread unsigned val;
int main() {
val = 0;
asm("incl %gs:val");
return 0;
}
最佳答案
如果出于某种原因你真的真的需要这样做,你应该通过在 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 又会有所不同。
关于c - 程序集中的线程本地存储,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13350936/