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

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

このコミットは、Go言語のコマンドラインツール(cmd/go)のテストスクリプトである src/cmd/go/test.bash に関連する変更です。具体的には、cgo を使用した共有ライブラリの検索パスに関するテストにおいて、macOS (Darwin) システムでの問題を回避するための修正が含まれています。

コミット

commit ff5f9bbf6a5d5281a2bf5326ce43df65deef4ac1
Author: Dave Cheney <dave@cheney.net>
Date:   Mon Feb 10 13:35:39 2014 +1100

    cmd/go: skip $ORIGIN test on darwin systems
    
    Fixes #7293.
    
    Update #7261
    
    The bsd ld(1) does not understand $ORIGIN and has restrictions on using -rpath when using clang(1), the default compiler on darwin.
    
    LGTM=iant
    R=iant
    CC=golang-codereviews
    https://golang.org/cl/58480045

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

https://github.com/golang/go/commit/ff5f9bbf6a5d5281a2bf5326ce43df65deef4ac1

元コミット内容

cmd/go: skip $ORIGIN test on darwin systems

Fixes #7293.

Update #7261

The bsd ld(1) does not understand $ORIGIN and has restrictions on using -rpath when using clang(1), the default compiler on darwin.

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/58480045

変更の背景

このコミットは、Goの cmd/go コマンドのテストスイートがmacOS (Darwin) システム上で失敗する問題を解決するために導入されました。問題の根源は、cgo を使用してC言語のコードをGoプログラムにリンクする際に、共有ライブラリの検索パスを指定するために使用される $ORIGIN というリンカの特殊な変数が、macOSのデフォルトリンカである bsd ld(1) によって正しく解釈されないことにありました。

さらに、macOSでは clang(1) がデフォルトのコンパイラとして使用されており、この clang(1)bsd ld(1) の組み合わせにおいて、-rpath リンカオプションの使用に特定の制限が存在します。これらの要因が複合的に作用し、$ORIGIN を利用した rpath の設定を含むテストがmacOS環境で期待通りに動作せず、テストの失敗を引き起こしていました。

この問題は、GoのIssue #7293として報告されており、このコミットはその修正を目的としています。また、Issue #7261に関連する更新も含まれています。

前提知識の解説

このコミットの変更内容を理解するためには、以下の技術的な概念を把握しておく必要があります。

1. $ORIGIN

$ORIGIN は、ELF (Executable and Linkable Format) 形式の実行ファイルや共有ライブラリにおいて、動的リンカが実行時に解釈する特殊なトークンです。これは、現在ロードされている実行ファイルまたは共有ライブラリが配置されているディレクトリのパスを表します。 例えば、/usr/local/bin/myprog という実行ファイルが /usr/local/lib/mylib.so という共有ライブラリに依存しており、myprogrpath$ORIGIN/../lib が指定されている場合、リンカは myprog のディレクトリ(/usr/local/bin)を基準として ../lib、つまり /usr/local/lib を共有ライブラリの検索パスとして使用します。 この機能は、アプリケーションとその依存ライブラリを相対パスで配置し、インストールパスに依存しないポータブルなバイナリを作成する際に非常に有用です。しかし、$ORIGIN は主にLinuxなどのELFベースのシステムでサポートされており、macOS (Darwin) のようなMach-Oベースのシステムでは直接的な同等の機能がありません。macOSでは @loader_path@executable_path といった独自の相対パス指定メカニズムが提供されています。

2. ld(1) (リンカ)

ld は "linker" の略で、コンパイラによって生成されたオブジェクトファイル(.o ファイル)やライブラリファイル(.a.so/.dylib ファイル)を結合し、最終的な実行ファイルや共有ライブラリを生成するプログラムです。 リンカは、未解決のシンボル(関数や変数の参照)を解決し、適切なメモリアドレスに配置する役割を担います。 macOSでは、BSD系の ld が使用されており、その挙動はLinuxなどで一般的に使用されるGNU ld とは異なる場合があります。この違いが、本コミットで扱われる問題の根源の一つです。

3. -rpath リンカオプション

-rpath (Run-time search path) は、リンカに対して、生成される実行ファイルや共有ライブラリが実行時に依存する共有ライブラリを検索するための追加パスを指定するオプションです。 通常、共有ライブラリはシステム標準のパス(例: /lib, /usr/lib)や環境変数 LD_LIBRARY_PATH (Linux) / DYLD_LIBRARY_PATH (macOS) を参照して検索されますが、-rpath を使用することで、バイナリ自体に検索パスを埋め込むことができます。これにより、環境変数に依存せずに特定のパスからライブラリをロードさせることが可能になります。 セキュリティ上の理由や、特定の環境でのみ有効なパスを指定するために使用されることがあります。

4. clang(1)

clang は、LLVMプロジェクトの一部として開発されているC、C++、Objective-C、Objective-C++ のコンパイラフロントエンドです。Apple製品の開発環境であるXcodeに同梱されており、macOSのデフォルトコンパイラとして広く使用されています。 clang は、GCC (GNU Compiler Collection) と高い互換性を持ちながら、より高速なコンパイル、優れたエラー診断、モジュール性などの利点を提供します。

5. cgo

cgo は、Go言語のプログラムからC言語のコードを呼び出したり、逆にC言語のコードからGo言語の関数を呼び出したりするためのGoツールチェーンの一部です。 Goのソースファイル内に import "C" という行を記述し、その直前のコメントブロックにC言語のコードを記述することで、GoとCの相互運用が可能になります。 cgo は、Cコンパイラやリンカに渡すオプションを指定するための特別なディレクティブ // #cgo をサポートしています。例えば、// #cgo LDFLAGS: -L/path/to/lib -lmylib のように記述することで、リンカにライブラリのパスやライブラリ名を渡すことができます。

技術的詳細

このコミットの技術的な核心は、macOS (Darwin) 環境における動的リンカの挙動と cgo の連携に関するものです。

Goの cmd/go コマンドのテストスイートには、cgo を利用して外部Cライブラリをリンクし、そのライブラリの検索パスに $ORIGIN を含む -rpath を指定するテストケースが含まれていました。このテストケースは、Goプログラムが自身の実行ファイルからの相対パスで共有ライブラリをロードできることを検証することを目的としていました。

しかし、macOSでは以下の問題が発生します。

  1. bsd ld(1)$ORIGIN 非サポート: macOSのデフォルトリンカである bsd ld(1) は、ELF形式のシステムで一般的に使用される $ORIGIN という特殊なトークンを理解しません。macOSの実行ファイル形式はMach-Oであり、ELFとは異なる動的リンカのメカニズムを持っています。そのため、-rpath,$ORIGIN のような指定は bsd ld(1) によって正しく解釈されず、リンカエラーや実行時のライブラリロード失敗につながります。

  2. clang(1)-rpath の制限: macOSのデフォルトコンパイラである clang(1) を使用して -rpath オプションを bsd ld(1) に渡す場合、特定の制限や挙動の違いがあります。特に、$ORIGIN のような動的なパス指定は、macOSのセキュリティモデルやリンカの設計思想と合致しないため、問題を引き起こしやすいです。macOSでは、@loader_path@executable_path といった独自の相対パス指定メカニズムが提供されていますが、これらは $ORIGIN とは異なる概念であり、Goのテストケースでは $ORIGIN が直接使用されていました。

これらの理由により、src/cmd/go/test.bash 内の cgo テストがmacOS上で失敗していました。このコミットは、この特定のテストケースがmacOS環境では実行されないようにすることで、テストスイート全体の安定性を確保しています。これは、macOSのリンカの特性に起因する根本的な問題を回避するための実用的なアプローチです。

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

変更は src/cmd/go/test.bash ファイルの1箇所のみです。

--- a/src/cmd/go/test.bash
+++ b/src/cmd/go/test.bash
@@ -608,7 +608,7 @@ export GOPATH=$d
 mkdir -p $d/src/origin
 echo '
 package origin
-// #cgo LDFLAGS: -Wl,-rpath -Wl,$ORIGIN
+// #cgo !darwin LDFLAGS: -Wl,-rpath -Wl,$ORIGIN
 // void f(void) {}
 import "C"
 

コアとなるコードの解説

変更された行は、src/cmd/go/test.bash スクリプト内で生成されるGoパッケージ origin のソースコードの一部です。このGoパッケージは、cgo を使用してC言語の関数 f をインポートしています。

元のコード:

// #cgo LDFLAGS: -Wl,-rpath -Wl,$ORIGIN

この行は、cgo ディレクティブを使用して、Cコンパイラ/リンカに LDFLAGS (リンカフラグ) を渡しています。具体的には、-Wl,-rpath -Wl,$ORIGIN というフラグを渡しています。

  • -Wl, は、続くオプションをリンカに渡すことを意味します。
  • -rpath は、実行時の共有ライブラリ検索パスを指定するリンカオプションです。
  • $ORIGIN は、前述の通り、実行ファイル自身のディレクトリを指す特殊な変数です。

このディレクティブは、生成されるバイナリが $ORIGIN を基準とした相対パスで共有ライブラリを検索するようにリンカに指示していました。

変更後のコード:

// #cgo !darwin LDFLAGS: -Wl,-rpath -Wl,$ORIGIN

この変更では、#cgo ディレクティブに !darwin というビルドタグが追加されています。

  • !darwin は、Goのビルドタグの一種で、「Darwin (macOS) 以外のシステムでのみこの行を有効にする」という意味を持ちます。

したがって、この変更により、// #cgo LDFLAGS: -Wl,-rpath -Wl,$ORIGIN というリンカフラグは、macOS以外のシステム(Linuxなど)でのみ適用されるようになります。macOS上では、この行は無視され、$ORIGIN を含む rpath の設定は行われなくなります。これにより、macOSのリンカが $ORIGIN を理解しないことによるテストの失敗が回避されます。

この修正は、Goのクロスプラットフォーム対応において、特定のOSのリンカの挙動の違いを吸収するための典型的なアプローチを示しています。

関連リンク

参考にした情報源リンク