[インデックス 17231] ファイルの概要
このコミットは、Goコンパイラツールチェーンの一部であるcmd/distおよびcmd/ldにおける、Darwin (macOS) 環境でのビルド問題を修正するものです。具体的には、スレッドローカルストレージ (TLS) の扱いがDarwinとELF (Linuxなど) で異なることに起因する問題を解決し、Darwin上でのGoプログラムのビルドを可能にします。
コミット
commit 308a3e6c522928a5787a54b122bfba18856a3347
Author: Russ Cox <rsc@golang.org>
Date: Wed Aug 14 12:57:05 2013 -0400
cmd/dist: fix darwin build
The TLS block on Darwin is not the same as on ELF.
TBR=elias.naur
CC=golang-dev
https://golang.org/cl/12741044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/308a3e6c522928a5787a54b122bfba18856a3347
元コミット内容
cmd/dist: fix darwin build
The TLS block on Darwin is not the same as on ELF.
変更の背景
このコミットの背景には、Go言語のランタイムがスレッドローカルストレージ (TLS) を利用する際の、オペレーティングシステム間の差異があります。特に、Darwin (macOS) とELF (Executable and Linkable Format) を採用するシステム(Linuxなど)では、TLSの実現方法が根本的に異なります。
Goランタイムは、各ゴルーチン(Goにおける軽量スレッド)が自身のスタックやその他のスレッド固有のデータに効率的にアクセスできるように、TLSのようなメカニズムを利用します。これは、g(現在のゴルーチン構造体へのポインタ)やm(現在のOSスレッド構造体へのポインタ)といった重要なランタイム内部データへの高速なアクセスを可能にするために不可欠です。
従来のGoのビルドシステムは、主にELFベースのシステム(Linuxなど)でのTLSの扱いに最適化されていました。ELFシステムでは、通常、%fsまたは%gsレジスタ(x86-64アーキテクチャの場合)を介してTLSブロックにアクセスし、そのオフセットを使ってスレッド固有のデータに到達します。Goランタイムは、このTLSブロック内にgとmへのポインタを格納していました。
しかし、Darwinシステムでは、TLSの管理方法が異なります。DarwinのTLSは、ELFのような直接的なレジスタベースのアクセスではなく、より抽象化されたAPI(例えばpthread_getspecificなど)を通じて提供されることが一般的です。Goのリンカ(cmd/ld)やランタイムビルドツール(cmd/dist)が、このDarwin特有のTLSの挙動を適切に扱えていなかったため、Darwin上でGoプログラムをビルドする際に問題が発生していました。
具体的には、runtime·tlsgmというシンボル(gとmへのポインタを格納するTLSブロック)の扱いが、DarwinとELFで異なっていたことが問題の核心です。このコミットは、Darwin環境でのTLSブロックの構造とアクセス方法の違いを吸収し、GoのビルドツールがDarwin上で正しく動作するように修正することを目的としています。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念とGoの内部構造に関する知識が必要です。
-
スレッドローカルストレージ (TLS: Thread Local Storage):
- 各スレッド(またはGoの場合はゴルーチン)が、他のスレッドから独立してアクセスできる独自のデータ領域を持つメカニズムです。グローバル変数とは異なり、TLSに格納されたデータはスレッドごとに異なる値を持ちます。
- Goランタイムでは、現在のゴルーチン(
g)やOSスレッド(m)に関する情報を高速に取得するためにTLSが利用されます。 - ELF (Executable and Linkable Format): LinuxなどのUnix系OSで広く使われている実行ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。ELFシステムでは、TLSは通常、
%fsまたは%gsセグメントレジスタを介してアクセスされます。TLSブロックのベースアドレスがこれらのレジスタに設定され、そこからのオフセットでスレッド固有のデータにアクセスします。 - Darwin (macOS): AppleのmacOSオペレーティングシステムです。DarwinのTLS実装は、ELFとは異なり、より高レベルのAPI(例:
__threadキーワードやpthread_getspecific)を通じて提供されることが多く、直接的なレジスタベースのアクセスモデルとは異なります。
-
Goのビルドツールチェーン:
cmd/dist: GoのソースコードからGoのツールチェーン自体(コンパイラ、リンカなど)をビルドするためのツールです。ランタイムのビルド設定や、アセンブリコードのプリプロセッサマクロなどを生成します。cmd/ld: Goのリンカです。オブジェクトファイルを結合して実行可能ファイルや共有ライブラリを生成します。この過程で、シンボルの解決、メモリレイアウトの決定、TLSセクションの配置などを行います。
-
Goランタイムの内部構造:
g(goroutine): Goにおける軽量スレッドの抽象化です。各ゴルーチンは独自のスタックを持ち、Goスケジューラによって管理されます。m(machine/OS thread): Goランタイムがゴルーチンを実行するために使用するOSスレッドです。mはOSスケジューラによって管理されます。get_tls(r): GoのアセンブリコードでTLSブロックのベースアドレスを取得するためのマクロまたは関数です。rはレジスタを表します。g(r):get_tls(r)で取得したTLSベースアドレスから、現在のゴルーチン構造体gへのポインタを取得するためのマクロです。通常、TLSブロックの先頭オフセットにgへのポインタが格納されています。m(r):get_tls(r)で取得したTLSベースアドレスから、現在のOSスレッド構造体mへのポインタを取得するためのマクロです。通常、TLSブロックのgのポインタの次のオフセットにmへのポインタが格納されています。runtime·tlsgm: Goランタイムが内部的に使用するシンボルで、gとmへのポインタを格納するTLSブロックの領域を指します。これは、Goのリンカによって特別に扱われるTLSセクション(.tbssなど)に配置されます。STLSBSS: リンカが認識するシンボルタイプの一つで、TLSのBSSセクション(初期化されていないデータセクション)に属するシンボルを示します。LinkExternal: リンカのモードの一つで、外部リンカ(例: GCC)を使用する場合を示します。この場合、Goのリンカは最終的な実行可能ファイルの生成を外部リンカに委ね、Goのオブジェクトファイルを外部リンカが理解できる形式で出力します。HEADTYPE: 実行可能ファイルのヘッダタイプを示します。HopenbsdはOpenBSDの実行可能ファイル形式を指します。iself: 現在のターゲットシステムがELF形式を使用しているかどうかを示すフラグです。
技術的詳細
このコミットは、主に以下の3つのファイルに対する変更を通じて、Darwin環境でのTLSの扱いを修正しています。
-
src/cmd/dist/buildruntime.c:- このファイルは、Goランタイムのアセンブリコードで使用されるマクロ(
get_tls,g,mなど)を定義します。 - 変更前は、
amd64アーキテクチャに対しては、OSを特定しない汎用的なTLSアクセス定義がありました。これは、get_tls(r)がMOVQ runtime·tlsgm(SB), r(runtime·tlsgmシンボルのアドレスをレジスタrに移動)として定義され、g(r)とm(r)がそれぞれ0(r)(GS*1)と8(r)(GS*1)(rレジスタが指すアドレスからオフセット0と8にgとmのポインタがあることを示し、GSセグメントレジスタをTLSベースとして使用)となっていました。 - このコミットでは、
amd64アーキテクチャに対して、linux固有のTLS定義を追加しました。これにより、amd64かつlinuxの場合にのみ、従来のMOVQ runtime·tlsgm(SB), rとGSレジスタを使ったTLSアクセスが適用されるようになりました。 - そして、
amd64アーキテクチャに対して、OSを特定しない汎用的な定義を新たに設けました。この新しい汎用定義では、get_tls(r)が空になり、g(r)が0(GS)、m(r)が8(GS)と定義されています。これは、Darwinのようなシステムではruntime·tlsgmシンボルを直接参照するのではなく、GSレジスタがTLSベースを指すという前提で、オフセットアクセスを行うことを示唆しています。Darwinでは、TLSのベースアドレスがGSレジスタに設定されるものの、その取得方法がELFとは異なるため、get_tlsマクロを空にすることで、GoのアセンブリコードがTLSベースアドレスを直接取得するのではなく、OSが提供するメカニズムに依存するように変更しています。
- このファイルは、Goランタイムのアセンブリコードで使用されるマクロ(
-
src/cmd/ld/data.c:- このファイルは、Goリンカのデータセクション処理に関連します。
dodata()関数内で、TLSのBSSセクション(STLSBSS)の扱いに関する条件が変更されました。- 変更前は、
iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && HEADTYPE != Hopenbsdという条件でした。 - 変更後は、
iselfの条件が二重に記述されていますが、これはおそらくタイプミスであり、意図としてはiselfが真である場合にのみ、このTLSセクションの処理を行うということを強調しています。つまり、ELF形式のシステムでのみ、外部リンカを使用し、かつOpenBSDではない場合に、.tbssセクションを追加するというロジックが適用されます。これにより、非ELFシステム(Darwinなど)では、この特定のTLSセクションの処理がスキップされるようになります。
-
src/cmd/ld/lib.c:- このファイルは、Goリンカのライブラリ処理に関連します。
loadlib()関数内で、runtime·tlsgmシンボルのreachable(到達可能)フラグの設定ロジックが変更されました。runtime·tlsgmは、gとmへのポインタを格納するTLSブロックのシンボルです。- 変更前は、
linkmode == LinkExternalの場合にgmsym->reachable = 1(到達可能)と設定されていました。 - 変更後は、
linkmode == LinkExternal && iself && HEADTYPE != Hopenbsdという条件が追加されました。これは、runtime·tlsgmシンボルが外部リンカから到達可能であるとマークされるのは、ELF形式のシステムで、かつOpenBSDではない場合に限られることを意味します。 - この変更により、Darwinのような非ELFシステムで外部リンカを使用する場合、
runtime·tlsgmシンボルは到達可能とマークされなくなり、リンカがDarwinのTLSメカニズムに合わせて適切に処理できるようになります。
これらの変更は、Goのビルドツールが、TLSの構造とアクセス方法が異なるオペレーティングシステム(特にDarwin)を正しくサポートするために不可欠でした。cmd/distでTLSアクセスのアセンブリマクロをOSごとに分岐させ、cmd/ldでTLSセクションのリンキングロジックをOSタイプに基づいて調整することで、Darwin上でのGoプログラムのビルドが可能になりました。
コアとなるコードの変更箇所
src/cmd/dist/buildruntime.c
--- a/src/cmd/dist/buildruntime.c
+++ b/src/cmd/dist/buildruntime.c
@@ -166,17 +166,16 @@ static struct {
// If the linker is not outputting a shared library, it will reduce
// the TLS accessors to the local exec model, effectively removing
// get_tls().
- {"amd64", "",
- "// The offsets 0 and 8 are known to:\\n"\
- "//\t../../cmd/6l/pass.c:/D_GS\\n"\
- "//\tcgo/gcc_linux_amd64.c:/^threadentry\\n"\
- "//\tcgo/gcc_darwin_amd64.c:/^threadentry\\n"\
- "//\\n"\
+ {"amd64", "linux",
"#define\tget_tls(r) MOVQ runtime·tlsgm(SB), r\\n"\
"#define\tg(r) 0(r)(GS*1)\\n"\
"#define\tm(r) 8(r)(GS*1)\\n"\
},\
-
+ {"amd64", "",
+ "#define get_tls(r)\\n"\
+ "#define g(r) 0(GS)\\n"\
+ "#define m(r) 8(GS)\\n"\
+ },\
{"arm", "",
"#define\tLR\tR14\\n"\
},\
src/cmd/ld/data.c
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -1212,7 +1212,7 @@ dodata(void)\
diag("data or bss segment too large");
}\
- if(iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && HEADTYPE != Hopenbsd) {\
+ if(iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && iself && HEADTYPE != Hopenbsd) {\
sect = addsection(&segdata, ".tbss", 06);\
sect->align = PtrSize;\
sect->vaddr = 0;\
src/cmd/ld/lib.c
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -360,16 +360,17 @@ loadlib(void)\
} else
s->type = 0;
}\
- }
+ }
+
gmsym = lookup("runtime.tlsgm", 0);\
gmsym->type = STLSBSS;\
gmsym->size = 2*PtrSize;\
gmsym->hide = 1;\
- if(linkmode == LinkExternal)\
+ if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd)\
gmsym->reachable = 1;\
else
gmsym->reachable = 0;\
-
+
// Now that we know the link mode, trim the dynexp list.\
x = CgoExportDynamic;\
if(linkmode == LinkExternal)\
コアとなるコードの解説
src/cmd/dist/buildruntime.c の変更
このファイルは、Goランタイムのアセンブリコードが使用するマクロを生成します。
- 変更前:
{"amd64", "", ...}のエントリは、amd64アーキテクチャ全般に対して、runtime·tlsgmシンボルを直接参照してTLSベースアドレスを取得し、GSレジスタをTLSセグメントとして使用するマクロを定義していました。これはELFベースのシステム(Linuxなど)の挙動を想定しています。 - 変更後:
{"amd64", "linux", ...}という新しいエントリが追加されました。これは、amd64アーキテクチャかつlinuxOSの場合にのみ、従来のruntime·tlsgmを参照するTLSアクセス方法を適用します。{"amd64", "", ...}というエントリが残されましたが、その定義が変更されました。#define get_tls(r)が空になり、#define g(r) 0(GS)と#define m(r) 8(GS)となっています。これは、get_tlsマクロがTLSベースアドレスを明示的に取得するコードを生成せず、gとmへのアクセスが直接GSレジスタからのオフセットで行われることを意味します。Darwinでは、TLSベースアドレスがGSレジスタに設定されるものの、その取得方法がELFとは異なるため、この変更により、GoのアセンブリコードがDarwinのTLSメカニズムに適合するようになります。
src/cmd/ld/data.c の変更
このファイルは、Goリンカがデータセクションを処理するロジックを含んでいます。
- 変更前:
if(iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && HEADTYPE != Hopenbsd)という条件で、TLSのBSSセクション(.tbss)を追加していました。 - 変更後: 条件に
&& iselfが追加されました。これは、iselfが二重に記述されているため、おそらくタイプミスですが、意図としては、このTLSセクションの処理がELF形式のシステムに限定されることをより明確にしています。これにより、Darwinのような非ELFシステムでは、この特定の.tbssセクションの追加ロジックが実行されなくなり、DarwinのTLSの扱いに合わせたリンキングが可能になります。
src/cmd/ld/lib.c の変更
このファイルは、Goリンカがライブラリをロードし、シンボルを処理するロジックを含んでいます。
- 変更前:
if(linkmode == LinkExternal)という条件で、runtime·tlsgmシンボルが外部リンカから到達可能(gmsym->reachable = 1)とマークされていました。 - 変更後: 条件が
if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd)に変更されました。これにより、runtime·tlsgmシンボルが外部リンカから到達可能とマークされるのは、外部リンカを使用し、かつELF形式のシステムで、OpenBSDではない場合に限定されます。Darwinのような非ELFシステムでは、このシンボルは到達可能とマークされなくなり、リンカがDarwinのTLSの挙動に合わせて適切に処理できるようになります。
これらの変更は、Goのビルドツールが異なるOS(特にDarwin)のTLS実装の違いを認識し、それに応じてアセンブリマクロの生成とリンキングの挙動を調整することで、クロスプラットフォームでのGoプログラムのビルドを可能にしています。
関連リンク
- Go CL 12741044: https://golang.org/cl/12741044
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/dist,src/cmd/ldディレクトリ) - Go言語のIssueトラッカーやメーリングリストでのTLSに関する議論 (一般的な知識として)
- ELF (Executable and Linkable Format) の仕様に関する情報 (TLSセクションの構造など)
- Darwin (macOS) のTLS実装に関する情報 (一般的な知識として)
- x86-64アーキテクチャにおけるセグメントレジスタ(
%fs,%gs)とTLSの利用に関する情報```markdown
[インデックス 17231] ファイルの概要
このコミットは、Goコンパイラツールチェーンの一部であるcmd/distおよびcmd/ldにおける、Darwin (macOS) 環境でのビルド問題を修正するものです。具体的には、スレッドローカルストレージ (TLS) の扱いがDarwinとELF (Linuxなど) で異なることに起因する問題を解決し、Darwin上でのGoプログラムのビルドを可能にします。
コミット
commit 308a3e6c522928a5787a54b122bfba18856a3347
Author: Russ Cox <rsc@golang.org>
Date: Wed Aug 14 12:57:05 2013 -0400
cmd/dist: fix darwin build
The TLS block on Darwin is not the same as on ELF.
TBR=elias.naur
CC=golang-dev
https://golang.org/cl/12741044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/308a3e6c522928a5787a54b122bfba18856a3347
元コミット内容
cmd/dist: fix darwin build
The TLS block on Darwin is not the same as on ELF.
変更の背景
このコミットの背景には、Go言語のランタイムがスレッドローカルストレージ (TLS) を利用する際の、オペレーティングシステム間の差異があります。特に、Darwin (macOS) とELF (Executable and Linkable Format) を採用するシステム(Linuxなど)では、TLSの実現方法が根本的に異なります。
Goランタイムは、各ゴルーチン(Goにおける軽量スレッド)が自身のスタックやその他のスレッド固有のデータに効率的にアクセスできるように、TLSのようなメカニズムを利用します。これは、g(現在のゴルーチン構造体へのポインタ)やm(現在のOSスレッド構造体へのポインタ)といった重要なランタイム内部データへの高速なアクセスを可能にするために不可欠です。
従来のGoのビルドシステムは、主にELFベースのシステム(Linuxなど)でのTLSの扱いに最適化されていました。ELFシステムでは、通常、%fsまたは%gsレジスタ(x86-64アーキテクチャの場合)を介してTLSブロックにアクセスし、そのオフセットを使ってスレッド固有のデータに到達します。Goランタイムは、このTLSブロック内にgとmへのポインタを格納していました。
しかし、Darwinシステムでは、TLSの管理方法が異なります。DarwinのTLSは、ELFのような直接的なレジスタベースのアクセスではなく、より抽象化されたAPI(例えばpthread_getspecificなど)を通じて提供されることが一般的です。Goのリンカ(cmd/ld)やランタイムビルドツール(cmd/dist)が、このDarwin特有のTLSの挙動を適切に扱えていなかったため、Darwin上でGoプログラムをビルドする際に問題が発生していました。
具体的には、runtime·tlsgmというシンボル(gとmへのポインタを格納するTLSブロック)の扱いが、DarwinとELFで異なっていたことが問題の核心です。このコミットは、Darwin環境でのTLSブロックの構造とアクセス方法の違いを吸収し、GoのビルドツールがDarwin上で正しく動作するように修正することを目的としています。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念とGoの内部構造に関する知識が必要です。
-
スレッドローカルストレージ (TLS: Thread Local Storage):
- 各スレッド(またはGoの場合はゴルーチン)が、他のスレッドから独立してアクセスできる独自のデータ領域を持つメカニズムです。グローバル変数とは異なり、TLSに格納されたデータはスレッドごとに異なる値を持ちます。
- Goランタイムでは、現在のゴルーチン(
g)やOSスレッド(m)に関する情報を高速に取得するためにTLSが利用されます。 - ELF (Executable and Linkable Format): LinuxなどのUnix系OSで広く使われている実行ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。ELFシステムでは、TLSは通常、
%fsまたは%gsセグメントレジスタを介してアクセスされます。TLSブロックのベースアドレスがこれらのレジスタに設定され、そこからのオフセットでスレッド固有のデータにアクセスします。 - Darwin (macOS): AppleのmacOSオペレーティングシステムです。DarwinのTLS実装は、ELFとは異なり、より高レベルのAPI(例:
__threadキーワードやpthread_getspecific)を通じて提供されることが多く、直接的なレジスタベースのアクセスモデルとは異なります。
-
Goのビルドツールチェーン:
cmd/dist: GoのソースコードからGoのツールチェーン自体(コンパイラ、リンカなど)をビルドするためのツールです。ランタイムのビルド設定や、アセンブリコードのプリプロセッサマクロなどを生成します。cmd/ld: Goのリンカです。オブジェクトファイルを結合して実行可能ファイルや共有ライブラリを生成します。この過程で、シンボルの解決、メモリレイアウトの決定、TLSセクションの配置などを行います。
-
Goランタイムの内部構造:
g(goroutine): Goにおける軽量スレッドの抽象化です。各ゴルーチンは独自のスタックを持ち、Goスケジューラによって管理されます。m(machine/OS thread): Goランタイムがゴルーチンを実行するために使用するOSスレッドです。mはOSスケジューラによって管理されます。get_tls(r): GoのアセンブリコードでTLSブロックのベースアドレスを取得するためのマクロまたは関数です。rはレジスタを表します。g(r):get_tls(r)で取得したTLSベースアドレスから、現在のゴルーチン構造体gへのポインタを取得するためのマクロです。通常、TLSブロックの先頭オフセットにgへのポインタが格納されています。m(r):get_tls(r)で取得したTLSベースアドレスから、現在のOSスレッド構造体mへのポインタを取得するためのマクロです。通常、TLSブロックのgのポインタの次のオフセットにmへのポインタが格納されています。runtime·tlsgm: Goランタイムが内部的に使用するシンボルで、gとmへのポインタを格納するTLSブロックの領域を指します。これは、Goのリンカによって特別に扱われるTLSセクション(.tbssなど)に配置されます。STLSBSS: リンカが認識するシンボルタイプの一つで、TLSのBSSセクション(初期化されていないデータセクション)に属するシンボルを示します。LinkExternal: リンカのモードの一つで、外部リンカ(例: GCC)を使用する場合を示します。この場合、Goのリンカは最終的な実行可能ファイルの生成を外部リンカに委ね、Goのオブジェクトファイルを外部リンカが理解できる形式で出力します。HEADTYPE: 実行可能ファイルのヘッダタイプを示します。HopenbsdはOpenBSDの実行可能ファイル形式を指します。iself: 現在のターゲットシステムがELF形式を使用しているかどうかを示すフラグです。
技術的詳細
このコミットは、主に以下の3つのファイルに対する変更を通じて、Darwin環境でのTLSの扱いを修正しています。
-
src/cmd/dist/buildruntime.c:- このファイルは、Goランタイムのアセンブリコードで使用されるマクロ(
get_tls,g,mなど)を定義します。 - 変更前は、
amd64アーキテクチャに対しては、OSを特定しない汎用的なTLSアクセス定義がありました。これは、get_tls(r)がMOVQ runtime·tlsgm(SB), r(runtime·tlsgmシンボルのアドレスをレジスタrに移動)として定義され、g(r)とm(r)がそれぞれ0(r)(GS*1)と8(r)(GS*1)(rレジスタが指すアドレスからオフセット0と8にgとmのポインタがあることを示し、GSセグメントレジスタをTLSベースとして使用)となっていました。 - このコミットでは、
amd64アーキテクチャに対して、linux固有のTLS定義を追加しました。これにより、amd64かつlinuxの場合にのみ、従来のMOVQ runtime·tlsgm(SB), rとGSレジスタを使ったTLSアクセスが適用されるようになりました。 - そして、
amd64アーキテクチャに対して、OSを特定しない汎用的な定義を新たに設けました。この新しい汎用定義では、get_tls(r)が空になり、g(r)が0(GS)、m(r)が8(GS)と定義されています。これは、Darwinのようなシステムではruntime·tlsgmシンボルを直接参照するのではなく、GSレジスタがTLSベースを指すという前提で、オフセットアクセスを行うことを示唆しています。Darwinでは、TLSのベースアドレスがGSレジスタに設定されるものの、その取得方法がELFとは異なるため、get_tlsマクロを空にすることで、GoのアセンブリコードがTLSベースアドレスを直接取得するのではなく、OSが提供するメカニズムに依存するように変更しています。
- このファイルは、Goランタイムのアセンブリコードで使用されるマクロ(
-
src/cmd/ld/data.c:- このファイルは、Goリンカのデータセクション処理に関連します。
dodata()関数内で、TLSのBSSセクション(STLSBSS)の扱いに関する条件が変更されました。- 変更前は、
iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && HEADTYPE != Hopenbsdという条件でした。 - 変更後は、
iselfの条件が二重に記述されていますが、これはおそらくタイプミスであり、意図としてはiselfが真である場合にのみ、このTLSセクションの処理を行うということを強調しています。つまり、ELF形式のシステムでのみ、外部リンカを使用し、かつOpenBSDではない場合に、.tbssセクションを追加するというロジックが適用されます。これにより、非ELFシステム(Darwinなど)では、この特定のTLSセクションの処理がスキップされるようになります。
-
src/cmd/ld/lib.c:- このファイルは、Goリンカのライブラリ処理に関連します。
loadlib()関数内で、runtime·tlsgmシンボルのreachable(到達可能)フラグの設定ロジックが変更されました。runtime·tlsgmは、gとmへのポインタを格納するTLSブロックのシンボルです。- 変更前は、
linkmode == LinkExternalの場合にgmsym->reachable = 1(到達可能)と設定されていました。 - 変更後は、
linkmode == LinkExternal && iself && HEADTYPE != Hopenbsdという条件が追加されました。これは、runtime·tlsgmシンボルが外部リンカから到達可能であるとマークされるのは、ELF形式のシステムで、かつOpenBSDではない場合に限られることを意味します。 - この変更により、Darwinのような非ELFシステムで外部リンカを使用する場合、
runtime·tlsgmシンボルは到達可能とマークされなくなり、リンカがDarwinのTLSメカニズムに合わせて適切に処理できるようになります。
これらの変更は、Goのビルドツールが、TLSの構造とアクセス方法が異なるオペレーティングシステム(特にDarwin)を正しくサポートするために不可欠でした。cmd/distでTLSアクセスのアセンブリマクロをOSごとに分岐させ、cmd/ldでTLSセクションのリンキングロジックをOSタイプに基づいて調整することで、Darwin上でのGoプログラムのビルドが可能になりました。
コアとなるコードの変更箇所
src/cmd/dist/buildruntime.c
--- a/src/cmd/dist/buildruntime.c
+++ b/src/cmd/dist/buildruntime.c
@@ -166,17 +166,16 @@ static struct {
// If the linker is not outputting a shared library, it will reduce
// the TLS accessors to the local exec model, effectively removing
// get_tls().
- {"amd64", "",
- "// The offsets 0 and 8 are known to:\\n"\
- "//\t../../cmd/6l/pass.c:/D_GS\\n"\
- "//\tcgo/gcc_linux_amd64.c:/^threadentry\\n"\
- "//\tcgo/gcc_darwin_amd64.c:/^threadentry\\n"\
- "//\\n"\
+ {"amd64", "linux",
"#define\tget_tls(r) MOVQ runtime·tlsgm(SB), r\\n"\
"#define\tg(r) 0(r)(GS*1)\\n"\
"#define\tm(r) 8(r)(GS*1)\\n"\
},\
-
+ {"amd64", "",
+ "#define get_tls(r)\\n"\
+ "#define g(r) 0(GS)\\n"\
+ "#define m(r) 8(GS)\\n"\
+ },\
{"arm", "",
"#define\tLR\tR14\\n"\
},\
src/cmd/ld/data.c
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -1212,7 +1212,7 @@ dodata(void)\
diag("data or bss segment too large");
}\
- if(iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && HEADTYPE != Hopenbsd) {\
+ if(iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && iself && HEADTYPE != Hopenbsd) {\
sect = addsection(&segdata, ".tbss", 06);\
sect->align = PtrSize;\
sect->vaddr = 0;\
src/cmd/ld/lib.c
--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -360,16 +360,17 @@ loadlib(void)\
} else
s->type = 0;
}\
- }
+ }
+
gmsym = lookup("runtime.tlsgm", 0);\
gmsym->type = STLSBSS;\
gmsym->size = 2*PtrSize;\
gmsym->hide = 1;\
- if(linkmode == LinkExternal)\
+ if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd)\
gmsym->reachable = 1;\
else
gmsym->reachable = 0;\
-
+
// Now that we know the link mode, trim the dynexp list.\
x = CgoExportDynamic;\
if(linkmode == LinkExternal)\
コアとなるコードの解説
src/cmd/dist/buildruntime.c の変更
このファイルは、Goランタイムのアセンブリコードが使用するマクロを生成します。
- 変更前:
{"amd64", "", ...}のエントリは、amd64アーキテクチャ全般に対して、runtime·tlsgmシンボルを直接参照してTLSベースアドレスを取得し、GSレジスタをTLSセグメントとして使用するマクロを定義していました。これはELFベースのシステム(Linuxなど)の挙動を想定しています。 - 変更後:
{"amd64", "linux", ...}という新しいエントリが追加されました。これは、amd64アーキテクチャかつlinuxOSの場合にのみ、従来のruntime·tlsgmを参照するTLSアクセス方法を適用します。{"amd64", "", ...}というエントリが残されましたが、その定義が変更されました。#define get_tls(r)が空になり、#define g(r) 0(GS)と#define m(r) 8(GS)となっています。これは、get_tlsマクロがTLSベースアドレスを明示的に取得するコードを生成せず、gとmへのアクセスが直接GSレジスタからのオフセットで行われることを意味します。Darwinでは、TLSベースアドレスがGSレジスタに設定されるものの、その取得方法がELFとは異なるため、この変更により、GoのアセンブリコードがDarwinのTLSメカニズムに適合するようになります。
src/cmd/ld/data.c の変更
このファイルは、Goリンカがデータセクションを処理するロジックを含んでいます。
- 変更前:
if(iself && linkmode == LinkExternal && s != nil && s->type == STLSBSS && HEADTYPE != Hopenbsd)という条件で、TLSのBSSセクション(.tbss)を追加していました。 - 変更後: 条件に
&& iselfが追加されました。これは、iselfが二重に記述されているため、おそらくタイプミスですが、意図としては、このTLSセクションの処理がELF形式のシステムに限定されることをより明確にしています。これにより、Darwinのような非ELFシステムでは、この特定の.tbssセクションの追加ロジックが実行されなくなり、DarwinのTLSの扱いに合わせたリンキングが可能になります。
src/cmd/ld/lib.c の変更
このファイルは、Goリンカがライブラリをロードし、シンボルを処理するロジックを含んでいます。
- 変更前:
if(linkmode == LinkExternal)という条件で、runtime·tlsgmシンボルが外部リンカから到達可能(gmsym->reachable = 1)とマークされていました。 - 変更後: 条件が
if(linkmode == LinkExternal && iself && HEADTYPE != Hopenbsd)に変更されました。これにより、runtime·tlsgmシンボルが外部リンカから到達可能とマークされるのは、外部リンカを使用し、かつELF形式のシステムで、OpenBSDではない場合に限定されます。Darwinのような非ELFシステムでは、このシンボルは到達可能とマークされなくなり、リンカがDarwinのTLSの挙動に合わせて適切に処理できるようになります。
これらの変更は、Goのビルドツールが異なるOS(特にDarwin)のTLS実装の違いを認識し、それに応じてアセンブリマクロの生成とリンキングの挙動を調整することで、クロスプラットフォームでのGoプログラムのビルドを可能にしています。
関連リンク
- Go CL 12741044: https://golang.org/cl/12741044
参考にした情報源リンク
- Go言語のソースコード (特に
src/cmd/dist,src/cmd/ldディレクトリ) - Go言語のIssueトラッカーやメーリングリストでのTLSに関する議論 (一般的な知識として)
- ELF (Executable and Linkable Format) の仕様に関する情報 (TLSセクションの構造など)
- Darwin (macOS) のTLS実装に関する情報 (一般的な知識として)
- x86-64アーキテクチャにおけるセグメントレジスタ(
%fs,%gs)とTLSの利用に関する情報