9

アセンブリで TLS 変数をインクリメントしたいのですが、アセンブリ コードでセグメンテーション エラーが発生します。コンパイラに他のレジスタやメモリを変更させたくありません。gcc の入力および出力構文を使用せずにこれを行う方法はありますか?

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

1 に答える 1

19

なんらかの理由で本当にこれを行う必要がある場合は、次のように 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 つの例はすべて異なることに注意してください。

于 2012-11-12T22:24:23.760 に答える