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

[インデックス 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へのパスを動的に特定して明示的にリンカに渡すことで、ビルドを成功させることを目的としています。

前提知識の解説

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

  1. cgo:

    • Go言語の機能の一つで、GoプログラムからC言語の関数を呼び出したり、C言語のコードをGoプログラムに組み込んだりすることを可能にします。
    • cgoを使用すると、GoのビルドプロセスはCコンパイラ(通常はGCC)を呼び出してCソースファイルをコンパイルし、その結果生成されたオブジェクトファイルをGoのパッケージとリンクします。
    • このリンキングの過程で、C言語の標準ライブラリや、GCCが提供するランタイムライブラリ(libgcc.aなど)が必要になることがあります。
  2. リンカ (ld):

    • コンパイラが生成したオブジェクトファイル(.oファイル)やライブラリファイル(.a.soファイル)を結合して、実行可能ファイルや共有ライブラリを生成するプログラムです。
    • -rオプション: リンカのオプションの一つで、複数の入力オブジェクトファイルを結合して、単一の再配置可能なオブジェクトファイル(実行可能ファイルではなく、別のリンキングステップでさらに結合できる中間ファイル)を生成します。これは、複数のCソースファイルをコンパイルして一つの大きなオブジェクトファイルにまとめる際などに使用されます。
    • リンカスクリプト: リンカの動作を制御するためのテキストファイルです。どのセクションをどこに配置するか、どのシンボルをエクスポートするか、どのライブラリを検索するか、といった詳細な指示をリンカに与えます。
    • SEARCH_DIR: リンカスクリプト内で定義される、ライブラリを検索するためのディレクトリパスを指定するディレクティブです。リンカは、-lオプションで指定されたライブラリ(例: -lgcc)を、これらのSEARCH_DIRで指定されたパスの中から探します。
  3. libgcc.a:

    • GCC(GNU Compiler Collection)のランタイムサポートライブラリです。
    • C言語のコードが特定のハードウェア命令に直接マッピングできないような複雑な操作(例: 64ビット整数に対する32ビットCPUでの除算、浮動小数点演算のソフトウェアエミュレーション、アトミック操作など)を行う際に、GCCが生成するコードが依存するヘルパー関数が含まれています。
    • 通常、GCCがインストールされているディレクトリの特定のサブディレクトリ(例: /usr/lib/gcc/x86_64-linux-gnu/X.Y.Z/libgcc.a)に配置されます。
  4. 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へのパスを明示的にリンカに渡すというアプローチを取っています。そのために、以下のステップが導入されました。

  1. libgcc.aのパスの動的取得: gcc -print-libgcc-file-nameコマンドを実行することで、現在のシステムにおけるlibgcc.aの正確なファイルパスを取得します。このコマンドは、GCCが自身のランタイムライブラリがどこにあるかを知っていることを利用しています。
  2. 明示的なリンキング: 取得した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ページやオンラインドキュメントを参照しました。)