X86-64アーキテクチャでは、2つのレジスタに特別な目的があります:FSおよびGS。Linux2.6。*では、FSレジスタはスレッドローカル情報を格納します。
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章の図を探してください)でかなりよく説明されています。
fs:0
の質問に実際に答えるには:x86_64 ABIでは、fs:0
にfs
自体が「指す」アドレスが含まれている必要があります。つまり、fs:-4
はfs: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レジスタ自体から読み取ることができるため、これは必要ありません。
では、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