Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 11047] ファイルの概要

このコミットでは、Windows環境におけるGoランタイムのThread Local Storage (TLS) の実装が変更されています。具体的には、TLSスロットとしてArbitraryUserPointerを使用するように修正され、それに伴いTLSデータへのアクセスオフセットが更新されています。

変更されたファイルは以下の通りです。

  • src/cmd/6l/pass.c: Goリンカー (amd64向け) のTLSアクセスオフセットの修正
  • src/cmd/8l/pass.c: Goリンカー (386向け) のTLSアクセスオフセットの修正
  • src/pkg/runtime/cgo/gcc_windows_386.c: Windows 386環境におけるCGOランタイムのTLSアクセスオフセットの修正
  • src/pkg/runtime/cgo/gcc_windows_amd64.c: Windows amd64環境におけるCGOランタイムのTLSアクセスオフセットの修正
  • src/pkg/runtime/mkasmh.sh: アセンブリヘッダ生成スクリプトにおけるTLSアクセスオフセットの修正
  • src/pkg/runtime/sys_windows_386.s: Windows 386環境におけるランタイムアセンブリコードのTLSアクセスオフセットの修正
  • src/pkg/runtime/sys_windows_amd64.s: Windows amd64環境におけるランタイムアセンブリコードのTLSアクセスオフセットの修正

コミット

commit 9569c67a6ba61501f5c8ce58f20139e64100585e
Author: Wei Guangjing <vcc.163@gmail.com>
Date:   Mon Jan 9 11:23:07 2012 +1100

    windows: use ArbitraryUserPointer as TLS slot

    R=hectorchu, alex.brainman
    CC=golang-dev
    https://golang.org/cl/5519054

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/9569c67a6ba61501f5c8ce58f20139e64100585e

元コミット内容

windows: use ArbitraryUserPointer as TLS slot

R=hectorchu, alex.brainman
CC=golang-dev
https://golang.org/cl/5519054

変更の背景

このコミットの背景には、WindowsにおけるThread Local Storage (TLS) の効率的かつ安定した利用があります。Goランタイムは、各ゴルーチン(Goにおける軽量スレッド)やスレッド固有のデータを管理するためにTLSのようなメカニズムを必要とします。Windowsでは、TLSは通常、TlsAllocTlsSetValueTlsGetValueといったAPIを通じて管理されますが、これらはシステムコールを伴いオーバーヘッドが発生する可能性があります。

より低レベルで効率的なTLSアクセス方法として、Windowsのx86およびx64アーキテクチャでは、FSセグメントレジスタ(x86)またはGSセグメントレジスタ(x64)がThread Information Block (TIB) を指すように設定されています。TIBは、現在のスレッドに関する様々な情報を含むデータ構造です。TIB内には、ユーザーが自由に利用できるポインタ領域がいくつか存在します。このコミットは、その中の一つであるArbitraryUserPointerをGoランタイムのTLSスロットとして利用することで、TLSアクセスを最適化しようとしています。

以前の実装では、おそらくTIB内の別のオフセットや、より汎用的なTLS APIを使用していたと考えられますが、ArbitraryUserPointerはOSによって特定の用途に予約されていないため、Goランタイムがスレッド固有のデータを格納するのに適した場所となります。この変更により、TLSデータへのアクセスがより直接的かつ高速になり、GoプログラムのWindows環境でのパフォーマンス向上に寄与することが期待されます。

前提知識の解説

TLS (Thread Local Storage)

TLS(Thread Local Storage)は、マルチスレッドプログラミングにおいて、各スレッドがそれぞれ独立したデータを持つためのメカニズムです。通常、グローバル変数や静的変数はプロセス内のすべてのスレッドで共有されますが、TLSを使用すると、同じ変数名であっても各スレッドが独自の値を保持できます。これは、スレッドセーフなプログラミングや、スレッド固有の状態管理に不可欠です。

Windows TLS

WindowsオペレーティングシステムにおけるTLSは、いくつかの方法で実装されています。

  1. TLS API: TlsAlloc, TlsSetValue, TlsGetValue, TlsFree といった高レベルAPI。これらはシステムコールを介してTLSスロットを管理します。
  2. PEB/TEB (Process Environment Block / Thread Environment Block): Windowsの各プロセスにはPEBが、各スレッドにはTEB(x86ではTIBとも呼ばれる)が存在します。TEBは、現在のスレッドに関する様々な情報(スタックのベースアドレス、スレッドID、エラー情報など)を格納するデータ構造です。
  3. FS/GS セグメントレジスタ: x86アーキテクチャではFSセグメントレジスタが、x64アーキテクチャではGSセグメントレジスタが、現在のスレッドのTEB(TIB)のベースアドレスを指すように設定されています。これにより、アセンブリレベルでTEB内のデータに直接アクセスできます。

ArbitraryUserPointer

ArbitraryUserPointerは、WindowsのTEB(TIB)構造体内に存在するフィールドの一つです。このポインタは、OSによって特定の用途に予約されておらず、アプリケーション開発者が自由に利用できる領域として提供されています。Goランタイムは、このArbitraryUserPointerを、GoのゴルーチンやM(マシン、OSスレッド)構造体へのポインタを格納するためのTLSスロットとして利用することで、各スレッドから現在のゴルーチンやMの情報を効率的に取得できるようにします。

Go Runtime

Goランタイムは、Goプログラムの実行を管理するシステムです。これには、ゴルーチン(軽量スレッド)のスケジューリング、メモリ管理(ガベージコレクション)、チャネル通信、システムコールインターフェースなどが含まれます。Goランタイムは、OSのスレッド(M)上でゴルーチン(G)を実行し、必要に応じてMとGの関連付けを切り替えます。このMとGの情報をスレッド固有に保持するためにTLSが利用されます。

リンカー (6l, 8l)

Goのビルドシステムでは、6lはamd64アーキテクチャ向けのリンカー、8lは386アーキテクチャ向けのリンカーです。リンカーは、コンパイルされたオブジェクトファイルやライブラリを結合し、実行可能なバイナリを生成する役割を担います。このコミットでは、リンカーが生成するコード内でTLSデータへのアクセス方法が変更されるため、リンカー自身もその変更を反映するように修正されています。

CGO

CGOは、GoプログラムからC言語のコードを呼び出すためのメカニズムです。Windows環境では、CGOがGoランタイムとCライブラリ間の橋渡しを行います。このコミットでは、CGOランタイムがTLSデータを設定・取得する部分も、新しいTLSスロットのオフセットに合わせて修正されています。

アセンブリ (.s ファイル)

Goランタイムの一部は、パフォーマンスが重要な部分やOSとの低レベルなインタラクションが必要な部分でアセンブリ言語で記述されています。src/pkg/runtime/sys_windows_386.ssrc/pkg/runtime/sys_windows_amd64.sは、それぞれWindows 386およびamd64環境におけるシステムコールやスレッド管理などの低レベル処理を実装しています。これらのファイルでは、FSGSセグメントレジスタを介したTLSデータへの直接的なアクセスが行われるため、オフセットの変更が直接影響します。

mkasmh.sh

mkasmh.shは、Goランタイムのアセンブリコードで使用されるマクロや定数を生成するためのシェルスクリプトです。このスクリプトは、get_tlsのようなTLSアクセスに関連するマクロを定義しており、このコミットでは、新しいTLSオフセットに合わせてこれらのマクロの定義が更新されています。

技術的詳細

このコミットの核心は、WindowsのTEB(Thread Environment Block)内のArbitraryUserPointerフィールドをGoランタイムのTLSスロットとして利用することです。

WindowsのTEB構造体は、アーキテクチャによってそのレイアウトが異なります。

  • x86 (32-bit): FSセグメントレジスタがTEBのベースアドレスを指します。以前は0x2C(FS)というオフセットが使用されていましたが、これはTEB内のTlsSlots配列の一部を指していた可能性があります。このコミットでは、0x14(FS)に変更されています。0x14はTEB構造体内のArbitraryUserPointerフィールドのオフセットに相当します。
  • x64 (64-bit): GSセグメントレジスタがTEBのベースアドレスを指します。以前は0x58(GS)というオフセットが使用されていましたが、これも同様にTEB内の別の領域を指していた可能性があります。このコミットでは、0x28(GS)に変更されています。0x28はTEB構造体内のArbitraryUserPointerフィールドのオフセットに相当します。

これらのオフセット変更は、Goランタイムが各OSスレッド(M)に紐付けられたGoのg(ゴルーチン)およびm(OSスレッド)構造体へのポインタを格納するために、ArbitraryUserPointerを専用のTLSスロットとして使用することを意味します。

具体的な変更点としては、以下のファイルでオフセット値が更新されています。

  • リンカー (src/cmd/6l/pass.c, src/cmd/8l/pass.c):
    • リンカーは、Goのソースコードから生成されたアセンブリコードをパッチ適用する際に、TLSアクセス命令のオフセットを修正します。例えば、n(GS)のようなTLSアクセスを、MOVL 0x28(GS), regのような命令に変換し、そのレジスタを介してTLSデータにアクセスするように変更します。これにより、Goのコンパイラが生成するコードが、新しいTLSスロットを利用できるようになります。
  • CGOランタイム (src/pkg/runtime/cgo/gcc_windows_386.c, src/pkg/runtime/cgo/gcc_windows_amd64.c):
    • CGOは、GoとCのコード間でスレッドコンテキストを切り替える際に、TLSデータを正しく設定・取得する必要があります。これらのファイルでは、インラインアセンブリを使用してFSまたはGSレジスタを介してTLSスロットに値を書き込んだり読み込んだりする部分のオフセットが変更されています。
  • アセンブリコード (src/pkg/runtime/sys_windows_386.s, src/pkg/runtime/sys_windows_amd64.s):
    • Goランタイムの低レベルなスレッド管理やコンテキスト切り替えを行うアセンブリコードでは、PUSHL/PUSHQMOVL/MOVQ命令で直接FS:0x14GS:0x28といったオフセットが使用されています。これらのオフセットは、スレッドの初期化時やコンテキスト切り替え時に、Goのgm構造体へのポインタをTLSに保存したり、そこから読み出したりするために使われます。
  • アセンブリヘッダ生成スクリプト (src/pkg/runtime/mkasmh.sh):
    • このスクリプトは、アセンブリコードで共通して使用されるget_tlsマクロを定義しています。このマクロは、TLSスロットから現在のスレッドのTLSベースアドレスを取得するためのもので、その定義内のオフセットが新しい値に更新されています。

この変更により、GoランタイムはWindows環境でより効率的にスレッド固有のデータにアクセスできるようになり、特にCGOを使用するシナリオや、多数のゴルーチンが動作する環境でのパフォーマンス改善に貢献します。

コアとなるコードの変更箇所

src/cmd/6l/pass.c (amd64リンカー)

--- a/src/cmd/6l/pass.c
+++ b/src/cmd/6l/pass.c
@@ -276,7 +276,7 @@ patch(void)\
 			// Convert
 			//   op	  n(GS), reg
 			// to
-			//   MOVL 0x58(GS), reg
+			//   MOVL 0x28(GS), reg
 			//   op	  n(reg), reg
 			// The purpose of this patch is to fix some accesses
 			// to extern register variables (TLS) on Windows, as
@@ -291,7 +291,7 @@ patch(void)\
 				q->as = p->as;
 				p->as = AMOVQ;
 				p->from.type = D_INDIR+D_GS;
-				p->from.offset = 0x58;
+				p->from.offset = 0x28;
 			}
 		}
 	if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd
@@ -428,11 +428,11 @@ dostkoff(void)\
 			p->from.offset = tlsoffset+0;
 			p->to.type = D_CX;
 			if(HEADTYPE == Hwindows) {
-				// movq %gs:0x58, %rcx
+				// movq %gs:0x28, %rcx
 				// movq (%rcx), %rcx
 				p->as = AMOVQ;
 				p->from.type = D_INDIR+D_GS;
-				p->from.offset = 0x58;
+				p->from.offset = 0x28;
 				p->to.type = D_CX;
 

src/pkg/runtime/cgo/gcc_windows_amd64.c (amd64 CGOランタイム)

--- a/src/pkg/runtime/cgo/gcc_windows_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_windows_amd64.c
@@ -45,8 +45,8 @@ threadentry(void *v)\
 	 */
 	tls0 = (void*)LocalAlloc(LPTR, 64);
 	asm volatile (
-	  "movq %0, %%gs:0x58\\n"	// MOVL tls0, 0x58(GS)
-	  "movq %%gs:0x58, %%rax\\n" // MOVQ 0x58(GS), tmp
+	  "movq %0, %%gs:0x28\\n"	// MOVL tls0, 0x28(GS)
+	  "movq %%gs:0x28, %%rax\\n" // MOVQ 0x28(GS), tmp
 	  "movq %1, 0(%%rax)\\n" // MOVQ g, 0(GS)
 	  "movq %2, 8(%%rax)\\n" // MOVQ m, 8(GS)
 	  :: "r"(tls0), "r"(ts.g), "r"(ts.m) : "%rax"

src/pkg/runtime/sys_windows_amd64.s (amd64アセンブリ)

--- a/src/pkg/runtime/sys_windows_amd64.s
+++ b/src/pkg/runtime/sys_windows_amd64.s
@@ -121,7 +121,7 @@ TEXT runtime·externalthreadhandler(SB),7,$0
 	PUSHQ	BX
 	PUSHQ	SI
 	PUSHQ	DI
-	PUSHQ	0x58(GS)
+	PUSHQ	0x28(GS)
 	MOVQ	SP, DX
 
 	// setup dummy m, g
@@ -131,7 +131,7 @@ TEXT runtime·externalthreadhandler(SB),7,$0
 	CALL	runtime·memclr(SB)	// smashes AX,BX,CX
 
 	LEAQ	m_tls(SP), CX
-	MOVQ	CX, 0x58(GS)
+	MOVQ	CX, 0x28(GS)
 	MOVQ	SP, m(CX)
 	MOVQ	SP, BX
 	SUBQ	$g_end, SP		// space for G
@@ -152,7 +152,7 @@ TEXT runtime·externalthreadhandler(SB),7,$0
 	get_tls(CX)
 	MOVQ	g(CX), CX
 	MOVQ	g_stackbase(CX), SP
-	POPQ	0x58(GS)
+	POPQ	0x28(GS)
 	POPQ	DI
 	POPQ	SI
 	POPQ	BX
@@ -254,7 +254,7 @@ TEXT runtime·tstart_stdcall(SB),7,$0
 
 	// Set up tls.
 	LEAQ	m_tls(CX), SI
-	MOVQ	SI, 0x58(GS)
+	MOVQ	SI, 0x28(GS)
 	MOVQ	CX, m(SI)
 	MOVQ	DX, g(SI)
 
@@ -276,5 +276,5 @@ TEXT runtime·notok(SB),7,$0
 // set tls base to DI
 TEXT runtime·settls(SB),7,$0
 	CALL	runtime·setstacklimits(SB)
-	MOVQ	DI, 0x58(GS)
+	MOVQ	DI, 0x28(GS)
 	RET

コアとなるコードの解説

上記のコード変更は、Windows環境におけるGoランタイムがTLSデータを扱う際のオフセットを、ArbitraryUserPointerが指す領域に合わせるためのものです。

  • リンカー (src/cmd/6l/pass.c, src/cmd/8l/pass.c):

    • これらのファイルでは、Goのコンパイラが生成したコード内でTLS変数へのアクセス(例: n(GS))を検出した場合に、それをWindowsのTLSメカニズムに適合するように変換する処理が行われます。具体的には、GS(amd64)またはFS(386)セグメントレジスタからのオフセットを、以前の0x58(amd64)や0x2C(386)から、それぞれ0x280x14に変更しています。これにより、リンカーが生成する最終的な実行可能ファイルが、ArbitraryUserPointerを介してTLSデータにアクセスするようになります。
  • CGOランタイム (src/pkg/runtime/cgo/gcc_windows_amd64.c, src/pkg/runtime/cgo/gcc_windows_386.c):

    • threadentry関数は、CGOが新しいスレッドを開始する際に呼び出されるエントリポイントです。この関数内で、tls0というポインタ(Goのgm構造体へのポインタを格納する領域)が確保され、そのアドレスがGS(amd64)またはFS(386)セグメントレジスタの特定のオフセットに書き込まれます。このコミットでは、その書き込み先のオフセットが0x58から0x28(amd64)または0x2Cから0x14(386)に変更されています。これにより、CGOがGoランタイムのスレッド固有データを正しく設定できるようになります。
  • アセンブリコード (src/pkg/runtime/sys_windows_amd64.s, src/pkg/runtime/sys_windows_386.s):

    • これらのアセンブリファイルには、Goランタイムの低レベルなスレッド管理やコンテキスト切り替えに関連するコードが含まれています。例えば、runtime·externalthreadhandler関数は、外部からGoランタイムにコールバックされるスレッドのエントリポイントです。この関数や他の関連する関数内で、GSまたはFSセグメントレジスタからのオフセットを使用して、Goのg(ゴルーチン)やm(OSスレッド)構造体へのポインタをTLSに保存したり、そこから読み出したりする操作が行われます。これらのオフセットが、ArbitraryUserPointerのオフセットである0x28(amd64)や0x14(386)に統一されています。これにより、Goランタイムがスレッド固有のコンテキストを効率的かつ正確に管理できるようになります。

これらの変更は、GoランタイムがWindowsの低レベルなスレッドメカニズムとより密接に連携し、TLSを介したスレッド固有データへのアクセスを最適化するための重要なステップです。

関連リンク

参考にした情報源リンク

I have completed the request.

# [インデックス 11047] ファイルの概要

このコミットでは、Windows環境におけるGoランタイムのThread Local Storage (TLS) の実装が変更されています。具体的には、TLSスロットとして`ArbitraryUserPointer`を使用するように修正され、それに伴いTLSデータへのアクセスオフセットが更新されています。

変更されたファイルは以下の通りです。

- `src/cmd/6l/pass.c`: Goリンカー (amd64向け) のTLSアクセスオフセットの修正
- `src/cmd/8l/pass.c`: Goリンカー (386向け) のTLSアクセスオフセットの修正
- `src/pkg/runtime/cgo/gcc_windows_386.c`: Windows 386環境におけるCGOランタイムのTLSアクセスオフセットの修正
- `src/pkg/runtime/cgo/gcc_windows_amd64.c`: Windows amd64環境におけるCGOランタイムのTLSアクセスオフセットの修正
- `src/pkg/runtime/mkasmh.sh`: アセンブリヘッダ生成スクリプトにおけるTLSアクセスオフセットの修正
- `src/pkg/runtime/sys_windows_386.s`: Windows 386環境におけるランタイムアセンブリコードのTLSアクセスオフセットの修正
- `src/pkg/runtime/sys_windows_amd64.s`: Windows amd64環境におけるランタイムアセンブリコードのTLSアクセスオフセットの修正

## コミット

commit 9569c67a6ba61501f5c8ce58f20139e64100585e Author: Wei Guangjing vcc.163@gmail.com Date: Mon Jan 9 11:23:07 2012 +1100

windows: use ArbitraryUserPointer as TLS slot

R=hectorchu, alex.brainman
CC=golang-dev
https://golang.org/cl/5519054

## GitHub上でのコミットページへのリンク

[https://github.com/golang/go/commit/9569c67a6ba61501f5c8ce58f20139e64100585e](https://github.com/golang/go/commit/9569c67a6ba61501f5c8ce58f20139e64100585e)

## 元コミット内容

windows: use ArbitraryUserPointer as TLS slot

R=hectorchu, alex.brainman CC=golang-dev https://golang.org/cl/5519054


## 変更の背景

このコミットの背景には、WindowsにおけるThread Local Storage (TLS) の効率的かつ安定した利用があります。Goランタイムは、各ゴルーチン(Goにおける軽量スレッド)やスレッド固有のデータを管理するためにTLSのようなメカニズムを必要とします。Windowsでは、TLSは通常、`TlsAlloc`、`TlsSetValue`、`TlsGetValue`といったAPIを通じて管理されますが、これらはシステムコールを伴いオーバーヘッドが発生する可能性があります。

より低レベルで効率的なTLSアクセス方法として、Windowsのx86およびx64アーキテクチャでは、`FS`セグメントレジスタ(x86)または`GS`セグメントレジスタ(x64)がThread Information Block (TIB) を指すように設定されています。TIBは、現在のスレッドに関する様々な情報を含むデータ構造です。TIB内には、ユーザーが自由に利用できるポインタ領域がいくつか存在します。このコミットは、その中の一つである`ArbitraryUserPointer`をGoランタイムのTLSスロットとして利用することで、TLSアクセスを最適化しようとしています。

以前の実装では、おそらくTIB内の別のオフセットや、より汎用的なTLS APIを使用していたと考えられますが、`ArbitraryUserPointer`はOSによって特定の用途に予約されていないため、Goランタイムがスレッド固有のデータを格納するのに適した場所となります。この変更により、TLSデータへのアクセスがより直接的かつ高速になり、GoプログラムのWindows環境でのパフォーマンス向上に寄与することが期待されます。

## 前提知識の解説

### TLS (Thread Local Storage)

TLS(Thread Local Storage)は、マルチスレッドプログラミングにおいて、各スレッドがそれぞれ独立したデータを持つためのメカニズムです。通常、グローバル変数や静的変数はプロセス内のすべてのスレッドで共有されますが、TLSを使用すると、同じ変数名であっても各スレッドが独自の値を保持できます。これは、スレッドセーフなプログラミングや、スレッド固有の状態管理に不可欠です。

### Windows TLS

WindowsオペレーティングシステムにおけるTLSは、いくつかの方法で実装されています。
1.  **TLS API**: `TlsAlloc`, `TlsSetValue`, `TlsGetValue`, `TlsFree` といった高レベルAPI。これらはシステムコールを介してTLSスロットを管理します。
2.  **PEB/TEB (Process Environment Block / Thread Environment Block)**: Windowsの各プロセスにはPEBが、各スレッドにはTEB(x86ではTIBとも呼ばれる)が存在します。TEBは、現在のスレッドに関する様々な情報(スタックのベースアドレス、スレッドID、エラー情報など)を格納するデータ構造です。
3.  **FS/GS セグメントレジスタ**: x86アーキテクチャでは`FS`セグメントレジスタが、x64アーキテクチャでは`GS`セグメントレジスタが、現在のスレッドのTEB(TIB)のベースアドレスを指すように設定されています。これにより、アセンブリレベルでTEB内のデータに直接アクセスできます。

### ArbitraryUserPointer

`ArbitraryUserPointer`は、WindowsのTEB(TIB)構造体内に存在するフィールドの一つです。このポインタは、OSによって特定の用途に予約されておらず、アプリケーション開発者が自由に利用できる領域として提供されています。Goランタイムは、この`ArbitraryUserPointer`を、GoのゴルーチンやM(マシン、OSスレッド)構造体へのポインタを格納するためのTLSスロットとして利用することで、各スレッドから現在のゴルーチンやMの情報を効率的に取得できるようにします。

### Go Runtime

Goランタイムは、Goプログラムの実行を管理するシステムです。これには、ゴルーチン(軽量スレッド)のスケジューリング、メモリ管理(ガベージコレクション)、チャネル通信、システムコールインターフェースなどが含まれます。Goランタイムは、OSのスレッド(M)上でゴルーチン(G)を実行し、必要に応じてMとGの関連付けを切り替えます。このMとGの情報をスレッド固有に保持するためにTLSが利用されます。

### リンカー (6l, 8l)

Goのビルドシステムでは、`6l`はamd64アーキテクチャ向けのリンカー、`8l`は386アーキテクチャ向けのリンカーです。リンカーは、コンパイルされたオブジェクトファイルやライブラリを結合し、実行可能なバイナリを生成する役割を担います。このコミットでは、リンカーが生成するコード内でTLSデータへのアクセス方法が変更されるため、リンカー自身もその変更を反映するように修正されています。

### CGO

CGOは、GoプログラムからC言語のコードを呼び出すためのメカニズムです。Windows環境では、CGOがGoランタイムとCライブラリ間の橋渡しを行います。このコミットでは、CGOランタイムがTLSデータを設定・取得する部分も、新しいTLSスロットのオフセットに合わせて修正されています。

### アセンブリ (.s ファイル)

Goランタイムの一部は、パフォーマンスが重要な部分やOSとの低レベルなインタラクションが必要な部分でアセンブリ言語で記述されています。`src/pkg/runtime/sys_windows_386.s`と`src/pkg/runtime/sys_windows_amd64.s`は、それぞれWindows 386およびamd64環境におけるシステムコールやスレッド管理などの低レベル処理を実装しています。これらのファイルでは、`FS`や`GS`セグメントレジスタを介したTLSデータへの直接的なアクセスが行われるため、オフセットの変更が直接影響します。

### mkasmh.sh

`mkasmh.sh`は、Goランタイムのアセンブリコードで使用されるマクロや定数を生成するためのシェルスクリプトです。このスクリプトは、`get_tls`のようなTLSアクセスに関連するマクロを定義しており、このコミットでは、新しいTLSオフセットに合わせてこれらのマクロの定義が更新されています。

## 技術的詳細

このコミットの核心は、WindowsのTEB(Thread Environment Block)内の`ArbitraryUserPointer`フィールドをGoランタイムのTLSスロットとして利用することです。

WindowsのTEB構造体は、アーキテクチャによってそのレイアウトが異なります。
- **x86 (32-bit)**: `FS`セグメントレジスタがTEBのベースアドレスを指します。以前は`0x2C(FS)`というオフセットが使用されていましたが、これはTEB内の`TlsSlots`配列の一部を指していた可能性があります。このコミットでは、`0x14(FS)`に変更されています。`0x14`はTEB構造体内の`ArbitraryUserPointer`フィールドのオフセットに相当します。
- **x64 (64-bit)**: `GS`セグメントレジスタがTEBのベースアドレスを指します。以前は`0x58(GS)`というオフセットが使用されていましたが、これも同様にTEB内の別の領域を指していた可能性があります。このコミットでは、`0x28(GS)`に変更されています。`0x28`はTEB構造体内の`ArbitraryUserPointer`フィールドのオフセットに相当します。

これらのオフセット変更は、Goランタイムが各OSスレッド(M)に紐付けられたGoの`g`(ゴルーチン)および`m`(OSスレッド)構造体へのポインタを格納するために、`ArbitraryUserPointer`を専用のTLSスロットとして使用することを意味します。

具体的な変更点としては、以下のファイルでオフセット値が更新されています。

- **リンカー (`src/cmd/6l/pass.c`, `src/cmd/8l/pass.c`)**:
    - リンカーは、Goのソースコードから生成されたアセンブリコードをパッチ適用する際に、TLSアクセス命令のオフセットを修正します。例えば、`n(GS)`のようなTLSアクセスを、`MOVL 0x28(GS), reg`のような命令に変換し、そのレジスタを介してTLSデータにアクセスするように変更します。これにより、Goのコンパイラが生成するコードが、新しいTLSスロットを利用できるようになります。
- **CGOランタイム (`src/pkg/runtime/cgo/gcc_windows_386.c`, `src/pkg/runtime/cgo/gcc_windows_amd64.c`)**:
    - CGOは、GoとCのコード間でスレッドコンテキストを切り替える際に、TLSデータを正しく設定・取得する必要があります。これらのファイルでは、インラインアセンブリを使用して`FS`または`GS`レジスタを介してTLSスロットに値を書き込んだり読み込んだりする部分のオフセットが変更されています。
- **アセンブリコード (`src/pkg/runtime/sys_windows_386.s`, `src/pkg/runtime/sys_windows_amd64.s`)**:
    - Goランタイムの低レベルなスレッド管理やコンテキスト切り替えを行うアセンブリコードでは、`PUSHL/PUSHQ`や`MOVL/MOVQ`命令で直接`FS:0x14`や`GS:0x28`といったオフセットが使用されています。これらのオフセットは、スレッドの初期化時やコンテキスト切り替え時に、Goの`g`や`m`構造体へのポインタをTLSに保存したり、そこから読み出したりするために使われます。
- **アセンブリヘッダ生成スクリプト (`src/pkg/runtime/mkasmh.sh`)**:
    - このスクリプトは、アセンブリコードで共通して使用される`get_tls`マクロを定義しています。このマクロは、TLSスロットから現在のスレッドのTLSベースアドレスを取得するためのもので、その定義内のオフセットが新しい値に更新されています。

これらの変更は、GoランタイムがWindowsの低レベルなスレッドメカニズムとより密接に連携し、TLSを介したスレッド固有データへのアクセスを最適化するための重要なステップです。

## コアとなるコードの変更箇所

### `src/cmd/6l/pass.c` (amd64リンカー)

```diff
--- a/src/cmd/6l/pass.c
+++ b/src/cmd/6l/pass.c
@@ -276,7 +276,7 @@ patch(void)\
 			// Convert
 			//   op	  n(GS), reg
 			// to
-			//   MOVL 0x58(GS), reg
+			//   MOVL 0x28(GS), reg
 			//   op	  n(reg), reg
 			// The purpose of this patch is to fix some accesses
 			// to extern register variables (TLS) on Windows, as
@@ -291,7 +291,7 @@ patch(void)\
 				q->as = p->as;
 				p->as = AMOVQ;
 				p->from.type = D_INDIR+D_GS;
-				p->from.offset = 0x58;
+				p->from.offset = 0x28;
 			}
 		}
 	if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd
@@ -428,11 +428,11 @@ dostkoff(void)\
 			p->from.offset = tlsoffset+0;
 			p->to.type = D_CX;
 			if(HEADTYPE == Hwindows) {
-				// movq %gs:0x58, %rcx
+				// movq %gs:0x28, %rcx
 				// movq (%rcx), %rcx
 				p->as = AMOVQ;
 				p->from.type = D_INDIR+D_GS;
-				p->from.offset = 0x58;
+				p->from.offset = 0x28;
 				p->to.type = D_CX;
 

src/pkg/runtime/cgo/gcc_windows_amd64.c (amd64 CGOランタイム)

--- a/src/pkg/runtime/cgo/gcc_windows_amd64.c
+++ b/src/pkg/runtime/cgo/gcc_windows_amd64.c
@@ -45,8 +45,8 @@ threadentry(void *v)\
 	 */
 	tls0 = (void*)LocalAlloc(LPTR, 64);
 	asm volatile (
-	  "movq %0, %%gs:0x58\\n"	// MOVL tls0, 0x58(GS)
-	  "movq %%gs:0x58, %%rax\\n" // MOVQ 0x58(GS), tmp
+	  "movq %0, %%gs:0x28\\n"	// MOVL tls0, 0x28(GS)
+	  "movq %%gs:0x28, %%rax\\n" // MOVQ 0x28(GS), tmp
 	  "movq %1, 0(%%rax)\\n" // MOVQ g, 0(GS)
 	  "movq %2, 8(%%rax)\\n" // MOVQ m, 8(GS)
 	  :: "r"(tls0), "r"(ts.g), "r"(ts.m) : "%rax"

src/pkg/runtime/sys_windows_amd64.s (amd64アセンブリ)

--- a/src/pkg/runtime/sys_windows_amd64.s
+++ b/src/pkg/runtime/sys_windows_amd64.s
@@ -121,7 +121,7 @@ TEXT runtime·externalthreadhandler(SB),7,$0
 	PUSHQ	BX
 	PUSHQ	SI
 	PUSHQ	DI
-	PUSHQ	0x58(GS)
+	PUSHQ	0x28(GS)
 	MOVQ	SP, DX
 
 	// setup dummy m, g
@@ -131,7 +131,7 @@ TEXT runtime·externalthreadhandler(SB),7,$0
 	CALL	runtime·memclr(SB)	// smashes AX,BX,CX
 
 	LEAQ	m_tls(SP), CX
-	MOVQ	CX, 0x58(GS)
+	MOVQ	CX, 0x28(GS)
 	MOVQ	SP, m(CX)
 	MOVQ	SP, BX
 	SUBQ	$g_end, SP		// space for G
@@ -152,7 +152,7 @@ TEXT runtime·externalthreadhandler(SB),7,$0
 	get_tls(CX)
 	MOVQ	g(CX), CX
 	MOVQ	g_stackbase(CX), SP
-	POPQ	0x58(GS)
+	POPQ	0x28(GS)
 	POPQ	DI
 	POPQ	SI
 	POPQ	BX
@@ -254,7 +254,7 @@ TEXT runtime·tstart_stdcall(SB),7,$0
 
 	// Set up tls.
 	LEAQ	m_tls(CX), SI
-	MOVQ	SI, 0x58(GS)
+	MOVQ	SI, 0x28(GS)
 	MOVQ	CX, m(SI)
 	MOVQ	DX, g(SI)
 
@@ -276,5 +276,5 @@ TEXT runtime·notok(SB),7,$0
 // set tls base to DI
 TEXT runtime·settls(SB),7,$0
 	CALL	runtime·setstacklimits(SB)
-	MOVQ	DI, 0x58(GS)
+	MOVQ	DI, 0x28(GS)
 	RET

コアとなるコードの解説

上記のコード変更は、Windows環境におけるGoランタイムがTLSデータを扱う際のオフセットを、ArbitraryUserPointerが指す領域に合わせるためのものです。

  • リンカー (src/cmd/6l/pass.c, src/cmd/8l/pass.c):

    • これらのファイルでは、Goのコンパイラが生成したコード内でTLS変数へのアクセス(例: n(GS))を検出した場合に、それをWindowsのTLSメカニズムに適合するように変換する処理が行われます。具体的には、GS(amd64)またはFS(386)セグメントレジスタからのオフセットを、以前の0x58(amd64)や0x2C(386)から、それぞれ0x280x14に変更しています。これにより、リンカーが生成する最終的な実行可能ファイルが、ArbitraryUserPointerを介してTLSデータにアクセスするようになります。
  • CGOランタイム (src/pkg/runtime/cgo/gcc_windows_amd64.c, src/pkg/runtime/cgo/gcc_windows_386.c):

    • threadentry関数は、CGOが新しいスレッドを開始する際に呼び出されるエントリポイントです。この関数内で、tls0というポインタ(Goのgm構造体へのポインタを格納する領域)が確保され、そのアドレスがGS(amd64)またはFS(386)セグメントレジスタの特定のオフセットに書き込まれます。このコミットでは、その書き込み先のオフセットが0x58から0x28(amd64)または0x2Cから0x14(386)に変更されています。これにより、CGOがGoランタイムのスレッド固有データを正しく設定できるようになります。
  • アセンブリコード (src/pkg/runtime/sys_windows_amd64.s, src/pkg/runtime/sys_windows_386.s):

    • これらのアセンブリファイルには、Goランタイムの低レベルなスレッド管理やコンテキスト切り替えに関連するコードが含まれています。例えば、runtime·externalthreadhandler関数は、外部からGoランタイムにコールバックされるスレッドのエントリポイントです。この関数や他の関連する関数内で、GSまたはFSセグメントレジスタからのオフセットを使用して、Goのg(ゴルーチン)やm(OSスレッド)構造体へのポインタをTLSに保存したり、そこから読み出したりする操作が行われます。これらのオフセットが、ArbitraryUserPointerのオフセットである0x28(amd64)や0x14(386)に統一されています。これにより、Goランタイムがスレッド固有のコンテキストを効率的かつ正確に管理できるようになります。

これらの変更は、GoランタイムがWindowsの低レベルなスレッドメカニズムとより密接に連携し、TLSを介したスレッド固有データへのアクセスを最適化するための重要なステップです。

関連リンク

参考にした情報源リンク