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

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

このコミットは、Go言語のリンカ(cmd/ld)における内部リンキングの不整合を修正するものです。特に、C言語で宣言された共通シンボルが最終的なバイナリで未定義として扱われ、アドレスがNULLになる問題を解決します。

コミット

commit 0e76a943c9efe69379bd6dcf83d254fd6abc48f2
Author: Rémy Oudompheng <oudomphe@phare.normalesup.org>
Date:   Wed Apr 10 07:15:49 2013 +0200

    cmd/ld: fix inconsistency in internal linking of common symbols.
    
    Some variables declared in C could end up as undefined symbols
    in the final binary and have null address.
    
    Fixes #5114.
    Fixes #5227.
    
    R=golang-dev, iant, ajstarks, dave, r
    CC=golang-dev
    https://golang.org/cl/8602044

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

https://github.com/golang/go/commit/0e76a943c9efe69379bd6dcf83d254fd6abc48f2

元コミット内容

cmd/ld: fix inconsistency in internal linking of common symbols.

Some variables declared in C could end up as undefined symbols
in the final binary and have null address.

Fixes #5114.
Fixes #5227.

R=golang-dev, iant, ajstarks, dave, r
CC=golang-dev
https://golang.org/cl/8602044

変更の背景

このコミットは、Go言語のリンカ(cmd/ld)がCgo(GoとC言語を連携させるためのメカニズム)を使用する際に発生していた特定の問題を解決するために導入されました。具体的には、C言語のコードで宣言された「共通シンボル(common symbols)」が、Goの内部リンカによって正しく処理されず、最終的な実行可能バイナリ内で未定義のシンボルとして扱われたり、誤ってNULLアドレスが割り当てられたりするバグが存在していました。

この問題は、GoプログラムがCgoを介してCライブラリやCコードと連携する際に、C側で定義されたグローバル変数などがGo側から参照できない、あるいは不正なアドレスを参照してしまうという形で現れました。コミットメッセージに記載されている #5114#5227 は、この問題に関連するGoプロジェクトのイシュートラッカー上のバグ報告を指していると考えられます。ただし、現在の一般的なWeb検索ではこれらのイシュー番号がGoの公式イシュートラッカーではなく、他のプロジェクトやIRSのフォームなどとしてヒットするため、当時のGoプロジェクトの内部的なイシュー番号である可能性が高いです。この修正は、GoとCの相互運用性における重要な安定性向上を目的としています。

前提知識の解説

リンカ (Linker)

リンカは、コンパイラによって生成された複数のオブジェクトファイル(機械語コードとデータを含むファイル)やライブラリを結合し、単一の実行可能ファイルやライブラリを生成するプログラムです。リンカの主な役割は以下の通りです。

  1. シンボル解決 (Symbol Resolution): オブジェクトファイル間で参照される関数や変数のアドレスを解決します。例えば、あるオブジェクトファイルで定義された関数が別のオブジェクトファイルで呼び出されている場合、リンカはその呼び出しが正しいアドレスを指すように修正します。
  2. 再配置 (Relocation): コードやデータ内のアドレス参照を、最終的なメモリ配置に合わせて調整します。
  3. ライブラリの結合 (Library Linking): 静的ライブラリ(.a.lib)や動的ライブラリ(.so.dll)をプログラムに結合します。

Go言語には独自のリンカ cmd/ld があり、Goプログラムのビルドプロセスにおいて重要な役割を担っています。

共通シンボル (Common Symbols)

C言語において、初期化されていないグローバル変数や静的変数は「共通シンボル」として扱われることがあります。これは、複数のソースファイルで同じ名前の変数が宣言された場合に、リンカがそれらを単一の変数として扱うためのメカニズムです。例えば、int x; のように初期化せずに宣言されたグローバル変数は、コンパイル時にはメモリが割り当てられず、リンカが最終的にメモリを割り当て、すべての参照をその単一のメモリ位置に解決します。

Cgo

Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Cgoを使用すると、既存のCライブラリをGoプロジェクトに統合したり、パフォーマンスが重要な部分をCで記述したりすることができます。Cgoは、Goのビルドプロセス中にCコードをコンパイルし、Goのコードとリンクする役割を担います。このプロセスには、Goのリンカ cmd/ld が深く関与します。

内部リンキング (Internal Linking) と 外部リンキング (External Linking)

Goのリンカには、主に2つのリンキングモードがあります。

  1. 内部リンキング (Internal Linking): Goのツールチェーンが提供するリンカ(cmd/ld)のみを使用して、すべてのGoコードとCgoでコンパイルされたCコードをリンクするモードです。このモードでは、外部のCコンパイラやリンカ(例: GCC)は使用されません。GoのリンカがCのオブジェクトファイルも直接処理します。
  2. 外部リンキング (External Linking): Goのリンカが生成したオブジェクトファイルを、システムのCコンパイラ(通常はGCC)のリンカと組み合わせてリンクするモードです。これは、Goのリンカが直接サポートしていない特定のCライブラリやシステム機能に依存する場合に必要となることがあります。

このコミットは、特に「内部リンキング」モードにおける共通シンボルの扱いの不整合を修正しています。

技術的詳細

このコミットの技術的な核心は、Goのリンカ cmd/ld が内部リンキングモードでC言語の共通シンボルを処理する方法の改善にあります。

以前のバージョンでは、cmd/ld が内部リンキングを行う際に、Cgoによって生成されたCのオブジェクトファイル内の共通シンボルを正しく「定義済み」として認識せず、結果として「未定義シンボル」として扱ってしまう問題がありました。これにより、これらのシンボルへの参照が解決されず、最終的なバイナリではNULLアドレスを指すか、リンクエラーを引き起こす可能性がありました。

この修正は、src/cmd/ld/lib.c ファイル内の loadlib 関数にロジックを追加することで実現されています。loadlib 関数は、リンカがライブラリをロードし、シンボルを処理する主要な部分です。

修正のポイントは以下の通りです。

  1. linkmode == LinkInternal のチェック: loadlib 関数内で、リンキングモードが LinkInternal(内部リンキング)である場合にのみ、特定の処理を行うように変更されています。
  2. cgo_import_static 宣言のドロップ: 内部リンキングモードの場合、リンカは cgo_import_static 宣言を「ドロップ」する(無視する)ように変更されました。cgo_import_static は、Cgoが静的にインポートされるCシンボルを示すために使用する内部的なメカニズムです。以前は、これらのシンボルが内部リンカによって適切に処理されず、未定義として扱われる原因となっていました。この修正により、内部リンカがこれらのシンボルを直接解決できるようになり、外部リンカに依存する必要がなくなります。

この変更により、GoのリンカはCgoを介してGoプログラムに組み込まれたCの共通シンボルを、内部リンキングモードでも正しく解決できるようになり、未定義シンボルやNULLアドレスの問題が解消されます。

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

このコミットにおけるコアとなるコードの変更は、src/cmd/ld/lib.c ファイルの loadlib 関数にあります。

--- a/src/cmd/ld/lib.c
+++ b/src/cmd/ld/lib.c
@@ -311,6 +311,9 @@ loadlib(void)
 	// Switch to internal.
 	if(linkmode == LinkAuto) {
 		linkmode = LinkInternal;
+	}
+
+	if(linkmode == LinkInternal) {
 		// Drop all the cgo_import_static declarations.
 		// Turns out we won\'t be needing them.
 		for(s = allsym; s != S; s = s->allsym)

また、この修正を検証するために、misc/cgo/test/issue5227.go という新しいテストファイルが追加され、misc/cgo/test/cgo_test.go からそのテストが呼び出されるようになっています。

コアとなるコードの解説

変更された src/cmd/ld/lib.cloadlib 関数内のコードについて解説します。

 	// Switch to internal.
 	if(linkmode == LinkAuto) {
 		linkmode = LinkInternal;
 	}
+
+	if(linkmode == LinkInternal) {
 		// Drop all the cgo_import_static declarations.
 		// Turns out we won\'t be needing them.
 		for(s = allsym; s != S; s = s->allsym)
  1. if(linkmode == LinkAuto) ブロック:

    • これは既存のロジックで、リンキングモードが LinkAuto(自動選択)に設定されている場合、それを LinkInternal(内部リンキング)に切り替えます。これは、Goのリンカがデフォルトで内部リンキングを試みることを示しています。
  2. 新しい if(linkmode == LinkInternal) ブロック:

    • このコミットで追加された主要な変更点です。
    • このブロックは、リンキングモードが LinkInternal である場合にのみ実行されます。これは、外部リンキングを使用している場合にはこの最適化が不要であることを意味します。
    • // Drop all the cgo_import_static declarations.: このコメントが示すように、ここで行われるのは cgo_import_static 宣言の「ドロップ」(破棄または無視)です。
    • for(s = allsym; s != S; s = s->allsym): リンカが認識しているすべてのシンボル(allsym リスト)をイテレートします。
    • このループ内で、cgo_import_static に関連するシンボルが特定され、それらが未定義として扱われないように、リンカの内部状態が調整されます。具体的には、これらのシンボルが外部リンカに解決を委ねる必要がないことをリンカに伝えることで、Goの内部リンカが直接それらを処理できるようになります。これにより、Cの共通シンボルがGoの内部リンキングプロセスで正しく解決されるようになります。

この修正により、GoのリンカはCgoによって導入されたCの共通シンボルを、Goの内部リンキングの枠組み内で適切に管理し、最終的なバイナリの整合性を保つことができるようになりました。

関連リンク

  • Go言語の公式ドキュメント (Cgo): https://go.dev/cmd/cgo/
  • Go言語のリンカに関する情報 (Goのソースコードやドキュメント内で cmd/ld を検索)

参考にした情報源リンク

  • コミットハッシュ: 0e76a943c9efe69379bd6dcf83d254fd6abc48f2
  • Goのソースコード: src/cmd/ld/lib.c
  • Goのテストコード: misc/cgo/test/issue5227.go, misc/cgo/test/cgo_test.go
  • Goのコードレビューシステム (Gerrit): https://golang.org/cl/8602044 (これは当時のGoのコードレビューシステムへのリンクであり、現在はGitHubに移行しています)
  • 一般的なリンカの概念に関する情報 (例: GNU ld ドキュメント、OSのリンカに関する書籍など)
  • C言語の共通シンボルに関する情報 (例: C言語の規格書、コンパイラのドキュメントなど)
  • Web検索結果 (Go issue 5114, Go issue 5227) - ただし、これらは直接的なGoのイシュートラッカーのイシューではない可能性が高いことを考慮。