[インデックス 13666] ファイルの概要
このコミットは、Go言語のツールチェイン、特にcmd/go
におけるNetBSD環境でのcgoリンキングの問題を修正するものです。具体的には、NetBSDのリンカがld -r
(再配置可能出力の生成)を使用する際にSEARCH_DIR
を提供しないため、libgcc.a
が見つからない問題を解決します。この修正は、gcc -print-libgcc-file-name
オプションを使用してlibgcc
のパスを明示的に取得し、リンキング時にそのパスを参照することで実現されています。
コミット
commit 2281c3294b01501b177670e21d571ca7d5074cbf
Author: Joel Sing <jsing@google.com>
Date: Wed Aug 22 22:23:56 2012 +1000
cmd/go: fix cgo linking on netbsd
NetBSD's built-in linker script for 'ld -r' does not provide a
SEARCH_DIR. As a result libgcc.a is not found when -lgcc is used.
Work around this by determining the path to libgcc (by invoking
gcc with the -print-libgcc-file-name option) and explicitly
referencing the resulting library.
R=golang-dev, iant, aram, lucio.dere, minux.ma
CC=golang-dev
https://golang.org/cl/6470044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2281c3294b01501b177670e21d571ca7d5074cbf
元コミット内容
cmd/go
: NetBSD上でのcgoリンキングを修正。
NetBSDのld -r
(再配置可能出力の生成)用の組み込みリンカスクリプトがSEARCH_DIR
を提供しないため、-lgcc
が使用された際にlibgcc.a
が見つからない問題が発生していました。
この問題を回避するため、gcc
を-print-libgcc-file-name
オプション付きで呼び出すことでlibgcc
へのパスを特定し、結果として得られるライブラリを明示的に参照するようにしました。
変更の背景
この変更は、Go言語のcgo機能がNetBSDオペレーティングシステム上で正しく動作しないという特定の問題に対処するために行われました。cgoはGoプログラムからCコードを呼び出すためのメカニズムであり、その過程でCコンパイラ(通常はGCC)とリンカが使用されます。
問題の核心は、NetBSDのリンカ(ld
)が、再配置可能なオブジェクトファイルを生成するモード(-r
オプション)で動作する際に、標準的なライブラリ検索パス(SEARCH_DIR
)を適切に設定しないことにありました。これにより、GoのビルドプロセスがCコードとリンクしようとした際に、GCCのランタイムサポートライブラリであるlibgcc.a
を見つけることができませんでした。libgcc.a
は、整数演算のオーバーフローチェック、浮動小数点演算のサポート、その他の低レベルなランタイム機能など、GCCが生成するコードが依存する基本的な関数を提供します。
libgcc.a
が見つからないと、cgoを使用するGoプログラムのビルドが失敗し、NetBSDユーザーがGoとCの相互運用機能を利用できなくなります。このコミットは、このプラットフォーム固有のリンカの挙動を回避し、libgcc.a
へのパスを動的に特定して明示的にリンカに渡すことで、ビルドを成功させることを目的としています。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念を把握しておく必要があります。
-
cgo:
- Go言語の機能の一つで、GoプログラムからC言語の関数を呼び出したり、C言語のコードをGoプログラムに組み込んだりすることを可能にします。
- cgoを使用すると、GoのビルドプロセスはCコンパイラ(通常はGCC)を呼び出してCソースファイルをコンパイルし、その結果生成されたオブジェクトファイルをGoのパッケージとリンクします。
- このリンキングの過程で、C言語の標準ライブラリや、GCCが提供するランタイムライブラリ(
libgcc.a
など)が必要になることがあります。
-
リンカ (
ld
):- コンパイラが生成したオブジェクトファイル(
.o
ファイル)やライブラリファイル(.a
や.so
ファイル)を結合して、実行可能ファイルや共有ライブラリを生成するプログラムです。 -r
オプション: リンカのオプションの一つで、複数の入力オブジェクトファイルを結合して、単一の再配置可能なオブジェクトファイル(実行可能ファイルではなく、別のリンキングステップでさらに結合できる中間ファイル)を生成します。これは、複数のCソースファイルをコンパイルして一つの大きなオブジェクトファイルにまとめる際などに使用されます。- リンカスクリプト: リンカの動作を制御するためのテキストファイルです。どのセクションをどこに配置するか、どのシンボルをエクスポートするか、どのライブラリを検索するか、といった詳細な指示をリンカに与えます。
SEARCH_DIR
: リンカスクリプト内で定義される、ライブラリを検索するためのディレクトリパスを指定するディレクティブです。リンカは、-l
オプションで指定されたライブラリ(例:-lgcc
)を、これらのSEARCH_DIR
で指定されたパスの中から探します。
- コンパイラが生成したオブジェクトファイル(
-
libgcc.a
:- GCC(GNU Compiler Collection)のランタイムサポートライブラリです。
- C言語のコードが特定のハードウェア命令に直接マッピングできないような複雑な操作(例: 64ビット整数に対する32ビットCPUでの除算、浮動小数点演算のソフトウェアエミュレーション、アトミック操作など)を行う際に、GCCが生成するコードが依存するヘルパー関数が含まれています。
- 通常、GCCがインストールされているディレクトリの特定のサブディレクトリ(例:
/usr/lib/gcc/x86_64-linux-gnu/X.Y.Z/libgcc.a
)に配置されます。
-
gcc -print-libgcc-file-name
:- GCCコンパイラのオプションの一つです。
- このオプションを付けて
gcc
を実行すると、libgcc.a
ファイルの完全なパスが標準出力に出力されます。 - これは、特定のシステム上で
libgcc.a
がどこにインストールされているかをプログラム的に、またはスクリプトから動的に特定するのに非常に便利な方法です。
技術的詳細
この問題は、NetBSDのリンカがld -r
モードで動作する際に、標準的なライブラリ検索パス(SEARCH_DIR
)をリンカスクリプトに含めないという、プラットフォーム固有の挙動に起因しています。通常、リンカは-l
オプション(例: -lgcc
)で指定されたライブラリを、デフォルトの検索パスやリンカスクリプトで指定されたパスから探します。しかし、NetBSDのld -r
の場合、この検索パスが欠落しているため、libgcc.a
が見つからず、リンキングエラーが発生していました。
Goのビルドシステム、特にcgoの処理では、Cコードをコンパイルした後、そのオブジェクトファイルをGoのコードとリンクする際に、libgcc.a
のようなランタイムライブラリが必要となることがあります。Goのビルドシステムは、通常-lgcc
というリンカフラグを渡すことでlibgcc.a
へのリンクを試みます。しかし、NetBSDのリンカの挙動により、このフラグだけではlibgcc.a
を見つけることができませんでした。
このコミットによる解決策は、この問題を回避するために、libgcc.a
へのパスを明示的にリンカに渡すというアプローチを取っています。そのために、以下のステップが導入されました。
libgcc.a
のパスの動的取得:gcc -print-libgcc-file-name
コマンドを実行することで、現在のシステムにおけるlibgcc.a
の正確なファイルパスを取得します。このコマンドは、GCCが自身のランタイムライブラリがどこにあるかを知っていることを利用しています。- 明示的なリンキング: 取得した
libgcc.a
のフルパスを、-lgcc
の代わりにリンカに直接渡します。これにより、リンカはSEARCH_DIR
に依存することなく、指定された正確なパスからlibgcc.a
をロードしてリンクすることができます。
このアプローチにより、NetBSDのリンカの特殊な挙動に依存することなく、cgoがlibgcc.a
を正しくリンクできるようになり、NetBSD上でのGoとCの相互運用性が確保されました。
コアとなるコードの変更箇所
diff --git a/src/cmd/go/build.go b/src/cmd/go/build.go
index e12698f9f0..fd11a4dcba 100644
--- a/src/cmd/go/build.go
+++ b/src/cmd/go/build.go
@@ -1427,6 +1427,16 @@ func gccgoPrefix(p *Package) string {
return "go_" + p.ImportPath
}
+// libgcc returns the filename for libgcc, as determined by invoking gcc with
+// the -print-libgcc-file-name option.
+func (b *builder) libgcc(p *Package) (string, error) {
+ f, err := b.runOut(p.Dir, p.ImportPath, b.gccCmd(p.Dir), "-print-libgcc-file-name")
+ if err != nil {
+ return "", nil
+ }
+ return strings.Trim(string(f), "\r\n"), nil
+}
+
// gcc runs the gcc C compiler to create an object from a single C file.
func (b *builder) gcc(p *Package, out string, flags []string, cfile string) error {
cfile = mkAbs(p.Dir, cfile)
@@ -1571,7 +1581,11 @@ func (b *builder) cgo(p *Package, cgoExe, obj string, gccfiles []gccfile) (outGo,\n \tbareLDFLAGS = append(bareLDFLAGS, f)\n \t\t}\n \t}\n-\tstaticLibs := []string{"-lgcc"}\n+\tlibgcc, err := b.libgcc(p)\n+\tif err != nil {\n+\t\treturn nil, nil, err\t}\n+\tstaticLibs := []string{libgcc}\n \tif goos == "windows" {\n \t\tstaticLibs = append(staticLibs, "-lmingwex", "-lmingw32")\n \t}\n```
## コアとなるコードの解説
このコミットでは、`src/cmd/go/build.go`ファイルに2つの主要な変更が加えられています。
1. **`libgcc`関数の追加**:
* 新しく`libgcc`というメソッドが`builder`構造体に追加されました。
* この関数の目的は、`gcc -print-libgcc-file-name`コマンドを実行し、その出力から`libgcc.a`のフルパスを取得することです。
* `b.runOut(p.Dir, p.ImportPath, b.gccCmd(p.Dir), "-print-libgcc-file-name")`は、指定されたディレクトリ(`p.Dir`)で`gcc`コマンドを`-print-libgcc-file-name`オプション付きで実行し、その標準出力をバイトスライスとして返します。
* エラーが発生した場合(例: `gcc`が見つからない、コマンドの実行に失敗したなど)は、空文字列とエラーを返します。
* 取得した出力(`f`)は、`strings.Trim(string(f), "\r\n")`によって改行コード(CRLFまたはLF)が除去され、クリーンなファイルパス文字列として返されます。
2. **`cgo`関数での`libgcc`の利用**:
* `cgo`関数は、cgoのビルドプロセスにおいてCコードをリンクする役割を担っています。
* 変更前は、`staticLibs`スライスに`-lgcc`という文字列が直接追加されていました。これは、リンカに`libgcc`という名前のライブラリを検索してリンクするように指示するものです。
* 変更後、まず`b.libgcc(p)`を呼び出して`libgcc.a`の正確なパスを取得します。
* もし`libgcc`のパスの取得に失敗した場合、エラーを返して処理を中断します。
* 取得した`libgcc`のフルパスが、`-lgcc`の代わりに`staticLibs`スライスに追加されます。これにより、リンカは`libgcc.a`を検索パスから探すのではなく、指定された絶対パスから直接ロードするようになります。
この変更により、NetBSDのリンカが`SEARCH_DIR`を提供しないという問題が回避され、`libgcc.a`が常に正しく見つけられるようになり、NetBSD上でのcgoのリンキングが成功するようになりました。
## 関連リンク
* Go言語のcgoに関する公式ドキュメント: [https://pkg.go.dev/cmd/cgo](https://pkg.go.dev/cmd/cgo)
* GCCのドキュメント(`-print-libgcc-file-name`オプションについて): [https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html](https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html) (このオプションは通常、GCCのコマンドラインオプションのセクションに記載されています)
* GNU Binutilsのリンカ(`ld`)に関するドキュメント: [https://sourceware.org/binutils/docs/ld/](https://sourceware.org/binutils/docs/ld/)
## 参考にした情報源リンク
* [https://golang.org/cl/6470044](https://golang.org/cl/6470044) (Goのコードレビューシステムにおけるこのコミットの変更リスト)
* NetBSDのリンカの挙動に関する一般的な情報(特定のURLは特定できませんでしたが、`ld -r`と`SEARCH_DIR`の組み合わせで検索すると、関連する議論やドキュメントが見つかります。)
* `libgcc.a`の役割に関する一般的な情報(GCCの内部動作やリンキングに関する資料を参照しました。)
* `gcc -print-libgcc-file-name`オプションの利用方法に関する一般的な情報(GCCのmanページやオンラインドキュメントを参照しました。)