[インデックス 10274] ファイルの概要
このコミットは、Go言語のランタイムにおけるCgo(C言語との相互運用機能)関連のヘッダーファイル src/pkg/runtime/cgo/libcgo.h
のデータ宣言に関する修正です。具体的には、libcgo_thread_start
という関数ポインタの宣言に extern
キーワードを追加することで、OS X(macOS)環境におけるツールチェーンが複数のオブジェクトファイルによる再宣言について警告を発する問題を解決しています。
コミット
- コミットハッシュ:
1c42db883522997230819f512a92622434838842
- 作者: Russ Cox rsc@golang.org
- コミット日時: 2011年11月7日 月曜日 13:15:06 -0500
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1c42db883522997230819f512a926262434838842
元コミット内容
runtime/cgo: fix data declaration to be extern
Otherwise some OS X toolchains complain about the redeclaration
of libcgo_thread_start by multiple object files. The real definition
is in util.c.
Fixes #2167.
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/5364045
変更の背景
このコミットの主な目的は、Go言語のCgoランタイムにおいて、libcgo_thread_start
というシンボルが複数のオブジェクトファイルで再宣言されることによって、特にOS Xのツールチェーンが警告を発する問題を解決することです。
Go言語は、C言語のコードを呼び出すためのCgoというメカニズムを提供しています。Cgoを使用すると、GoのプログラムからCの関数を呼び出したり、Cのデータ構造を扱ったりすることができます。この相互運用性のために、GoランタイムはC言語のコードと連携する部分を持っています。
libcgo_thread_start
は、Cgoが新しいOSスレッドを開始する際に使用する関数ポインタです。コミットメッセージによると、このシンボルの「本当の定義 (real definition)」は util.c
という別のファイルに存在します。しかし、libcgo.h
というヘッダーファイルでこのシンボルが宣言されているにもかかわらず、一部のOS Xツールチェーンが、この宣言を「再宣言」と誤解し、複数のオブジェクトファイルが同じシンボルを定義しようとしていると解釈して警告を出していました。
これは、C言語における「宣言 (declaration)」と「定義 (definition)」の区別が曖昧になることで発生する問題です。libcgo.h
はヘッダーファイルであり、通常は宣言のみを含み、複数のソースファイルにインクルードされることを想定しています。しかし、libcgo_thread_start
の宣言が、ツールチェーンによっては定義とみなされてしまい、結果としてリンク時に問題を引き起こす可能性がありました。
この問題を解決するために、libcgo_thread_start
の宣言に extern
キーワードを追加することで、このシンボルが外部で定義されていることを明示し、ツールチェーンの誤解を解消することが目的でした。これにより、OS X環境でのビルド時の警告が解消され、よりクリーンなビルドプロセスが実現されます。
この変更は、GoのIssue #2167 に対応するものです。
前提知識の解説
このコミットを理解するためには、以下のC言語およびGo言語のCgoに関する基本的な知識が必要です。
-
C言語における宣言 (Declaration) と定義 (Definition):
- 宣言 (Declaration): 変数や関数の名前、型、引数リストなどをコンパイラに伝えることです。これにより、コンパイラはその変数や関数がどこかで存在することを知り、それらを使用するコードをコンパイルできます。宣言は複数回行うことができます。
例:
int x;
(変数の宣言),void func(int a);
(関数の宣言) - 定義 (Definition): 変数にメモリを割り当てたり、関数の具体的な実装(コードブロック)を提供したりすることです。定義はプログラム全体で一度だけ行う必要があります。
例:
int x = 10;
(変数の定義),void func(int a) { /* ... */ }
(関数の定義) extern
キーワード:extern
キーワードは、変数が現在のファイルではなく、別のファイルで定義されていることをコンパイラに伝えます。これにより、コンパイラはその変数の定義をリンク時に解決することを期待します。関数宣言では通常省略されますが、明示的にextern
を付けることも可能です。これは、その関数が外部リンケージを持つことを意味します。
- 宣言 (Declaration): 変数や関数の名前、型、引数リストなどをコンパイラに伝えることです。これにより、コンパイラはその変数や関数がどこかで存在することを知り、それらを使用するコードをコンパイルできます。宣言は複数回行うことができます。
例:
-
Cgo:
- Cgoは、GoプログラムからC言語のコードを呼び出すためのGoの機能です。Goのソースファイル内にCのコードを記述したり、既存のCライブラリをリンクしたりすることができます。
- Cgoを使用すると、GoとCの間でデータを受け渡したり、Cの関数をGoから呼び出したりすることが可能になります。これは、OSのシステムコールや既存のCライブラリを利用する際に特に重要です。
- Cgoは、Goのビルドプロセスの一部として、GoとCのコードをコンパイルし、リンクします。このプロセスでは、GoのコードとCのコードが相互に参照できるように、シンボル解決が行われます。
-
リンケージ (Linkage):
- リンケージは、プログラムの異なる部分(異なるソースファイルやライブラリ)間でシンボル(変数や関数名)がどのように参照されるかを決定するC言語の概念です。
- 外部リンケージ (External Linkage): シンボルが複数のソースファイルから参照可能であることを意味します。グローバル変数や非
static
な関数はデフォルトで外部リンケージを持ちます。 - 内部リンケージ (Internal Linkage): シンボルが定義されているソースファイル内でのみ参照可能であることを意味します。
static
キーワードで宣言されたグローバル変数や関数は内部リンケージを持ちます。 - リンカー (Linker): コンパイラによって生成された複数のオブジェクトファイルとライブラリを結合し、実行可能ファイルを生成するツールです。リンカーは、未解決のシンボル参照を解決し、各シンボルがプログラム全体で一意に定義されていることを確認します。
-
OS X ツールチェーンの特性:
- 特定のOSやコンパイラツールチェーン(この場合はOS XのClang/GCCなど)は、C言語の標準に厳密に従うか、あるいは特定の解釈を持つことがあります。このコミットで言及されている問題は、OS Xのツールチェーンが、
libcgo_thread_start
のような関数ポインタの宣言を、デフォルトで外部リンケージを持つ定義と解釈してしまい、結果として複数のオブジェクトファイルが同じシンボルを「定義」しようとしていると判断したために発生しました。
- 特定のOSやコンパイラツールチェーン(この場合はOS XのClang/GCCなど)は、C言語の標準に厳密に従うか、あるいは特定の解釈を持つことがあります。このコミットで言及されている問題は、OS Xのツールチェーンが、
技術的詳細
この問題の核心は、C言語のヘッダーファイル src/pkg/runtime/cgo/libcgo.h
内での libcgo_thread_start
の宣言が、OS Xの特定のツールチェーンによって「定義」と誤解釈されたことにあります。
元のコードでは、libcgo_thread_start
は以下のように宣言されていました。
void (*libcgo_thread_start)(ThreadStart *ts);
これは、libcgo_thread_start
が ThreadStart *ts
を引数にとり、void
を返す関数へのポインタであることを宣言しています。C言語では、グローバルスコープでのこのような宣言は、通常、外部リンケージを持つ変数の「定義」とみなされることがあります。特に、初期化子がない場合でも、一部のコンパイラやリンカーはこれを「共通ブロック (common block)」として扱い、複数の定義を許容する場合がありますが、OS Xのツールチェーンはより厳格であったと考えられます。
コミットメッセージにあるように、「本当の定義は util.c
にある」ということは、libcgo_thread_start
という関数ポインタの実体(メモリ上の場所)は util.c
で割り当てられ、初期化されていることを意味します。しかし、libcgo.h
が複数のCソースファイルにインクルードされると、各ソースファイルが libcgo_thread_start
の「宣言」を含みます。OS Xのツールチェーンがこの宣言を「定義」と解釈した場合、複数のオブジェクトファイルが同じグローバルシンボル libcgo_thread_start
を定義しようとしていると判断し、リンケージエラーや警告を発生させました。
この問題を解決するために、extern
キーワードが追加されました。
extern void (*libcgo_thread_start)(ThreadStart *ts);
extern
キーワードは、コンパイラに対して「このシンボルは現在の翻訳単位(ソースファイル)では定義されておらず、別の場所(通常は別のソースファイル)で定義されているので、リンカーがその定義を見つけることを期待する」と明示的に伝えます。これにより、libcgo.h
をインクルードするすべてのソースファイルは、libcgo_thread_start
が外部で定義されたシンボルであることを認識し、自身でそのシンボルを定義しようとはしなくなります。結果として、リンカーは util.c
内の唯一の定義を見つけ、重複定義の警告やエラーを回避できるようになります。
この修正は、C言語のリンケージ規則と、異なるプラットフォームのツールチェーンがこれらの規則をどのように解釈するかの違いに対処する、典型的なクロスプラットフォーム互換性の問題解決策です。
コアとなるコードの変更箇所
変更は src/pkg/runtime/cgo/libcgo.h
ファイルの1箇所のみです。
--- a/src/pkg/runtime/cgo/libcgo.h
+++ b/src/pkg/runtime/cgo/libcgo.h
@@ -42,7 +42,7 @@ struct ThreadStart
* Makes a local copy of the ThreadStart and
* calls libcgo_sys_thread_start(ts).\n
*/
-void (*libcgo_thread_start)(ThreadStart *ts);\n
+extern void (*libcgo_thread_start)(ThreadStart *ts);\n
\n /*
* Creates the new operating system thread (OS, arch dependent).\n
コアとなるコードの解説
変更された行は以下の通りです。
- 変更前:
void (*libcgo_thread_start)(ThreadStart *ts);
- 変更後:
extern void (*libcgo_thread_start)(ThreadStart *ts);
この変更は、libcgo_thread_start
という関数ポインタの宣言に extern
キーワードを追加しただけです。
extern
キーワードの追加により、この宣言はもはや「定義」とはみなされず、単に「libcgo_thread_start
という名前の関数ポインタがどこか別の場所で定義されている」ということをコンパイラに伝えるだけのものになります。これにより、libcgo.h
が複数のCソースファイルにインクルードされても、各ソースファイルが libcgo_thread_start
の定義を重複して提供しようとすることがなくなり、OS Xツールチェーンが発していた再宣言に関する警告が解消されます。
この修正は、GoランタイムのCgo部分が、異なるオペレーティングシステムやコンパイラツールチェーンの特性に適切に対応し、クロスプラットフォームでの安定したビルドを保証するために重要です。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/1c42db883522997230819f512a92622434838842
- Go Issue #2167: https://go.dev/issue/2167
- Go CL 5364045: https://golang.org/cl/5364045
参考にした情報源リンク
- C言語の
extern
キーワードに関する一般的な情報源 (例: C言語の教科書、オンラインリファレンス) - Cgoに関するGo言語の公式ドキュメント
- リンケージに関するC言語の仕様書または解説
- OS X/macOSのコンパイラツールチェーン(Clang/GCC)の動作に関する情報