[インデックス 15836] ファイルの概要
このコミットは、Go言語のリンカー(cmd/ld
)におけるリンキングモードの指定方法を変更するものです。具体的には、既存の-hostobj
フラグを廃止し、より汎用的な-linkmode
フラグに置き換えています。この変更は、Goプログラムのビルドプロセスにおける外部リンカーとの連携をより柔軟に制御することを目的としていますが、コミットメッセージには「Still disabled. Need to fix TLS.」とあり、TLS(Thread Local Storage)に関する未解決の問題のため、この新機能はまだ完全に有効ではないことが示唆されています。
コミット
commit b4f3533c92dc59f22bbddbc5b73a1575ce6f7f8b
Author: Russ Cox <rsc@golang.org>
Date: Tue Mar 19 15:45:42 2013 -0400
cmd/ld: replace -hostobj with -linkmode
Still disabled. Need to fix TLS.
R=golang-dev, minux.ma, bradfitz
CC=golang-dev
https://golang.org/cl/7783044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b4f3533c92dc59f22bbddbc5b73a1575ce6f7f8b
元コミット内容
Goリンカー(cmd/ld
)において、-hostobj
というコマンドラインフラグを-linkmode
という新しいフラグに置き換える変更です。この変更は、Goのビルドシステムが外部のCリンカー(ホストリンカー)を利用する際の挙動を制御するためのものです。しかし、TLS(Thread Local Storage)に関する未解決の問題があるため、この機能はまだ完全に有効化されていません。
変更の背景
Goのビルドシステムは、通常、Go独自のリンカー(cmd/ld
)を使用して実行可能ファイルを生成します。しかし、Cgoを使用する場合や、特定のシステムライブラリにリンクする必要がある場合など、外部のCリンカー(例えばGCCのld
)を利用することがあります。
これまでのGoリンカーでは、外部リンカーを使用するかどうかを制御するために-hostobj
というフラグが使われていました。しかし、このフラグは「ホストオブジェクトファイルを生成する」という特定の目的を示唆しており、リンキングのモード全体を表現するには不十分でした。
このコミットの背景には、リンキングの挙動をより細かく、かつ明確に制御できるような汎用的なメカニズムが必要であるという設計上の要求があったと考えられます。-linkmode
という新しいフラグを導入することで、Goリンカーが「内部リンキング」を行うのか、「外部リンキング」を行うのか、あるいは「自動的に判断する」のかを明示的に指定できるようになります。これにより、将来的なリンキングオプションの拡張性も向上します。
また、コミットメッセージにある「Still disabled. Need to fix TLS.」という記述は、外部リンキングモードが完全に機能するためには、TLS(Thread Local Storage)の扱いに関する問題が解決される必要があることを示しています。TLSは、スレッドごとに独立したデータを保持するためのメカニズムであり、特にCgoを介してCライブラリと連携する際に重要となる場合があります。この問題が未解決であるため、新しい-linkmode
フラグが導入されても、外部リンキングはまだ実用段階ではないことが示されています。
前提知識の解説
Go言語のビルドプロセスとリンカー (cmd/ld
)
Go言語のプログラムは、go build
コマンドによってコンパイルされ、実行可能ファイルが生成されます。このプロセスには、コンパイラ(cmd/compile
)、アセンブラ(cmd/asm
)、そしてリンカー(cmd/ld
)が関与します。
- コンパイラ (
cmd/compile
): Goのソースコードをアセンブリコードに変換します。 - アセンブラ (
cmd/asm
): アセンブリコードをオブジェクトファイル(.o
ファイル)に変換します。 - リンカー (
cmd/ld
): 複数のオブジェクトファイルやライブラリを結合し、最終的な実行可能ファイルや共有ライブラリを生成します。Goのリンカーは、Goランタイムや標準ライブラリのコードを静的にリンクする能力を持っています。
内部リンキングと外部リンキング
Goのリンカーは、大きく分けて2つのリンキングモードを持っています。
-
内部リンキング (Internal Linking):
- Goのリンカー(
cmd/ld
)が、GoのオブジェクトファイルとGoランタイム、標準ライブラリの全てを結合して、単一の実行可能ファイルを生成するモードです。 - このモードでは、外部のCリンカーは使用されません。
- 生成される実行可能ファイルは完全に自己完結型であり、Goの実行環境がインストールされていないシステムでも動作します。
- Goのプログラムのほとんどは、この内部リンキングモードでビルドされます。
- Goのリンカー(
-
外部リンキング (External Linking):
- Goのリンカーが、Goのオブジェクトファイルを生成し、そのGoオブジェクトファイルとCgoによって生成されたCのオブジェクトファイル、および外部のCライブラリを、システムにインストールされているCリンカー(例: GCCの
ld
)に渡して最終的な実行可能ファイルを生成するモードです。 - このモードは、Cgoを使用してC/C++のコードと連携する場合や、特定のシステムライブラリ(例:
libc
)に動的にリンクする必要がある場合などに使用されます。 - 生成される実行可能ファイルは、外部のCライブラリに依存する場合があります。
- 以前の
-hostobj
フラグは、この外部リンキングモードに関連していました。
- Goのリンカーが、Goのオブジェクトファイルを生成し、そのGoオブジェクトファイルとCgoによって生成されたCのオブジェクトファイル、および外部のCライブラリを、システムにインストールされているCリンカー(例: GCCの
-hostobj
フラグ (旧)
このコミット以前に存在した-hostobj
フラグは、Goリンカーに対して「ホストリンカーが処理できるオブジェクトファイルを生成する」ことを指示するものでした。これは実質的に、Goリンカーが外部リンキングモードで動作するためのトリガーとなっていました。しかし、このフラグ名はその目的を完全に表しているわけではなく、リンキングモードの概念をより明確にする必要がありました。
TLS (Thread Local Storage)
TLS(Thread Local Storage)は、マルチスレッドプログラミングにおいて、各スレッドが自分専用のデータを保持するためのメカニズムです。グローバル変数や静的変数はプロセス内の全てのスレッドで共有されますが、TLSに格納されたデータは各スレッドから独立してアクセスできます。
C言語やC++では、__thread
キーワードなどを用いてTLS変数を宣言できます。GoプログラムがCgoを介してCライブラリと連携する場合、CライブラリがTLSを使用していると、GoのランタイムとCライブラリの間でTLSの管理方法に不整合が生じることがあります。これが、コミットメッセージで言及されている「Need to fix TLS」の問題の根源であると考えられます。外部リンキングでは、Goのランタイムと外部のCライブラリが同じプロセス空間で動作するため、TLSの正しい連携が不可欠となります。
ELF (Executable and Linkable Format) / Mach-O
ELFは、Unix系システム(Linux、FreeBSDなど)で広く使用されている実行可能ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。Mach-Oは、macOSやiOSで使用される同様のフォーマットです。リンカーは、これらのフォーマットのファイルを生成し、解析する役割を担います。このコミットでは、ELFやMach-O関連のコードでisobj
からlinkmode == LinkExternal
への変更が見られます。
技術的詳細
このコミットの主要な技術的変更点は、Goリンカーの内部でリンキングモードを管理する方法を刷新したことです。
-
-hostobj
フラグの削除と-linkmode
フラグの導入:- Goのリンカー(
src/cmd/5l/obj.c
,src/cmd/6l/obj.c
,src/cmd/8l/obj.c
)から、これまでのリンキングモードを制御していたisobj
というブール型フラグ(およびそれに対応する-hostobj
コマンドラインオプション)が削除されました。 - 代わりに、
setlinkmode
という新しい関数が導入され、-linkmode
コマンドラインオプションが追加されました。このオプションは、"internal"、"external"、"auto"のいずれかの文字列を受け取ります。 setlinkmode
関数は、受け取った文字列に基づいて、linkmode
という新しい変数(おそらく列挙型)の値を設定します。LinkInternal
: 内部リンキングモードLinkExternal
: 外部リンキングモードLinkAuto
: リンカーが自動的に最適なモードを判断する
- Goのリンカー(
-
リンキングモードの条件分岐の変更:
- Goリンカーのコードベース全体で、
isobj
変数を参照していた箇所が、新しいlinkmode == LinkExternal
という条件に置き換えられました。これは、ELFリロケーションの生成、Mach-Oリロケーションの生成、DWARFデバッグ情報の処理、ELFヘッダの生成など、外部リンキングに特有の処理を行う部分に影響を与えます。 - 例えば、
src/cmd/5l/asm.c
やsrc/cmd/6l/asm.c
、src/cmd/8l/asm.c
では、if(isobj)
がif(linkmode == LinkExternal)
に変更されています。これは、外部リンキングの場合にのみ、ELFやMach-Oのリロケーション情報を出力する必要があるためです。 src/cmd/ld/data.c
では、シンボルのリロケーション処理において、isobj
の代わりにlinkmode == LinkExternal
が使用されています。これは、外部リンキングの場合に、シンボルの解決方法やリロケーションの適用方法が異なるためです。src/cmd/ld/elf.c
では、ELFファイルのセクションヘッダやプログラムヘッダの生成、エントリポイントの設定など、ELFフォーマット固有の処理において、isobj
からlinkmode == LinkExternal
への変更が多数見られます。これにより、外部リンキング時にELFファイルの構造が適切に調整されるようになります。
- Goリンカーのコードベース全体で、
-
LinkAuto
モードの導入:src/cmd/6l/obj.c
やsrc/cmd/8l/obj.c
のmain
関数内で、HEADTYPE
(ターゲットOS/アーキテクチャのヘッダタイプ)に基づいてlinkmode
がLinkAuto
の場合にLinkInternal
に設定されるロジックが追加されています。これは、特定のプラットフォームでは外部リンキングがサポートされていない場合に、自動的に内部リンキングにフォールバックするためのものです。- また、
linkmode == LinkExternal
がサポートされていないHEADTYPE
の場合にsysfatal
(システムエラー)を発生させるチェックも追加されています。
-
src/cmd/ld/go.c
における変更:loadcgo
関数内で、cgo_import_static
ディレクティブの処理において、SHOSTOBJ
シンボルタイプの設定がisobj
の条件分岐から外され、常に設定されるようになりました。これは、SHOSTOBJ
が外部リンキングの有無に関わらず、Cgoによってインポートされたシンボルを示すようになったことを意味します。- 新しい
setlinkmode
関数が定義され、コマンドライン引数からリンキングモードを解析し、linkmode
変数を設定する役割を担っています。
-
テストスクリプトの更新:
src/run.bash
スクリプトが更新され、go test
コマンドの-ldflags
オプションで、-hostobj
の代わりに-linkmode=auto
や-linkmode=external
が使用されるようになりました。これにより、新しいリンキングモードのフラグがビルドシステムで正しくテストされることが保証されます。
これらの変更により、Goリンカーのリンキングモードの制御がより明確で、将来の拡張性に対応できるような設計に進化しました。しかし、TLSの問題が解決されるまでは、外部リンキングモードはまだ実験的な段階にあることが示されています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のパターンに集約されます。
-
isobj
変数の削除とlinkmode
変数の導入:- リンカーの各アーキテクチャ固有の
obj.c
ファイル(例:src/cmd/5l/obj.c
,src/cmd/6l/obj.c
,src/cmd/8l/obj.c
)において、flagcount("hostobj", ..., &isobj)
のような-hostobj
フラグの定義が削除され、代わりにflagfn1("linkmode", ..., setlinkmode)
という-linkmode
フラグの定義が追加されています。 - これにより、リンキングモードの制御がブール値の
isobj
から、より詳細なlinkmode
列挙型(LinkInternal
,LinkExternal
,LinkAuto
)に移行しました。
- リンカーの各アーキテクチャ固有の
-
isobj
による条件分岐のlinkmode == LinkExternal
への置き換え:- リンカーのコードベース全体(
src/cmd/5l/asm.c
,src/cmd/6l/asm.c
,src/cmd/8l/asm.c
,src/cmd/ld/data.c
,src/cmd/ld/elf.c
など)で、外部リンキングに特有の処理を行う箇所の条件分岐が、if(isobj)
からif(linkmode == LinkExternal)
に変更されています。
例1:
src/cmd/5l/asm.c
--- a/src/cmd/5l/asm.c +++ b/src/cmd/5l/asm.c @@ -605,7 +605,7 @@ asmb(void) Bprint(&bso, "%5.2f dwarf\\n", cputime()); dwarfemitdebugsections(); - if(isobj) + if(linkmode == LinkExternal) elfemitreloc(); } break;
例2:
src/cmd/ld/data.c
--- a/src/cmd/ld/data.c +++ b/src/cmd/ld/data.c @@ -178,11 +178,11 @@ relocsym(Sym *s) switch(r->type) { default: o = 0; - if(isobj || archreloc(r, s, &o) < 0) + if(linkmode == LinkExternal || archreloc(r, s, &o) < 0) diag("unknown reloc %d", r->type); break; case D_ADDR: - if(isobj && r->sym->type != SCONST) { + if(linkmode == LinkExternal && r->sym->type != SCONST) { r->done = 0;
- リンカーのコードベース全体(
-
setlinkmode
関数の追加:src/cmd/ld/go.c
に、-linkmode
フラグの引数を解析し、linkmode
変数を設定するsetlinkmode
関数が追加されました。
src/cmd/ld/go.c
void setlinkmode(char *arg) { if(strcmp(arg, "internal") == 0) linkmode = LinkInternal; else if(strcmp(arg, "external") == 0) linkmode = LinkExternal; else if(strcmp(arg, "auto") == 0) linkmode = LinkAuto; else { fprintf(2, "unknown link mode -linkmode %s\n", arg); errorexit(); } }
- この関数のプロトタイプは
src/cmd/ld/lib.h
に追加されています。
-
src/run.bash
におけるテストコマンドの更新:- ビルドテストスクリプトで、
-hostobj
の代わりに-linkmode
が使用されるようになりました。
src/run.bash
--- a/src/run.bash +++ b/src/run.bash @@ -75,10 +75,11 @@ go run $GOROOT/test/run.go - . [ "$CGO_ENABLED" != 1 ] || (xcd ../misc/cgo/test -go test +go test -ldflags '-linkmode=auto' +go test -ldflags '-linkmode=internal' case "$GOHOSTOS-$GOARCH" in darwin-386 | darwin-amd64 | freebsd-386 | freebsd-amd64 | linux-386 | linux-amd64 | netbsd-386 | netbsd-amd64 | openbsd-386 | openbsd-amd64) - go test -ldflags '-w -hostobj' + go test -ldflags '-linkmode=external' esac ) || exit $?
- ビルドテストスクリプトで、
これらの変更は、Goリンカーのリンキングモード制御の基盤を、より明確で拡張性の高いものへと移行させるものです。
コアとなるコードの解説
このコミットの核心は、Goリンカーが外部リンカーを使用するかどうかを判断するロジックを、ブール型のisobj
フラグから、より表現力豊かなlinkmode
列挙型に移行した点にあります。
以前は、-hostobj
フラグが指定されるとisobj
がtrue
になり、リンカーは外部リンキングに必要な処理(例えば、ELFやMach-Oのリロケーション情報の生成)を行っていました。しかし、このアプローチは、リンキングモードが「外部リンキング」であるという意図を直接的に示していませんでした。
新しいlinkmode
変数とsetlinkmode
関数は、リンキングの意図を明確に表現します。
linkmode = LinkInternal
: Goリンカーが全てを処理し、外部リンカーは使用しない。linkmode = LinkExternal
: Goリンカーは外部リンカーと連携し、外部リンカーが最終的な実行可能ファイルを生成する。linkmode = LinkAuto
: リンカーが環境やビルド設定に基づいて最適なモードを自動的に選択する。
コードの変更箇所では、if(isobj)
という単純な条件分岐がif(linkmode == LinkExternal)
に置き換えられています。これは、外部リンキングに特有の処理(例えば、elfemitreloc()
やmachoemitreloc()
といったリロケーション情報の出力、またはELFヘッダの特定のフィールド設定)が、まさにLinkExternal
モードの場合にのみ実行されるべきであることを明確に示しています。
例えば、src/cmd/ld/data.c
のrelocsym
関数における変更は、リロケーション処理が外部リンキングの場合に異なる挙動をすることを示しています。D_ADDR
やD_PCREL
といったリロケーションタイプにおいて、linkmode == LinkExternal
の場合にr->done = 0
を設定しているのは、これらのリロケーションがGoリンカーによって完全に解決されるのではなく、外部リンカーによって最終的に解決される必要があることを示唆しています。
また、src/cmd/ld/go.c
のloadcgo
関数におけるcgo_import_static
の処理からisobj
の条件分岐が削除されたことは、SHOSTOBJ
シンボルタイプが、外部リンキングの有無に関わらず、Cgoによってインポートされたシンボルを示すようになったことを意味します。これは、リンキングモードの概念がより洗練され、個々のシンボルの特性とリンキングモードがより独立して扱われるようになったことを示唆しています。
全体として、この変更はGoリンカーの内部構造をよりモジュール化し、リンキングモードの概念をより明確にすることで、将来的なリンキング機能の拡張やデバッグを容易にすることを目的としています。ただし、TLSの問題が解決されるまでは、LinkExternal
モードの完全な利用は制限されるという注意書きが重要です。
関連リンク
- Go言語のリンカーに関する公式ドキュメント(もしあれば、Goの公式ドキュメントサイトで
cmd/ld
やリンキングに関するセクションを探す) - Go言語のCgoに関するドキュメント
- ELFフォーマットの仕様
- Mach-Oフォーマットの仕様
- Thread Local Storage (TLS) に関する一般的な情報
参考にした情報源リンク
- Go言語のソースコード (GitHub)
- Go言語の公式ドキュメント
- GoのIssue Tracker (TLS関連のIssueがあれば)
- Wikipedia: Executable and Linkable Format (ELF)
- Wikipedia: Mach-O
- Wikipedia: Thread-local storage
- golang.org/cl/7783044 (このコミットに対応するGoのコードレビューシステム上の変更リスト)
- このリンクはコミットメッセージに記載されているもので、Goのコードレビューシステム(Gerrit)へのリンクです。詳細な議論や関連する変更履歴が確認できます。
- 注:
golang.org/cl/
はGoプロジェクトのGerritインスタンスへのリンクであり、コミットの詳細な議論やレビューコメントが含まれている可能性があります。これは、コミットの背景や意図を深く理解するための非常に貴重な情報源となります。 - このリンクを直接参照することで、コミットの意図や議論の経緯をより深く理解できます。The user asked for a detailed technical explanation of a Git commit in Japanese, following a specific Markdown structure. I have already read the commit data. Now I need to generate the explanation.
I have analyzed the commit message and the file changes, and identified the key concepts to explain: Go linker (cmd/ld
), internal vs. external linking, the old -hostobj
flag, the new -linkmode
flag (with "internal", "external", "auto" values), and the mention of TLS issues.
I will now construct the Markdown output based on this analysis and the required structure. I will ensure all sections are present and provide detailed explanations for each.
# [インデックス 15836] ファイルの概要
このコミットは、Go言語のリンカー(`cmd/ld`)におけるリンキングモードの指定方法を変更するものです。具体的には、既存の`-hostobj`フラグを廃止し、より汎用的な`-linkmode`フラグに置き換えています。この変更は、Goプログラムのビルドプロセスにおける外部リンカーとの連携をより柔軟に制御することを目的としていますが、コミットメッセージには「Still disabled. Need to fix TLS.」とあり、TLS(Thread Local Storage)に関する未解決の問題のため、この新機能はまだ完全に有効ではないことが示唆されています。
## コミット
commit b4f3533c92dc59f22bbddbc5b73a1575ce6f7f8b Author: Russ Cox rsc@golang.org Date: Tue Mar 19 15:45:42 2013 -0400
cmd/ld: replace -hostobj with -linkmode
Still disabled. Need to fix TLS.
R=golang-dev, minux.ma, bradfitz
CC=golang-dev
https://golang.org/cl/7783044
## GitHub上でのコミットページへのリンク
[https://github.com/golang/go/commit/b4f3533c92dc59f22bbddbc5b73a1575ce6f7f8b](https://github.com/golang/go/commit/b4f3533c92dc59f22bbddbc5b73a1575ce6f7f8b)
## 元コミット内容
Goリンカー(`cmd/ld`)において、`-hostobj`というコマンドラインフラグを`-linkmode`という新しいフラグに置き換える変更です。この変更は、Goのビルドシステムが外部のCリンカー(ホストリンカー)を利用する際の挙動を制御するためのものです。しかし、TLS(Thread Local Storage)に関する未解決の問題があるため、この機能はまだ完全に有効化されていません。
## 変更の背景
Goのビルドシステムは、通常、Go独自のリンカー(`cmd/ld`)を使用して実行可能ファイルを生成します。しかし、Cgoを使用する場合や、特定のシステムライブラリにリンクする必要がある場合など、外部のCリンカー(例えばGCCの`ld`)を利用することがあります。
これまでのGoリンカーでは、外部リンカーを使用するかどうかを制御するために`-hostobj`というフラグが使われていました。しかし、このフラグは「ホストオブジェクトファイルを生成する」という特定の目的を示唆しており、リンキングのモード全体を表現するには不十分でした。
このコミットの背景には、リンキングの挙動をより細かく、かつ明確に制御できるような汎用的なメカニズムが必要であるという設計上の要求があったと考えられます。`-linkmode`という新しいフラグを導入することで、Goリンカーが「内部リンキング」を行うのか、「外部リンキング」を行うのか、あるいは「自動的に判断する」のかを明示的に指定できるようになります。これにより、将来的なリンキングオプションの拡張性も向上します。
また、コミットメッセージにある「Still disabled. Need to fix TLS.」という記述は、外部リンキングモードが完全に機能するためには、TLS(Thread Local Storage)の扱いに関する問題が解決される必要があることを示しています。TLSは、スレッドごとに独立したデータを保持するためのメカニズムであり、特にCgoを介してCライブラリと連携する際に重要となる場合があります。この問題が未解決であるため、新しい`-linkmode`フラグが導入されても、外部リンキングはまだ実用段階ではないことが示されています。
## 前提知識の解説
### Go言語のビルドプロセスとリンカー (`cmd/ld`)
Go言語のプログラムは、`go build`コマンドによってコンパイルされ、実行可能ファイルが生成されます。このプロセスには、コンパイラ(`cmd/compile`)、アセンブラ(`cmd/asm`)、そしてリンカー(`cmd/ld`)が関与します。
* **コンパイラ (`cmd/compile`)**: Goのソースコードをアセンブリコードに変換します。
* **アセンブラ (`cmd/asm`)**: アセンブリコードをオブジェクトファイル(`.o`ファイル)に変換します。
* **リンカー (`cmd/ld`)**: 複数のオブジェクトファイルやライブラリを結合し、最終的な実行可能ファイルや共有ライブラリを生成します。Goのリンカーは、Goランタイムや標準ライブラリのコードを静的にリンクする能力を持っています。
### 内部リンキングと外部リンキング
Goのリンカーは、大きく分けて2つのリンキングモードを持っています。
1. **内部リンキング (Internal Linking)**:
* Goのリンカー(`cmd/ld`)が、GoのオブジェクトファイルとGoランタイム、標準ライブラリの全てを結合して、単一の実行可能ファイルを生成するモードです。
* このモードでは、外部のCリンカーは使用されません。
* 生成される実行可能ファイルは完全に自己完結型であり、Goの実行環境がインストールされていないシステムでも動作します。
* Goのプログラムのほとんどは、この内部リンキングモードでビルドされます。
2. **外部リンキング (External Linking)**:
* Goのリンカーが、Goのオブジェクトファイルを生成し、そのGoオブジェクトファイルとCgoによって生成されたCのオブジェクトファイル、および外部のCライブラリを、システムにインストールされているCリンカー(例: GCCの`ld`)に渡して最終的な実行可能ファイルを生成するモードです。
* このモードは、Cgoを使用してC/C++のコードと連携する場合や、特定のシステムライブラリ(例: `libc`)に動的にリンクする必要がある場合などに使用されます。
* 生成される実行可能ファイルは、外部のCライブラリに依存する場合があります。
* 以前の`-hostobj`フラグは、この外部リンキングモードに関連していました。
### `-hostobj` フラグ (旧)
このコミット以前に存在した`-hostobj`フラグは、Goリンカーに対して「ホストリンカーが処理できるオブジェクトファイルを生成する」ことを指示するものでした。これは実質的に、Goリンカーが外部リンキングモードで動作するためのトリガーとなっていました。しかし、このフラグ名はその目的を完全に表しているわけではなく、リンキングモードの概念をより明確にする必要がありました。
### TLS (Thread Local Storage)
TLS(Thread Local Storage)は、マルチスレッドプログラミングにおいて、各スレッドが自分専用のデータを保持するためのメカニズムです。グローバル変数や静的変数はプロセス内の全てのスレッドで共有されますが、TLSに格納されたデータは各スレッドから独立してアクセスできます。
C言語やC++では、`__thread`キーワードなどを用いてTLS変数を宣言できます。GoプログラムがCgoを介してCライブラリと連携する場合、CライブラリがTLSを使用していると、GoのランタイムとCライブラリの間でTLSの管理方法に不整合が生じることがあります。これが、コミットメッセージで言及されている「Need to fix TLS」の問題の根源であると考えられます。外部リンキングでは、Goのランタイムと外部のCライブラリが同じプロセス空間で動作するため、TLSの正しい連携が不可欠となります。
### ELF (Executable and Linkable Format) / Mach-O
ELFは、Unix系システム(Linux、FreeBSDなど)で広く使用されている実行可能ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。Mach-Oは、macOSやiOSで使用される同様のフォーマットです。リンカーは、これらのフォーマットのファイルを生成し、解析する役割を担います。このコミットでは、ELFやMach-O関連のコードで`isobj`から`linkmode == LinkExternal`への変更が見られます。
## 技術的詳細
このコミットの主要な技術的変更点は、Goリンカーの内部でリンキングモードを管理する方法を刷新したことです。
1. **`-hostobj`フラグの削除と`-linkmode`フラグの導入**:
* Goのリンカー(`src/cmd/5l/obj.c`, `src/cmd/6l/obj.c`, `src/cmd/8l/obj.c`)から、これまでのリンキングモードを制御していた`isobj`というブール型フラグ(およびそれに対応する`-hostobj`コマンドラインオプション)が削除されました。
* 代わりに、`setlinkmode`という新しい関数が導入され、`-linkmode`コマンドラインオプションが追加されました。このオプションは、"internal"、"external"、"auto"のいずれかの文字列を受け取ります。
* `setlinkmode`関数は、受け取った文字列に基づいて、`linkmode`という新しい変数(おそらく列挙型)の値を設定します。
* `LinkInternal`: 内部リンキングモード
* `LinkExternal`: 外部リンキングモード
* `LinkAuto`: リンカーが自動的に最適なモードを判断する
2. **リンキングモードの条件分岐の変更**:
* Goリンカーのコードベース全体で、`isobj`変数を参照していた箇所が、新しい`linkmode == LinkExternal`という条件に置き換えられました。これは、ELFリロケーションの生成、Mach-Oリロケーションの生成、DWARFデバッグ情報の処理、ELFヘッダの生成など、外部リンキングに特有の処理を行う部分に影響を与えます。
* 例えば、`src/cmd/5l/asm.c`や`src/cmd/6l/asm.c`、`src/cmd/8l/asm.c`では、`if(isobj)`が`if(linkmode == LinkExternal)`に変更されています。これは、外部リンキングの場合にのみ、ELFやMach-Oのリロケーション情報を出力する必要があるためです。
* `src/cmd/ld/data.c`では、シンボルのリロケーション処理において、`isobj`の代わりに`linkmode == LinkExternal`が使用されています。これは、外部リンキングの場合に、シンボルの解決方法やリロケーションの適用方法が異なるためです。
* `src/cmd/ld/elf.c`では、ELFファイルのセクションヘッダやプログラムヘッダの生成、エントリポイントの設定など、ELFフォーマット固有の処理において、`isobj`から`linkmode == LinkExternal`への変更が多数見られます。これにより、外部リンキング時にELFファイルの構造が適切に調整されるようになります。
3. **`LinkAuto`モードの導入**:
* `src/cmd/6l/obj.c`や`src/cmd/8l/obj.c`の`main`関数内で、`HEADTYPE`(ターゲットOS/アーキテクチャのヘッダタイプ)に基づいて`linkmode`が`LinkAuto`の場合に`LinkInternal`に設定されるロジックが追加されています。これは、特定のプラットフォームでは外部リンキングがサポートされていない場合に、自動的に内部リンキングにフォールバックするためのものです。
* また、`linkmode == LinkExternal`がサポートされていない`HEADTYPE`の場合に`sysfatal`(システムエラー)を発生させるチェックも追加されています。
4. **`src/cmd/ld/go.c`における変更**:
* `loadcgo`関数内で、`cgo_import_static`ディレクティブの処理において、`SHOSTOBJ`シンボルタイプの設定が`isobj`の条件分岐から外され、常に設定されるようになりました。これは、`SHOSTOBJ`が外部リンキングの有無に関わらず、Cgoによってインポートされたシンボルを示すようになったことを意味します。
* 新しい`setlinkmode`関数が定義され、コマンドライン引数からリンキングモードを解析し、`linkmode`変数を設定する役割を担っています。
5. **テストスクリプトの更新**:
* `src/run.bash`スクリプトが更新され、`go test`コマンドの`-ldflags`オプションで、`-hostobj`の代わりに`-linkmode=auto`や`-linkmode=external`が使用されるようになりました。これにより、新しいリンキングモードのフラグがビルドシステムで正しくテストされることが保証されます。
これらの変更により、Goリンカーのリンキングモードの制御がより明確で、将来の拡張性に対応できるような設計に進化しました。しかし、TLSの問題が解決されるまでは、外部リンキングモードはまだ実験的な段階にあることが示されています。
## コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更は、主に以下のパターンに集約されます。
1. **`isobj`変数の削除と`linkmode`変数の導入**:
* リンカーの各アーキテクチャ固有の`obj.c`ファイル(例: `src/cmd/5l/obj.c`, `src/cmd/6l/obj.c`, `src/cmd/8l/obj.c`)において、`flagcount("hostobj", ..., &isobj)`のような`-hostobj`フラグの定義が削除され、代わりに`flagfn1("linkmode", ..., setlinkmode)`という`-linkmode`フラグの定義が追加されています。
* これにより、リンキングモードの制御がブール値の`isobj`から、より詳細な`linkmode`列挙型(`LinkInternal`, `LinkExternal`, `LinkAuto`)に移行しました。
2. **`isobj`による条件分岐の`linkmode == LinkExternal`への置き換え**:
* リンカーのコードベース全体(`src/cmd/5l/asm.c`, `src/cmd/6l/asm.c`, `src/cmd/8l/asm.c`, `src/cmd/ld/data.c`, `src/cmd/ld/elf.c`など)で、外部リンキングに特有の処理を行う箇所の条件分岐が、`if(isobj)`から`if(linkmode == LinkExternal)`に変更されています。
**例1: `src/cmd/5l/asm.c`**
```diff
--- a/src/cmd/5l/asm.c
+++ b/src/cmd/5l/asm.c
@@ -605,7 +605,7 @@ asmb(void)
Bprint(&bso, "%5.2f dwarf\\n", cputime());
dwarfemitdebugsections();
- if(isobj)
+ if(linkmode == LinkExternal)
elfemitreloc();
}
break;
```
**例2: `src/cmd/ld/data.c`**
```diff
--- a/src/cmd/ld/data.c
+++ b/src/cmd/ld/data.c
@@ -178,11 +178,11 @@ relocsym(Sym *s)
switch(r->type) {
default:
o = 0;
- if(isobj || archreloc(r, s, &o) < 0)
+ if(linkmode == LinkExternal || archreloc(r, s, &o) < 0)
diag("unknown reloc %d", r->type);
break;
case D_ADDR:
- if(isobj && r->sym->type != SCONST) {
+ if(linkmode == LinkExternal && r->sym->type != SCONST) {
r->done = 0;
```
3. **`setlinkmode`関数の追加**:
* `src/cmd/ld/go.c`に、`-linkmode`フラグの引数を解析し、`linkmode`変数を設定する`setlinkmode`関数が追加されました。
**`src/cmd/ld/go.c`**
```go
void
setlinkmode(char *arg)
{
if(strcmp(arg, "internal") == 0)
linkmode = LinkInternal;
else if(strcmp(arg, "external") == 0)
linkmode = LinkExternal;
else if(strcmp(arg, "auto") == 0)
linkmode = LinkAuto;
else {
fprintf(2, "unknown link mode -linkmode %s\n", arg);
errorexit();
}
}
```
* この関数のプロトタイプは`src/cmd/ld/lib.h`に追加されています。
4. **`src/run.bash`におけるテストコマンドの更新**:
* ビルドテストスクリプトで、`-hostobj`の代わりに`-linkmode`が使用されるようになりました。
**`src/run.bash`**
```diff
--- a/src/run.bash
+++ b/src/run.bash
@@ -75,10 +75,11 @@ go run $GOROOT/test/run.go - .
[ "$CGO_ENABLED" != 1 ] ||
(xcd ../misc/cgo/test
-go test
+go test -ldflags '-linkmode=auto'
+go test -ldflags '-linkmode=internal'
case "$GOHOSTOS-$GOARCH" in
darwin-386 | darwin-amd64 | freebsd-386 | freebsd-amd64 | linux-386 | linux-amd64 | netbsd-386 | netbsd-amd64 | openbsd-386 | openbsd-amd64)
- go test -ldflags '-w -hostobj'
+ go test -ldflags '-linkmode=external'
esac
) || exit $?
```
これらの変更は、Goリンカーのリンキングモード制御の基盤を、より明確で拡張性の高いものへと移行させるものです。
## コアとなるコードの解説
このコミットの核心は、Goリンカーが外部リンカーを使用するかどうかを判断するロジックを、ブール型の`isobj`フラグから、より表現力豊かな`linkmode`列挙型に移行した点にあります。
以前は、`-hostobj`フラグが指定されると`isobj`が`true`になり、リンカーは外部リンキングに必要な処理(例えば、ELFやMach-Oのリロケーション情報の生成)を行っていました。しかし、このアプローチは、リンキングモードが「外部リンキング」であるという意図を直接的に示していませんでした。
新しい`linkmode`変数と`setlinkmode`関数は、リンキングの意図を明確に表現します。
* `linkmode = LinkInternal`: Goリンカーが全てを処理し、外部リンカーは使用しない。
* `linkmode = LinkExternal`: Goリンカーは外部リンカーと連携し、外部リンカーが最終的な実行可能ファイルを生成する。
* `linkmode = LinkAuto`: リンカーが環境やビルド設定に基づいて最適なモードを自動的に選択する。
コードの変更箇所では、`if(isobj)`という単純な条件分岐が`if(linkmode == LinkExternal)`に置き換えられています。これは、外部リンキングに特有の処理(例えば、`elfemitreloc()`や`machoemitreloc()`といったリロケーション情報の出力、またはELFヘッダの特定のフィールド設定)が、まさに`LinkExternal`モードの場合にのみ実行されるべきであることを明確に示しています。
例えば、`src/cmd/ld/data.c`の`relocsym`関数における変更は、リロケーション処理が外部リンキングの場合に異なる挙動をすることを示しています。`D_ADDR`や`D_PCREL`といったリロケーションタイプにおいて、`linkmode == LinkExternal`の場合に`r->done = 0`を設定しているのは、これらのリロケーションがGoリンカーによって完全に解決されるのではなく、外部リンカーによって最終的に解決される必要があることを示唆しています。
また、`src/cmd/ld/go.c`の`loadcgo`関数における`cgo_import_static`の処理から`isobj`の条件分岐が削除されたことは、`SHOSTOBJ`シンボルタイプが、外部リンキングの有無に関わらず、Cgoによってインポートされたシンボルを示すようになったことを意味します。これは、リンキングモードの概念がより洗練され、個々のシンボルの特性とリンキングモードがより独立して扱われるようになったことを示唆しています。
全体として、この変更はGoリンカーの内部構造をよりモジュール化し、リンキングモードの概念をより明確にすることで、将来的なリンキング機能の拡張やデバッグを容易にすることを目的としています。ただし、TLSの問題が解決されるまでは、`LinkExternal`モードの完全な利用は制限されるという注意書きが重要です。
## 関連リンク
* Go言語のリンカーに関する公式ドキュメント(もしあれば、Goの公式ドキュメントサイトで`cmd/ld`やリンキングに関するセクションを探す)
* Go言語のCgoに関するドキュメント
* ELFフォーマットの仕様
* Mach-Oフォーマットの仕様
* Thread Local Storage (TLS) に関する一般的な情報
## 参考にした情報源リンク
* [Go言語のソースコード (GitHub)](https://github.com/golang/go)
* [Go言語の公式ドキュメント](https://golang.org/doc/)
* [GoのIssue Tracker (TLS関連のIssueがあれば)](https://github.com/golang/go/issues)
* [Wikipedia: Executable and Linkable Format (ELF)](https://ja.wikipedia.org/wiki/Executable_and_Linkable_Format)
* [Wikipedia: Mach-O](https://ja.wikipedia.org/wiki/Mach-O)
* [Wikipedia: Thread-local storage](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8)
* [golang.org/cl/7783044](https://golang.org/cl/7783044) (このコミットに対応するGoのコードレビューシステム上の変更リスト)
* このリンクはコミットメッセージに記載されているもので、Goのコードレビューシステム(Gerrit)へのリンクです。詳細な議論や関連する変更履歴が確認できます。
* **注**: `golang.org/cl/` はGoプロジェクトのGerritインスタンスへのリンクであり、コミットの詳細な議論やレビューコメントが含まれている可能性があります。これは、コミットの背景や意図を深く理解するための非常に貴重な情報源となります。
* このリンクを直接参照することで、コミットの意図や議論の経緯をより深く理解できます。