アセンブリで 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
. 上記の構成により、コンパイラは正しいコード シーケンスを生成できます。追加の命令なしでスレッドローカル変数にアクセスできる場合、アドレス生成はアセンブリ操作に組み込まれます。例として、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 用にコンパイルした場合、 4 つの例はすべて異なることに注意してください。