我想在程序集中增加TLS變量,但在彙編代碼中給出了分段錯誤。我不想讓編譯器改變任何其他寄存器或內存。有沒有辦法做到這一點,而不使用gcc輸入和輸出語法?程序集中的線程本地存儲
__thread unsigned val;
int main() {
val = 0;
asm("incl %gs:val");
return 0;
}
我想在程序集中增加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支持的每個OS和CPU組合都是不同的,而且如果您正在爲共享庫而不是可執行文件編譯(即使用-fPIC
),也會有所不同。上述構造允許編譯器爲您發出正確的代碼序列。在沒有任何額外指令的情況下可以訪問線程局部變量的情況下,地址生成將被摺疊到彙編操作中。通過圖示的方式,這裏是如何GCC 4.7的x86/Linux的編譯以上幾種不同的可能模式(我已經剝離出來一堆在所有情況下的彙編指令,爲清楚起見)...
# -S -O2 -m32 -fomit-frame-pointer
incval:
incl %gs:[email protected]
ret
# -S -O2 -m64
incval:
incl %fs:[email protected]
ret
# -S -O2 -m32 -fomit-frame-pointer -fpic
incval:
pushl %ebx
call __x86.get_pc_thunk.bx
addl $_GLOBAL_OFFSET_TABLE_, %ebx
leal [email protected](,%ebx,1), %eax
call [email protected]
incl (%eax)
popl %ebx
ret
# -S -O2 -m64 -fpic
incval:
.byte 0x66
leaq [email protected](%rip), %rdi
.value 0x6666
rex64
call [email protected]
incl (%rax)
ret
請意識到所有四個示例將不同,如果我爲x86/OSX編譯,並再次爲x86/Windows不同。
1)爲什麼不能寫''val + = 1;''而不是? 2)編寫它,用'-O2 -S'編譯,並檢查程序集輸出;你會發現你誤解了如何訪問'__thread'變量。 – zwol
@Zack你能寫一個關於這個的答案嗎? – 0x90
val ++轉換爲movl $ 0x1,%gs:0xfffffffc,但當我手動執行asm(「movl $ 1,%gs:val」)時,它將轉換爲movl $ 0x1,%gs:0x8049f14。如何在我的程序中獲取地址0xfffffffc。 – Yogi