2012-11-12 32 views
6

我想在程序集中增加TLS變量,但在彙編代碼中給出了分段錯誤。我不想讓編譯器改變任何其他寄存器或內存。有沒有辦法做到這一點,而不使用gcc輸入和輸出語法?程序集中的線程本地存儲

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

1)爲什麼不能寫''val + = 1;''而不是? 2)編寫它,用'-O2 -S'編譯,並檢查程序集輸出;你會發現你誤解了如何訪問'__thread'變量。 – zwol

+1

@Zack你能寫一個關於這個的答案嗎? – 0x90

+0

val ++轉換爲movl $ 0x1,%gs:0xfffffffc,但當我手動執行asm(「movl $ 1,%gs:val」)時,它將轉換爲movl $ 0x1,%gs:0x8049f14。如何在我的程序中獲取地址0xfffffffc。 – Yogi

回答

14

如果你真的很需要能夠爲某些原因,你應該通過預加載其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不同。

+0

很好的答案。最後一個代碼中是否需要額外的前綴? – Jester

+2

@Jester它們會給鏈接器一些額外的空間,以便它可以用更高效的序列(但包含更長的指令)替換您看到的指令(如果可能的話)。血淋淋的細節見http://people.redhat.com/drepper/tls.pdf和http://www.x86-64.org/pipermail/discuss/2002-September/002829.html。 – zwol