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

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

このコミットは、Go言語のビルドシステムとリンカに関する重要な変更を導入しています。特に、macOS (旧称 OS X) の特定のバージョンにおけるシステムリンカのバグに対応し、Goプログラムのリンクモードのデフォルト挙動を調整することが目的です。

コミット

commit 3197be48078311368d5a37a89c61bbd47e00622f
Author: Ian Lance Taylor <iant@golang.org>
Date:   Fri Mar 29 16:33:35 2013 -0700

    cmd/dist, cmd/ld: GO_EXTLINK_ENABLED=0 defaults to -linkmode=internal
    
    Change build system to set GO_EXTLINK_ENABLED=0 by default for
    OS X 10.6, since the system linker has a bug and can not
    handle the object files generated by 6l.
    
    Fixes #5130.
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/8183043

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

https://github.com/golang/go/commit/3197be48078311368d5a37a89c61bbd47e00622f

元コミット内容

このコミットは、cmd/dist (Goのビルドシステム) と cmd/ld (Goのリンカ) に関連する変更です。主な内容は、GO_EXTLINK_ENABLED=0 がデフォルトで -linkmode=internal に設定されるようにすることです。これは特に OS X 10.6 環境において、システムリンカが 6l (Goのx86-64リンカ) によって生成されたオブジェクトファイルを適切に処理できないバグが存在するためです。この変更は、Go issue #5130 を解決することを目的としています。

変更の背景

Go言語のプログラムをビルドする際、コンパイラが生成したオブジェクトファイルを最終的な実行可能ファイルに結合するためにリンカが使用されます。Goには独自の内部リンカがありますが、Cgo(GoからC言語のコードを呼び出す機能)を使用する場合など、外部のシステムリンカ(例: gccclang)を利用する「外部リンクモード」も存在します。

Go issue #5130 は、OS X 10.6 (Snow Leopard) のシステムリンカに存在する特定のバグを報告しています。このバグにより、Goのコンパイラ(特に 6l、x86-64アーキテクチャ用のリンカ)が生成するオブジェクトファイルを、OS X 10.6 のシステムリンカが正しく処理できず、外部リンクモードでのビルドが失敗するという問題が発生していました。

この問題に対処するため、Goチームは OS X 10.6 環境でのデフォルトのリンクモードを内部リンカを使用するモードに強制的に変更する必要がありました。これにより、ユーザーが明示的に外部リンクモードを指定しない限り、バグのあるシステムリンカが使用されることを避け、ビルドの成功を保証します。

前提知識の解説

  • Go言語のビルドプロセス: Goプログラムは、ソースコードがコンパイラによってオブジェクトファイルに変換され、その後リンカによって実行可能ファイルに結合されます。
  • リンカ (Linker): オブジェクトファイルやライブラリを結合して実行可能ファイルを作成するプログラムです。
  • 内部リンカ (Internal Linker): Go言語自体が提供するリンカです。Goのランタイムや標準ライブラリを静的にリンクする際に使用されます。
  • 外部リンカ (External Linker): オペレーティングシステムが提供するリンカ(例: macOSの ld、Linuxの ld、Windowsの link.exe)です。Cgoを使用する場合や、特定のシステムライブラリに依存する場合に利用されます。
  • -linkmode オプション: Goのビルドコマンド (go buildgo install) で使用できるオプションで、リンカの動作を指定します。
    • -linkmode=internal: 常にGoの内部リンカを使用します。
    • -linkmode=external: 常に外部リンカを使用します。
    • -linkmode=auto: Goが自動的に最適なリンカを選択します。通常、Cgoを使用する場合は外部リンカを、それ以外の場合は内部リンカを選択します。
  • GO_EXTLINK_ENABLED 環境変数: Goのビルド時に、外部リンカの使用を許可するかどうかを制御する環境変数です。
    • GO_EXTLINK_ENABLED=1: 外部リンカの使用を許可します。
    • GO_EXTLINK_ENABLED=0: 外部リンカの使用を禁止し、内部リンカを強制します。
  • OS X 10.6 (Snow Leopard): 2009年にリリースされたmacOSのバージョンです。このコミットが作成された2013年時点ではまだ広く使用されていましたが、システムリンカに特定のバグが存在していました。
  • 6l: Goのツールチェーンにおける、x86-64アーキテクチャ向けのリンカの旧称です。現在は go tool link に統合されています。

技術的詳細

このコミットの技術的詳細は、Goのビルドシステムがどのように環境変数を読み取り、それに基づいてリンカの挙動を調整するか、そして特定のOSバージョンでの特殊なケースをどのように扱うかを示しています。

  1. GO_EXTLINK_ENABLED の導入:

    • src/cmd/dist/a.hsrc/cmd/dist/build.cgoextlinkenabled という変数が導入され、GO_EXTLINK_ENABLED 環境変数の値を保持するようになりました。
    • src/cmd/dist/build.cinit 関数内で、GO_EXTLINK_ENABLED 環境変数が読み取られ、その値が 0 または 1 でない場合はエラーを発生させます。これにより、この環境変数の値が厳密に制御されます。
    • ビルド時に、この goextlinkenabled の値がコンパイラに渡されるようになります。
  2. OS X 10.6 の検出と GO_EXTLINK_ENABLED の設定:

    • src/cmd/dist/unix.c は、GoのビルドシステムがUnix系システムで動作する際のロジックを含んでいます。
    • このファイルに、ホストOSが darwin (macOS) であるかどうかをチェックする新しいロジックが追加されました。
    • さらに、uname(&u) システムコールを使用してOSのリリースバージョンを取得し、それが 10. で始まるか、または最初の数字が 10 以下である場合に(これは OS X 10.6 以前のバージョンを示す一般的なパターンです)、goextlinkenabled"0" に設定します。これにより、OS X 10.6 環境ではデフォルトで外部リンカが無効化されます。
  3. リンカの挙動変更:

    • src/cmd/5l/obj.c (ARMリンカ), src/cmd/6l/obj.c (x86-64リンカ), src/cmd/8l/obj.c (x86リンカ) の各リンカのソースコードが変更されました。
    • これらのファイルでは、main 関数内で linkmode == LinkAuto かつ getgoextlinkenabled()"0" を返す場合、linkmodeLinkInternal に強制的に設定するロジックが追加されました。これは、GO_EXTLINK_ENABLED=0 が設定されている場合に、自動選択モードであっても内部リンカを使用することを意味します。
    • また、6l8l では、linkmode == LinkExternal かつ getgoextlinkenabled()"1" でない場合に、外部リンカを使用できないというエラーを発生させる条件が追加されました。これは、外部リンカが明示的に無効化されているにもかかわらず、ユーザーが外部リンクモードを強制しようとした場合にエラーを出すためのものです。
  4. getgoextlinkenabled 関数の実装:

    • src/lib9/goos.cgetgoextlinkenabled 関数が追加されました。この関数は、ビルド時に設定された GO_EXTLINK_ENABLED の値を返します。これは、リンカがビルド時の設定を参照するためのメカニズムです。
  5. テストスクリプトの調整:

    • src/run.bash はGoのテストスイートを実行するスクリプトです。
    • このスクリプトが変更され、darwin-386 および darwin-amd64 環境において、uname -r の結果が [0-9].* または 10.* (OS X 10.6 以前のバージョンを示す) である場合、-ldflags '-linkmode=external' を使用したテストをスキップするようになりました。これは、バグのあるリンカが原因でテストが失敗するのを避けるためです。

これらの変更により、GoのビルドシステムはOS X 10.6 環境を自動的に検出し、外部リンカのバグを回避するためにデフォルトで内部リンカを使用するようになります。これにより、ユーザーは特別な設定なしにGoプログラムを正常にビルドできるようになります。

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

このコミットにおけるコアとなるコードの変更箇所は以下のファイルに集中しています。

  • src/cmd/dist/unix.c: OS X 10.6 の検出と goextlinkenabled の設定ロジックが追加されています。

    --- a/src/cmd/dist/unix.c
    +++ b/src/cmd/dist/unix.c
    @@ -698,6 +698,19 @@ main(int argc, char **argv)
     	if(strcmp(gohostarch, "arm") == 0)
     		maxnbg = 1;
     
    +	// The OS X 10.6 linker does not support external
    +	// linking mode; see
    +	// https://code.google.com/p/go/issues/detail?id=5130 .
    +	// The mapping from the uname release field to the OS X
    +	// version number is complicated, but basically 10 or under is
    +	// OS X 10.6 or earlier.
    +	if(strcmp(gohostos, "darwin") == 0) {
    +		if(uname(&u) < 0)
    +			fatal("uname: %s", strerror(errno));
    +		if(u.release[1] == '.' || hasprefix(u.release, "10"))
    +			goextlinkenabled = "0";
    +	}
    +
     	init();
     	xmain(argc, argv);
     	bfree(&b);
    
  • src/cmd/6l/obj.c (および src/cmd/5l/obj.c, src/cmd/8l/obj.c): リンカが GO_EXTLINK_ENABLED の値に基づいてリンクモードを調整するロジックが追加されています。

    --- a/src/cmd/6l/obj.c
    +++ b/src/cmd/6l/obj.c
    @@ -133,11 +133,16 @@ main(int argc, char *argv[])
     	if(HEADTYPE == -1)
     		HEADTYPE = headtype(goos);
     
    +	// getgoextlinkenabled is based on GO_EXTLINK_ENABLED when
    +	// Go was built; see ../../make.bash.
    +	if(linkmode == LinkAuto && strcmp(getgoextlinkenabled(), "0") == 0)
    +		linkmode = LinkInternal;
    +
     	switch(HEADTYPE) {
     	default:
     		if(linkmode == LinkAuto)
     			linkmode = LinkInternal;
    -		if(linkmode == LinkExternal)
    +		if(linkmode == LinkExternal && strcmp(getgoextlinkenabled(), "1") != 0)
     			sysfatal("cannot use -linkmode=external with -H %s", headstr(HEADTYPE));
     		break;
     	case Hdarwin:
    
  • src/lib9/goos.c: getgoextlinkenabled 関数の実装が追加されています。

    --- a/src/lib9/goos.c
    +++ b/src/lib9/goos.c
    @@ -51,3 +51,9 @@ getgo386(void)
     {
     	return defgetenv("GO386", GO386);
     }
    +
    +char *
    +getgoextlinkenabled(void)
    +{
    +	return GO_EXTLINK_ENABLED;
    +}
    

コアとなるコードの解説

  • src/cmd/dist/unix.c の変更:

    • if(strcmp(gohostos, "darwin") == 0): ホストOSがmacOSであるかをチェックします。
    • if(u.release[1] == '.' || hasprefix(u.release, "10")): uname -r コマンドで取得されるOSのリリースバージョン文字列をチェックします。macOSのリリースバージョンは X.Y.Z の形式で、10.610.6.0 のように表現されます。u.release[1] == '.'X. の形式(例: 9. は OS X 10.5 以前)を、hasprefix(u.release, "10")10. で始まるバージョン(例: 10.6)を検出します。これにより、OS X 10.6 以前のバージョンを特定し、goextlinkenabled = "0" を設定して外部リンカを無効化します。
  • src/cmd/6l/obj.c (リンカ) の変更:

    • if(linkmode == LinkAuto && strcmp(getgoextlinkenabled(), "0") == 0): リンカが自動選択モード (LinkAuto) であり、かつビルド時に GO_EXTLINK_ENABLED0 に設定されていた場合(つまり、外部リンカが明示的に無効化されている場合)、linkmodeLinkInternal に強制的に変更します。これにより、OS X 10.6 のような環境で外部リンカのバグを回避できます。
    • if(linkmode == LinkExternal && strcmp(getgoextlinkenabled(), "1") != 0): ユーザーが明示的に外部リンクモード (LinkExternal) を指定しているにもかかわらず、ビルド時に GO_EXTLINK_ENABLED1 でない場合(つまり、外部リンカが許可されていない場合)、sysfatal を呼び出して致命的なエラーを発生させます。これは、矛盾する設定が与えられた場合のガードレールとして機能します。
  • src/lib9/goos.cgetgoextlinkenabled:

    • この関数は、Goのビルドシステムによって設定された GO_EXTLINK_ENABLED の値を、Goのランタイムやリンカが参照できるようにするためのシンプルなラッパーです。これにより、ビルド時の環境設定が実行時のリンカの挙動に影響を与えることができます。

これらの変更は、Goのビルドシステムが特定のOS環境の特性(この場合はOS X 10.6 のリンカバグ)を認識し、それに応じてビルドの挙動を自動的に調整することで、ユーザーエクスペリエンスを向上させるための堅牢なメカニズムを構築していることを示しています。

関連リンク

参考にした情報源リンク

  • Go issue #5130 の内容
  • Go言語のビルドシステムとリンカに関する一般的な知識
  • uname コマンドとmacOSのバージョン番号の対応に関する情報
  • Goのソースコード内のコメントと既存のコードパターン