web-dev-qa-db-ja.com

LinuxAMD64でfs / gsレジスタはどのように使用されますか?

X86-64アーキテクチャでは、2つのレジスタに特別な目的があります:FSおよびGS。Linux2.6。*では、FSレジスタはスレッドローカル情報を格納します。

  • あれは正しいですか?
  • Fs:0には何が格納されていますか?このコンテンツを説明するC構造体はありますか?
  • では、GSの使用は何ですか?
36
TheRealNeo

X86-64には TLSエントリ があり、そのうちの2つは FSおよびGS 、FSはglibcによって内部的に使用されます( IA32はどうやら FSはWineで使用され、GSはglibcで使用されます )。

Glibcは、TLSエントリポイントを _struct pthread_ に設定します。これには、スレッド化のためのいくつかの内部構造が含まれています。 Glibcは通常、_struct pthread_変数をpdと呼びます。これは、おそらくpthread記述子の場合です。

X86-64では、_struct pthread_は _tcbhead_t_ で始まります(これはアーキテクチャによって異なります。マクロを参照してください _TLS_DTV_AT_TP_ および- _TLS_TCB_AT_TP_ )。このスレッド制御ブロックヘッダーAFAIUには、単一のスレッドがある場合でも必要ないくつかのフィールドが含まれています。 DTVは動的スレッドベクトルであり、dlopen()を介してロードされたDSOのTLSブロックへのポインターが含まれています。 TCBの前後には、(プログラムの)ロード時にリンクされた実行可能ファイルとDSOの静的TLSブロックがあります。 TCBとDTVは、 lrich DrepperのTLSドキュメント (第3章の図を探してください)でかなりよく説明されています。

39
ninjalj

fs:0の質問に実際に答えるには:x86_64 ABIでは、fs:0fs自体が「指す」アドレスが含まれている必要があります。つまり、fs:-4fs:0 - 4に格納されている値をロードします。この機能が必要なのは、カーネルコードを経由しないと、fsが指すアドレスを簡単に取得できないためです。したがって、アドレスをfs:0に格納すると、スレッドローカルストレージでの作業がはるかに効率的になります。

スレッドローカル変数のアドレスを取得すると、これが実際に動作していることがわかります。

static __thread int test = 0;

int *f(void) {
    return &test;
}

int g(void) {
    return test;
}

にコンパイルされます

f:
    movq    %fs:0, %rax
    leaq    -4(%rax), %rax
    retq

g:
    movl    %fs:-4, %eax
    retq

i686も同じように動作しますが、%gsを使用します。 aarch64では、アドレスはtlsレジスタ自体から読み取ることができるため、これは必要ありません。

16
MuhKarma

では、GSの使用は何ですか?

x86_64 Linuxカーネルは、システムコール用のカーネルスペーススタックを取得する効率的な方法としてGSレジスタを使用します。

GSレジスタは、CPUごとの領域のベースアドレスを格納します。カーネルスペーススタックを取得するには、entry_SYSCALL_64で

movq    PER_CPU_VAR(cpu_current_top_of_stack), %rsp

PER_CPU_VARを展開すると、次のようになります。

movq    %gs:cpu_current_top_of_stack, %rsp
2
firo