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

[インデックス 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つのリンキングモードを持っています。

  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.csrc/cmd/6l/asm.csrc/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.csrc/cmd/8l/obj.cmain関数内で、HEADTYPE(ターゲットOS/アーキテクチャのヘッダタイプ)に基づいてlinkmodeLinkAutoの場合に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

    --- 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;
    
  3. 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に追加されています。
  4. 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フラグが指定されるとisobjtrueになり、リンカーは外部リンキングに必要な処理(例えば、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.crelocsym関数における変更は、リロケーション処理が外部リンキングの場合に異なる挙動をすることを示しています。D_ADDRD_PCRELといったリロケーションタイプにおいて、linkmode == LinkExternalの場合にr->done = 0を設定しているのは、これらのリロケーションがGoリンカーによって完全に解決されるのではなく、外部リンカーによって最終的に解決される必要があることを示唆しています。

また、src/cmd/ld/go.cloadcgo関数における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インスタンスへのリンクであり、コミットの詳細な議論やレビューコメントが含まれている可能性があります。これは、コミットの背景や意図を深く理解するための非常に貴重な情報源となります。
    *   このリンクを直接参照することで、コミットの意図や議論の経緯をより深く理解できます。