[インデックス 16745] ファイルの概要
このコミットは、Go言語のmisc/cgo/test
ディレクトリ内のテストコードにおける、C言語のstatic
関数名の衝突問題を解決します。具体的には、issue5337.go
とissue3350.go
という2つのテストファイル内で定義されていたstatic
関数thread
が、-linkmode=internal
という特殊なリンクモードを使用した場合に衝突を起こす問題に対処しています。解決策として、issue5337.go
内のstatic
関数thread
の名前をthread1
に変更することで、衝突を回避しています。
コミット
commit 41fd4f988ce2297ea6d2bf7755e753638a8cf33a
Author: Russ Cox <rsc@golang.org>
Date: Thu Jul 11 23:24:35 2013 -0400
misc/cgo/test: make test work with -linkmode=internal
The static func named thread in issue5337.go's C snippet
conflicts with the static func named thread in issue3350.go's C snippet.
I don't know why (they're both static) but I also don't care,
because -linkmode=internal only needs to be able to handle
the cgo in the standard library, and it does.
Change the test to avoid this problem.
Fixes build (after run.bash is fixed to detect the breakage).
R=minux.ma
TBR=minux.ma
CC=golang-dev
https://golang.org/cl/11201043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/41fd4f988ce2297ea6d2bf7755e753638a8cf33a
元コミット内容
このコミットは、misc/cgo/test
ディレクトリ内のテストが-linkmode=internal
オプションで動作するように修正するものです。issue5337.go
のCコードスニペットにあるstatic
関数thread
が、issue3350.go
のCコードスニペットにある同じくstatic
関数thread
と衝突していました。コミットの作者であるRuss Coxは、両者がstatic
であるにもかかわらず衝突する理由は不明だが、-linkmode=internal
は標準ライブラリ内のcgoを処理できれば十分であるため、この問題に深く関心はないと述べています。この問題はテストコードの変更によって回避され、ビルドの破損を修正しました。
変更の背景
この変更の背景には、Goのcgo
テストスイートにおける特定のリンキングモードでの問題がありました。Goのビルドシステムには、CコードとGoコードをリンクする際にいくつかのモードがあります。その一つが-linkmode=internal
です。
通常、C言語のstatic
キーワードで宣言された関数や変数は、その定義されたファイル(翻訳単位)内でのみ可視であり、外部からはアクセスできません(内部リンケージ)。そのため、異なるファイルで同じ名前のstatic
関数が定義されていても、通常は名前の衝突は発生しません。
しかし、このコミットメッセージによると、misc/cgo/test/issue5337.go
とmisc/cgo/test/issue3350.go
という2つのテストファイルに含まれるCコードスニペット内で、それぞれstatic void *thread(void *p)
という同じ名前の関数が定義されており、これが-linkmode=internal
というGoのビルドオプションを使用した場合に衝突を引き起こしていました。
この衝突は、GoのビルドシステムがCコードをどのようにコンパイルし、リンクするかに起因する可能性があります。特に-linkmode=internal
は、GoのリンカがCのオブジェクトファイルを直接処理しようとする場合に、通常は隠蔽されるべきstatic
シンボルが何らかの形で可視になってしまう、あるいはシンボルテーブルの処理方法に特殊性があるために発生したと考えられます。コミットメッセージの作者自身が「なぜ衝突するのか分からない」と述べていることから、これはGoのビルドシステムの深部に潜む、当時としては予期せぬ挙動であったことが伺えます。
この衝突により、テストスイートのビルドが失敗していたため、テストを通過させるためにこの変更が必要となりました。
前提知識の解説
このコミットを理解するためには、以下の概念についての前提知識が必要です。
- cgo: Go言語からC言語のコードを呼び出すためのGoの機能です。Goプログラム内でCの関数やデータ構造を利用できるようにします。
import "C"
という特殊なインポート宣言を使用し、GoコードとCコードの間の相互運用を可能にします。 - C言語の
static
キーワード:- 関数に対する
static
: 関数にstatic
キーワードを付けると、その関数は定義されたソースファイル(翻訳単位)内でのみ可視となります。つまり、他のソースファイルからはその関数を呼び出すことができません。これは「内部リンケージ」と呼ばれ、名前の衝突を防ぐために使用されます。 - 変数に対する
static
: グローバル変数にstatic
を付けると、その変数は定義されたソースファイル内でのみ可視となります。ローカル変数にstatic
を付けると、その変数は関数の呼び出しが終了しても値が保持され続けます(静的記憶域期間)。
- 関数に対する
- リンケージ (Linkage): プログラムの異なる部分で定義された識別子(関数名や変数名)が、同じ実体を参照するかどうかを決定する規則です。C言語には主に以下の3種類のリンケージがあります。
- 外部リンケージ (External Linkage): 複数の翻訳単位間で共有される識別子(例:
extern
なしのグローバル変数、通常の関数)。 - 内部リンケージ (Internal Linkage): 単一の翻訳単位内でのみ共有される識別子(例:
static
なグローバル変数や関数)。 - リンケージなし (No Linkage): 識別子がそのスコープ内でのみ有効で、他の場所からは参照できない(例: ローカル変数)。
- 外部リンケージ (External Linkage): 複数の翻訳単位間で共有される識別子(例:
pthread_create
: POSIXスレッドライブラリ(Pthreads)の一部で、新しいスレッドを作成するための関数です。int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
start_routine
引数には、新しく作成されるスレッドが実行を開始する関数のポインタを渡します。
pthread_kill
: 特定のスレッドにシグナルを送信するための関数です。int pthread_kill(pthread_t thread, int sig);
SIGPROF
: プロファイリングタイマーが満了したときにプロセスに送信されるシグナルです。通常、プログラムの実行プロファイルを収集するために使用されます。- Goの
-linkmode=internal
ビルドオプション: Goのビルドツール(go build
)に渡されるオプションの一つで、Goの内部リンカを使用してCgoコードをリンクするように指示します。通常、Cgoはシステムリンカ(gccなど)を呼び出してCコードをコンパイル・リンクしますが、-linkmode=internal
を使用すると、GoのリンカがCのオブジェクトファイルを直接取り込み、Goのオブジェクトファイルと結合しようとします。これは、クロスコンパイル環境や、特定のビルド要件がある場合に利用されることがあります。このモードでは、システムリンカの挙動とは異なるシンボル解決のルールが適用される可能性があり、それが今回のstatic
関数の衝突問題を引き起こしたと考えられます。
技術的詳細
この問題の核心は、C言語のstatic
キーワードの意図された挙動と、Goの-linkmode=internal
オプションがその挙動に与える影響の間にあったと考えられます。
C言語において、static
関数は内部リンケージを持つため、異なるソースファイルで同じ名前のstatic
関数が定義されていても、それらは別々の実体として扱われ、名前の衝突は発生しないはずです。各static
関数は、その定義された翻訳単位(この場合はissue5337.go
とissue3350.go
内のCコードスニペットがそれぞれ独立した翻訳単位として扱われる)のローカルスコープに限定されます。
しかし、Goのcgo
は、GoとCのコードを統合するために、Cコードをコンパイルし、その結果生成されたオブジェクトファイルをGoのビルドプロセスに組み込みます。通常、cgo
はシステムリンカ(例えばGCC)を利用してCコードをコンパイル・リンクします。この場合、システムリンカはstatic
関数の内部リンケージを適切に処理し、シンボル衝突は発生しません。
問題は-linkmode=internal
オプションが使用された場合に発生しました。このオプションは、GoのビルドツールがCコードのオブジェクトファイルをGoの内部リンカで直接処理しようとすることを意味します。Goの内部リンカは、GoのオブジェクトファイルとCのオブジェクトファイルを結合する際に、システムリンカとは異なるシンボル解決のロジックを持つ可能性があります。
考えられるシナリオとしては、以下のいずれかが原因でstatic
関数の衝突が発生した可能性があります。
- シンボルテーブルの統合: Goの内部リンカが、異なるCソースファイルから生成されたオブジェクトファイルのシンボルテーブルを統合する際に、
static
シンボルを適切にスコープ分離せず、グローバルシンボルとして扱ってしまった。 - 名前マングリングの欠如: Cコンパイラが
static
関数に対して、ファイルごとに異なる名前マングリング(シンボル名を一意にするための変換)を行わない場合、内部リンカがそれらを区別できなくなる。 - テスト環境の特殊性:
misc/cgo/test
ディレクトリ内のテストは、Goのcgo
機能の様々なエッジケースや特定の挙動を検証するために書かれています。これらのテストは、通常のアプリケーション開発では遭遇しないような、リンカやコンパイラの挙動の限界を試す場合があります。今回の衝突も、そのような特殊なテスト環境下で、Goの内部リンカの特定の挙動が露呈した結果である可能性が高いです。
コミットメッセージの作者が「なぜ衝突するのか分からない」と述べているのは、C言語のstatic
のセマンティクスからすれば予期せぬ挙動であったためです。しかし、この問題がGoの標準ライブラリのcgo機能の動作に影響を与えない(つまり、標準ライブラリのcgoコードは-linkmode=internal
で正しく動作する)ため、テストコード側で回避策を講じることで、根本原因の深掘りよりもビルドの修正を優先したという判断が伺えます。
コアとなるコードの変更箇所
変更はmisc/cgo/test/issue5337.go
ファイル内で行われました。
--- a/misc/cgo/test/issue5337.go
+++ b/misc/cgo/test/issue5337.go
@@ -10,14 +10,14 @@ package cgotest
#include <signal.h>
#include <pthread.h>
-static void *thread(void *p) {
+static void *thread1(void *p) {
(void)p;
pthread_kill(pthread_self(), SIGPROF);\
return NULL;
}
void test5337() {
pthread_t tid;
- pthread_create(&tid, 0, thread, NULL);
+ pthread_create(&tid, 0, thread1, NULL);
pthread_join(tid, 0);
}
*/
具体的には、以下の2箇所が変更されました。
static void *thread(void *p) {
がstatic void *thread1(void *p) {
に変更されました。pthread_create(&tid, 0, thread, NULL);
がpthread_create(&tid, 0, thread1, NULL);
に変更されました。
コアとなるコードの解説
この変更は非常にシンプルで、issue5337.go
内のCコードスニペットで定義されていたstatic
関数thread
の名前をthread1
にリネームしただけです。
-
static void *thread(void *p)
からstatic void *thread1(void *p)
への変更: これにより、issue5337.go
内で定義されるC関数がthread1
という名前になり、issue3350.go
内で定義されている同じくstatic
なthread
関数との名前の衝突が物理的に回避されます。たとえGoの内部リンカがstatic
シンボルを適切に分離できない場合でも、異なる名前を持つことでシンボル衝突は発生しなくなります。 -
pthread_create(&tid, 0, thread, NULL)
からpthread_create(&tid, 0, thread1, NULL)
への変更:pthread_create
関数の第3引数は、新しく作成されるスレッドが実行を開始する関数のポインタです。関数名をthread
からthread1
に変更したため、この呼び出しも新しい関数名に合わせて更新する必要があります。これにより、Goのテストコードが意図したC関数(thread1
)を正しく呼び出すことが保証されます。
この変更は、問題の根本原因(Goの内部リンカがstatic
シンボルをどのように扱うか)を解決するものではなく、あくまでテストコード側で衝突を回避するための実用的なワークアラウンドです。コミットメッセージの作者が「なぜ衝突するのか分からないが、気にしない」と述べているのは、この問題がGoの標準ライブラリのcgo機能の動作に影響を与えず、テストスイートのビルドを修正することが喫緊の課題であったためと考えられます。
関連リンク
- Go言語のcgoに関する公式ドキュメント: https://pkg.go.dev/cmd/cgo
- Pthreads (POSIX Threads) の概要: https://ja.wikipedia.org/wiki/POSIX%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89
- C言語のリンケージに関する解説: https://ja.wikipedia.org/wiki/%E3%83%AA%E3%83%B3%E3%82%B1%E3%83%BC%E3%82%B8_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0)
参考にした情報源リンク
- Goのソースコードリポジトリ (golang/go): https://github.com/golang/go
- Goのコードレビューシステム (Gerrit): https://go.dev/cl/11201043 (コミットメッセージに記載されているChange-ID)
- C言語の
static
キーワードに関する一般的な情報源 (例: C言語の教科書、オンラインリファレンス) - Goのビルドオプションに関する情報源 (例:
go help build
コマンドの出力、Goの公式ドキュメント) - Pthreads APIに関する情報源 (例: POSIX標準ドキュメント、manページ)
- Goの
issue5337
およびissue3350
に関する情報 (Goのissueトラッカーや関連するコミット履歴)