[インデックス 15937] ファイルの概要
このコミットは、Go言語のリンカ (cmd/ld
) におけるCgoシンボルのエクスポート処理を改善するものです。具体的には、シンボルが静的 (cgo_export_static
) および動的 (cgo_export_dynamic
) の両方でエクスポートされることを許可するように変更し、SWIG (Simplified Wrapper and Interface Generator) を使用したコールバック機能が正しく動作するように修正しています。
コミット
cmd/ld: permit sym to be both cgo_export_static and cgo_export_dynamic
Fixes SWIG callbacks. Previously crosscall2 was only
cgo_export_static, despite the use of two #pragma declarations
in runtime/cgo/callbacks.c.
R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/7817048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5a529b61aa2174b4c44809a02c9735e0c725f2f3
元コミット内容
cmd/ld: permit sym to be both cgo_export_static and cgo_export_dynamic
Fixes SWIG callbacks. Previously crosscall2 was only
cgo_export_static, despite the use of two #pragma declarations
in runtime/cgo/callbacks.c.
R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/7817048
変更の背景
この変更の背景には、Go言語とC/C++言語を連携させるためのCgoメカニズム、特にSWIG (Simplified Wrapper and Interface Generator) を介したコールバック機能に関する問題がありました。
GoプログラムがC/C++ライブラリを呼び出す際、またはC/C++ライブラリがGoの関数をコールバックとして呼び出す際には、Cgoがその橋渡しをします。Cgoでは、Goの関数をC/C++から呼び出せるようにするために、特定のシンボルをエクスポートする必要があります。このエクスポートには、静的リンクと動的リンクの2つの形式があります。
問題は、Goのリンカ (cmd/ld
) が、あるシンボルに対して静的エクスポート (cgo_export_static
) と動的エクスポート (cgo_export_dynamic
) の両方を同時に設定することを許可していなかった点にありました。具体的には、Cgoの内部でコールバック処理に使用される crosscall2
という重要なシンボルが、runtime/cgo/callbacks.c
内で静的および動的の両方のエクスポートを指示する #pragma
ディレクティブを持っていたにもかかわらず、リンカはどちらか一方(この場合は静的エクスポート)しか認識していませんでした。
このリンカの制限により、SWIGのようなツールが生成するコードが、Goの関数をC/C++からコールバックとして呼び出す際に必要な動的エクスポート情報を持てず、結果としてSWIGを利用したアプリケーションでコールバックが正しく機能しないというバグが発生していました。このコミットは、このリンカの制約を取り除き、シンボルが静的・動的の両方でエクスポートされることを可能にすることで、SWIGコールバックの問題を解決することを目的としています。
前提知識の解説
Go言語のCgo
Cgoは、GoプログラムがC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Goのソースファイル内にCのコードを直接記述したり、既存のCライブラリをリンクしたりすることができます。Cgoを使用することで、Goの強力な並行処理能力と、既存のC/C++ライブラリの豊富なエコシステムを組み合わせることが可能になります。
Goリンカ (cmd/ld
)
cmd/ld
はGo言語のビルドツールチェーンの一部であるリンカです。コンパイラによって生成されたオブジェクトファイル(Goのコードから生成されたものと、Cgoを介してC/C++のコードから生成されたもの)を結合し、実行可能なバイナリやライブラリを生成する役割を担います。リンカは、シンボル解決(関数や変数の定義と参照を紐付けること)や、異なるモジュール間の依存関係の解決を行います。
#pragma
ディレクティブ (Cgo関連)
C言語の #pragma
ディレクティブは、コンパイラやリンカに対して特別な指示を与えるために使用されます。Cgoの文脈では、Goの関数をC/C++から呼び出せるようにエクスポートするために、特定の #pragma
ディレクティブが使用されます。
#pragma cgo_export_static(symbol)
: このディレクティブは、指定されたsymbol
を静的ライブラリ(例:.a
ファイル)にエクスポートするようリンカに指示します。これにより、Goプログラムが静的にリンクされる際に、C/C++コードからそのシンボルを参照できるようになります。#pragma cgo_export_dynamic(symbol)
: このディレクティブは、指定されたsymbol
を動的ライブラリ(例:.so
や.dll
ファイル)にエクスポートするようリンカに指示します。これにより、Goプログラムが動的にリンクされる際や、Goプログラムが共有ライブラリとしてビルドされる際に、C/C++コードからそのシンボルを参照できるようになります。
静的リンクと動的リンク
- 静的リンク: プログラムのビルド時に、必要なライブラリのコードが実行可能ファイルに直接組み込まれる方式です。これにより、実行可能ファイルは自己完結型となり、他のシステムに配布する際にライブラリの有無を気にする必要がなくなります。しかし、ファイルサイズが大きくなる傾向があり、ライブラリの更新があった場合にはプログラム全体を再ビルドする必要があります。
- 動的リンク: プログラムの実行時に、必要なライブラリがメモリにロードされる方式です。実行可能ファイルはライブラリへの参照のみを持ち、ファイルサイズは小さくなります。複数のプログラムが同じライブラリを共有できるため、メモリ効率が良い場合があります。ライブラリの更新があっても、プログラムを再ビルドせずに新しいライブラリを利用できる利点がありますが、実行環境に適切なライブラリが存在しないとプログラムが実行できないという依存性の問題があります。
Cgoの文脈では、Goの関数をC/C++から呼び出す際に、Goのランタイムが静的または動的にエクスポートされる必要があります。
SWIG (Simplified Wrapper and Interface Generator)
SWIGは、C/C++で書かれたライブラリを、Python, Java, Ruby, Go, C# など、さまざまなスクリプト言語や高水準言語から利用できるようにするためのインターフェースコードを自動生成するツールです。SWIGは、C/C++のヘッダファイルを解析し、ターゲット言語の規約に合わせたラッパーコードを生成します。これにより、開発者は手動でバインディングコードを書く手間を省き、C/C++ライブラリの機能を他の言語から簡単に利用できるようになります。Go言語においても、既存のC/C++ライブラリをGoから利用したり、Goの関数をC/C++からコールバックとして呼び出したりする際にSWIGが活用されることがあります。
コールバック
コールバックとは、ある関数やルーチンが、別の関数やルーチンを引数として受け取り、特定のイベントが発生したときや処理が完了したときに、その引数として渡された関数を呼び出すプログラミングパターンです。Cgoの文脈では、C/C++コードがGoの関数を呼び出す場合、Goの関数がC/C++コードにとっての「コールバック」となります。
crosscall2
crosscall2
は、GoのCgoランタイム内部で使用される、GoとC/C++間の呼び出しを仲介する低レベルの関数です。Goの関数がC/C++から呼び出される際や、C/C++の関数がGoから呼び出される際に、引数の変換やスタックフレームの調整など、言語間の呼び出し規約の違いを吸収する役割を担います。このシンボルは、Cgoのコールバック機能の根幹をなすものです。
技術的詳細
このコミットは、Goリンカのソースコード src/cmd/ld/go.c
内の loadcgo
関数に焦点を当てています。loadcgo
関数は、Cgoによってエクスポートされるシンボルに関する情報を処理する役割を担っています。
リンカは、Cgoの #pragma
ディレクティブ(例: #pragma cgo_export_static(symbol)
や #pragma cgo_export_dynamic(symbol)
) を解析し、Goのシンボル構造体 s
の cgoexport
フィールドにエクスポートタイプを示すフラグを設定します。cgoexport
はビットマスクとして機能し、CgoExportStatic
(静的エクスポート) と CgoExportDynamic
(動的エクスポート) というビットフラグを保持できます。
変更前のコードでは、loadcgo
関数内に以下のような条件分岐がありました。
if(s->cgoexport == 0) {
if(strcmp(f[0], "cgo_export_static") == 0)
s->cgoexport |= CgoExportStatic;
else
s->cgoexport |= CgoExportDynamic;
// ... その他の処理 ...
}
この if(s->cgoexport == 0)
という条件は、「もしシンボル s
の cgoexport
フラグがまだ何も設定されていない場合のみ、エクスポートタイプを設定する」という意味になります。
このロジックの問題点は、もし runtime/cgo/callbacks.c
のように、同じシンボル(例: crosscall2
)に対して静的エクスポートと動的エクスポートの両方を指示する #pragma
ディレクティブが存在した場合に顕在化します。
- 最初の
#pragma
ディレクティブ(例:cgo_export_static
)が処理されると、s->cgoexport
はCgoExportStatic
に設定されます。 - 次に、同じシンボルに対する2番目の
#pragma
ディレクティブ(例:cgo_export_dynamic
)が処理される際、s->cgoexport
は既にCgoExportStatic
が設定されているため、s->cgoexport == 0
の条件が偽となり、このブロック内のコードは実行されません。 - 結果として、リンカは
crosscall2
シンボルがCgoExportDynamic
でエクスポートされるべきであるという情報を無視してしまい、CgoExportStatic
のみでエクスポートされることになります。
このリンカの挙動が、SWIGを介したGoのコールバックが正しく機能しない原因となっていました。SWIGが生成するC/C++コードは、Goの関数を動的に呼び出すことを期待している場合があるため、crosscall2
が動的にエクスポートされていないと、リンクエラーや実行時エラーが発生する可能性がありました。
コアとなるコードの変更箇所
変更は src/cmd/ld/go.c
ファイルの loadcgo
関数内で行われました。
--- a/src/cmd/ld/go.c
+++ b/src/cmd/ld/go.c
@@ -508,10 +508,6 @@ loadcgo(char *file, char *pkg, char *p, int n)
}
if(s->cgoexport == 0) {
- if(strcmp(f[0], "cgo_export_static") == 0)
- s->cgoexport |= CgoExportStatic;
- else
- s->cgoexport |= CgoExportDynamic;
s->extname = remote;
if(ndynexp%32 == 0)
dynexp = erealloc(dynexp, (ndynexp+32)*sizeof dynexp[0]);
@@ -521,6 +517,10 @@ loadcgo(char *file, char *pkg, char *p, int n)
nerrors++;
return;
}
+ if(strcmp(f[0], "cgo_export_static") == 0)
+ s->cgoexport |= CgoExportStatic;
+ else
+ s->cgoexport |= CgoExportDynamic;
if(local != f[1])
free(local);
continue;
具体的には、以下の4行が if(s->cgoexport == 0)
ブロックから削除されました。
if(strcmp(f[0], "cgo_export_static") == 0)
s->cgoexport |= CgoExportStatic;
else
s->cgoexport |= CgoExportDynamic;
そして、これらの行とほぼ同じロジックが、if(s->cgoexport == 0)
ブロックの外側、かつ if(s->cgoexport == 0)
ブロックの直後に移動されました。
if(strcmp(f[0], "cgo_export_static") == 0)
s->cgoexport |= CgoExportStatic;
else
s->cgoexport |= CgoExportDynamic;
コアとなるコードの解説
この変更の核心は、シンボルのエクスポートタイプを設定するロジックを if(s->cgoexport == 0)
という条件から解放した点にあります。
変更前は、s->cgoexport
が一度でも設定されると、その後の同じシンボルに対するエクスポート指示は無視されていました。これは、s->cgoexport
が 0
でない限り、エクスポートタイプを設定するコードが実行されなかったためです。
変更後、エクスポートタイプを設定する以下のコードは、常に実行されるようになりました。
if(strcmp(f[0], "cgo_export_static") == 0)
s->cgoexport |= CgoExportStatic;
else
s->cgoexport |= CgoExportDynamic;
この |=
(ビットOR代入) 演算子を使用することで、s->cgoexport
に既に設定されているフラグを保持したまま、新しいエクスポートタイプ(CgoExportStatic
または CgoExportDynamic
)のビットを追加できるようになります。
例えば、crosscall2
シンボルに対して、最初に cgo_export_static
が処理され、次に cgo_export_dynamic
が処理される場合:
cgo_export_static
が処理されると、s->cgoexport
はCgoExportStatic
になります。- 次に
cgo_export_dynamic
が処理される際、s->cgoexport
は既にCgoExportStatic
が設定されていますが、新しいロジックではif(s->cgoexport == 0)
のチェックがないため、s->cgoexport |= CgoExportDynamic;
が実行されます。 - 結果として、
s->cgoexport
はCgoExportStatic | CgoExportDynamic
となり、シンボルが静的および動的の両方でエクスポートされることをリンカが正しく認識するようになります。
この修正により、crosscall2
のようなシンボルが、runtime/cgo/callbacks.c
で意図されていた通り、静的・動的両方のエクスポート属性を持つことができるようになり、SWIGを介したGoのコールバック機能が正常に動作するようになりました。これは、GoとC/C++間のより柔軟な相互運用性を実現するための重要な改善です。
関連リンク
- Go言語公式ドキュメント - Cgo: https://go.dev/blog/c-go-cgo
- SWIG 公式サイト: http://www.swig.org/
- Goのリンカに関する情報 (Goのソースコードリポジトリ内): https://github.com/golang/go/tree/master/src/cmd/ld
参考にした情報源リンク
- GoのCgoに関するブログ記事やドキュメント
- SWIGのドキュメント
- Go言語のソースコード (
src/cmd/ld/go.c
,runtime/cgo/callbacks.c
) - GoのIssueトラッカーやコードレビューシステム (Gerrit) の関連する議論 (コミットメッセージの
https://golang.org/cl/7817048
など) - 一般的な静的・動的リンクに関するコンピュータサイエンスの知識
- C言語の
#pragma
ディレクティブに関する情報```markdown
[インデックス 15937] ファイルの概要
このコミットは、Go言語のリンカ (cmd/ld
) におけるCgoシンボルのエクスポート処理を改善するものです。具体的には、シンボルが静的 (cgo_export_static
) および動的 (cgo_export_dynamic
) の両方でエクスポートされることを許可するように変更し、SWIG (Simplified Wrapper and Interface Generator) を使用したコールバック機能が正しく動作するように修正しています。
コミット
cmd/ld: permit sym to be both cgo_export_static and cgo_export_dynamic
Fixes SWIG callbacks. Previously crosscall2 was only
cgo_export_static, despite the use of two #pragma declarations
in runtime/cgo/callbacks.c.
R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/7817048
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5a529b61aa2174b4c44809a02c9735e0c725f2f3
元コミット内容
cmd/ld: permit sym to be both cgo_export_static and cgo_export_dynamic
Fixes SWIG callbacks. Previously crosscall2 was only
cgo_export_static, despite the use of two #pragma declarations
in runtime/cgo/callbacks.c.
R=golang-dev, bradfitz, rsc
CC=golang-dev
https://golang.org/cl/7817048
変更の背景
この変更の背景には、Go言語とC/C++言語を連携させるためのCgoメカニズム、特にSWIG (Simplified Wrapper and Interface Generator) を介したコールバック機能に関する問題がありました。
GoプログラムがC/C++ライブラリを呼び出す際、またはC/C++ライブラリがGoの関数をコールバックとして呼び出す際には、Cgoがその橋渡しをします。Cgoでは、Goの関数をC/C++から呼び出せるようにするために、特定のシンボルをエクスポートする必要があります。このエクスポートには、静的リンクと動的リンクの2つの形式があります。
問題は、Goのリンカ (cmd/ld
) が、あるシンボルに対して静的エクスポート (cgo_export_static
) と動的エクスポート (cgo_export_dynamic
) の両方を同時に設定することを許可していなかった点にありました。具体的には、Cgoの内部でコールバック処理に使用される crosscall2
という重要なシンボルが、runtime/cgo/callbacks.c
内で静的および動的の両方のエクスポートを指示する #pragma
ディレクティブを持っていたにもかかわらず、リンカはどちらか一方(この場合は静的エクスポート)しか認識していませんでした。
このリンカの制限により、SWIGのようなツールが生成するコードが、Goの関数をC/C++からコールバックとして呼び出す際に必要な動的エクスポート情報を持てず、結果としてSWIGを利用したアプリケーションでコールバックが正しく機能しないというバグが発生していました。このコミットは、このリンカの制約を取り除き、シンボルが静的・動的の両方でエクスポートされることを可能にすることで、SWIGコールバックの問題を解決することを目的としています。
前提知識の解説
Go言語のCgo
Cgoは、GoプログラムがC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Goのソースファイル内にCのコードを直接記述したり、既存のCライブラリをリンクしたりすることができます。Cgoを使用することで、Goの強力な並行処理能力と、既存のC/C++ライブラリの豊富なエコシステムを組み合わせることが可能になります。
Goリンカ (cmd/ld
)
cmd/ld
はGo言語のビルドツールチェーンの一部であるリンカです。コンパイラによって生成されたオブジェクトファイル(Goのコードから生成されたものと、Cgoを介してC/C++のコードから生成されたもの)を結合し、実行可能なバイナリやライブラリを生成する役割を担います。リンカは、シンボル解決(関数や変数の定義と参照を紐付けること)や、異なるモジュール間の依存関係の解決を行います。
#pragma
ディレクティブ (Cgo関連)
C言語の #pragma
ディレクティブは、コンパイラやリンカに対して特別な指示を与えるために使用されます。Cgoの文脈では、Goの関数をC/C++から呼び出せるようにエクスポートするために、特定の #pragma
ディレクティブが使用されます。
#pragma cgo_export_static(symbol)
: このディレクティブは、指定されたsymbol
を静的ライブラリ(例:.a
ファイル)にエクスポートするようリンカに指示します。これにより、Goプログラムが静的にリンクされる際に、C/C++コードからそのシンボルを参照できるようになります。#pragma cgo_export_dynamic(symbol)
: このディレクティブは、指定されたsymbol
を動的ライブラリ(例:.so
や.dll
ファイル)にエクスポートするようリンカに指示します。これにより、Goプログラムが動的にリンクされる際や、Goプログラムが共有ライブラリとしてビルドされる際に、C/C++コードからそのシンボルを参照できるようになります。
静的リンクと動的リンク
- 静的リンク: プログラムのビルド時に、必要なライブラリのコードが実行可能ファイルに直接組み込まれる方式です。これにより、実行可能ファイルは自己完結型となり、他のシステムに配布する際にライブラリの有無を気にする必要がなくなります。しかし、ファイルサイズが大きくなる傾向があり、ライブラリの更新があった場合にはプログラム全体を再ビルドする必要があります。
- 動的リンク: プログラムの実行時に、必要なライブラリがメモリにロードされる方式です。実行可能ファイルはライブラリへの参照のみを持ち、ファイルサイズは小さくなります。複数のプログラムが同じライブラリを共有できるため、メモリ効率が良い場合があります。ライブラリの更新があっても、プログラムを再ビルドせずに新しいライブラリを利用できる利点がありますが、実行環境に適切なライブラリが存在しないとプログラムが実行できないという依存性の問題があります。
Cgoの文脈では、Goの関数をC/C++から呼び出す際に、Goのランタイムが静的または動的にエクスポートされる必要があります。
SWIG (Simplified Wrapper and Interface Generator)
SWIGは、C/C++で書かれたライブラリを、Python, Java, Ruby, Go, C# など、さまざまなスクリプト言語や高水準言語から利用できるようにするためのインターフェースコードを自動生成するツールです。SWIGは、C/C++のヘッダファイルを解析し、ターゲット言語の規約に合わせたラッパーコードを生成します。これにより、開発者は手動でバインディングコードを書く手間を省き、C/C++ライブラリの機能を他の言語から簡単に利用できるようになります。Go言語においても、既存のC/C++ライブラリをGoから利用したり、Goの関数をC/C++からコールバックとして呼び出したりする際にSWIGが活用されることがあります。
コールバック
コールバックとは、ある関数やルーチンが、別の関数やルーチンを引数として受け取り、特定のイベントが発生したときや処理が完了したときに、その引数として渡された関数を呼び出すプログラミングパターンです。Cgoの文脈では、C/C++コードがGoの関数を呼び出す場合、Goの関数がC/C++コードにとっての「コールバック」となります。
crosscall2
crosscall2
は、GoのCgoランタイム内部で使用される、GoとC/C++間の呼び出しを仲介する低レベルの関数です。Goの関数がC/C++から呼び出される際や、C/C++の関数がGoから呼び出される際に、引数の変換やスタックフレームの調整など、言語間の呼び出し規約の違いを吸収する役割を担います。このシンボルは、Cgoのコールバック機能の根幹をなすものです。
技術的詳細
このコミットは、Goリンカのソースコード src/cmd/ld/go.c
内の loadcgo
関数に焦点を当てています。loadcgo
関数は、Cgoによってエクスポートされるシンボルに関する情報を処理する役割を担っています。
リンカは、Cgoの #pragma
ディレクティブ(例: #pragma cgo_export_static(symbol)
や #pragma cgo_export_dynamic(symbol)
) を解析し、Goのシンボル構造体 s
の cgoexport
フィールドにエクスポートタイプを示すフラグを設定します。cgoexport
はビットマスクとして機能し、CgoExportStatic
(静的エクスポート) と CgoExportDynamic
(動的エクスポート) というビットフラグを保持できます。
変更前のコードでは、loadcgo
関数内に以下のような条件分岐がありました。
if(s->cgoexport == 0) {
if(strcmp(f[0], "cgo_export_static") == 0)
s->cgoexport |= CgoExportStatic;
else
s->cgoexport |= CgoExportDynamic;
// ... その他の処理 ...
}
この if(s->cgoexport == 0)
という条件は、「もしシンボル s
の cgoexport
フラグがまだ何も設定されていない場合のみ、エクスポートタイプを設定する」という意味になります。
このロジックの問題点は、もし runtime/cgo/callbacks.c
のように、同じシンボル(例: crosscall2
)に対して静的エクスポートと動的エクスポートの両方を指示する #pragma
ディレクティブが存在した場合に顕在化します。
- 最初の
#pragma
ディレクティブ(例:cgo_export_static
)が処理されると、s->cgoexport
はCgoExportStatic
に設定されます。 - 次に、同じシンボルに対する2番目の
#pragma
ディレクティブ(例:cgo_export_dynamic
)が処理される際、s->cgoexport
は既にCgoExportStatic
が設定されているため、s->cgoexport == 0
の条件が偽となり、このブロック内のコードは実行されません。 - 結果として、リンカは
crosscall2
シンボルがCgoExportDynamic
でエクスポートされるべきであるという情報を無視してしまい、CgoExportStatic
のみでエクスポートされることになります。
このリンカの挙動が、SWIGを介したGoのコールバックが正しく機能しない原因となっていました。SWIGが生成するC/C++コードは、Goの関数を動的に呼び出すことを期待している場合があるため、crosscall2
が動的にエクスポートされていないと、リンクエラーや実行時エラーが発生する可能性がありました。
コアとなるコードの変更箇所
変更は src/cmd/ld/go.c
ファイルの loadcgo
関数内で行われました。
--- a/src/cmd/ld/go.c
+++ b/src/cmd/ld/go.c
@@ -508,10 +508,6 @@ loadcgo(char *file, char *pkg, char *p, int n)
}
if(s->cgoexport == 0) {
- if(strcmp(f[0], "cgo_export_static") == 0)
- s->cgoexport |= CgoExportStatic;
- else
- s->cgoexport |= CgoExportDynamic;
s->extname = remote;
if(ndynexp%32 == 0)
dynexp = erealloc(dynexp, (ndynexp+32)*sizeof dynexp[0]);
@@ -521,6 +517,10 @@ loadcgo(char *file, char *pkg, char *p, int n)
nerrors++;
return;
}
+ if(strcmp(f[0], "cgo_export_static") == 0)
+ s->cgoexport |= CgoExportStatic;
+ else
+ s->cgoexport |= CgoExportDynamic;
if(local != f[1])
free(local);
continue;
具体的には、以下の4行が if(s->cgoexport == 0)
ブロックから削除されました。
if(strcmp(f[0], "cgo_export_static") == 0)
s->cgoexport |= CgoExportStatic;
else
s->cgoexport |= CgoExportDynamic;
そして、これらの行とほぼ同じロジックが、if(s->cgoexport == 0)
ブロックの外側、かつ if(s->cgoexport == 0)
ブロックの直後に移動されました。
if(strcmp(f[0], "cgo_export_static") == 0)
s->cgoexport |= CgoExportStatic;
else
s->cgoexport |= CgoExportDynamic;
コアとなるコードの解説
この変更の核心は、シンボルのエクスポートタイプを設定するロジックを if(s->cgoexport == 0)
という条件から解放した点にあります。
変更前は、s->cgoexport
が一度でも設定されると、その後の同じシンボルに対するエクスポート指示は無視されていました。これは、s->cgoexport
が 0
でない限り、エクスポートタイプを設定するコードが実行されなかったためです。
変更後、エクスポートタイプを設定する以下のコードは、常に実行されるようになりました。
if(strcmp(f[0], "cgo_export_static") == 0)
s->cgoexport |= CgoExportStatic;
else
s->cgoexport |= CgoExportDynamic;
この |=
(ビットOR代入) 演算子を使用することで、s->cgoexport
に既に設定されているフラグを保持したまま、新しいエクスポートタイプ(CgoExportStatic
または CgoExportDynamic
)のビットを追加できるようになります。
例えば、crosscall2
シンボルに対して、最初に cgo_export_static
が処理され、次に cgo_export_dynamic
が処理される場合:
cgo_export_static
が処理されると、s->cgoexport
はCgoExportStatic
になります。- 次に
cgo_export_dynamic
が処理される際、s->cgoexport
は既にCgoExportStatic
が設定されていますが、新しいロジックではif(s->cgoexport == 0)
のチェックがないため、s->cgoexport |= CgoExportDynamic;
が実行されます。 - 結果として、
s->cgoexport
はCgoExportStatic | CgoExportDynamic
となり、シンボルが静的および動的の両方でエクスポートされることをリンカが正しく認識するようになります。
この修正により、crosscall2
のようなシンボルが、runtime/cgo/callbacks.c
で意図されていた通り、静的・動的両方のエクスポート属性を持つことができるようになり、SWIGを介したGoのコールバック機能が正常に動作するようになりました。これは、GoとC/C++間のより柔軟な相互運用性を実現するための重要な改善です。
関連リンク
- Go言語公式ドキュメント - Cgo: https://go.dev/blog/c-go-cgo
- SWIG 公式サイト: http://www.swig.org/
- Goのリンカに関する情報 (Goのソースコードリポジトリ内): https://github.com/golang/go/tree/master/src/cmd/ld
参考にした情報源リンク
- GoのCgoに関するブログ記事やドキュメント
- SWIGのドキュメント
- Go言語のソースコード (
src/cmd/ld/go.c
,runtime/cgo/callbacks.c
) - GoのIssueトラッカーやコードレビューシステム (Gerrit) の関連する議論 (コミットメッセージの
https://golang.org/cl/7817048
など) - 一般的な静的・動的リンクに関するコンピュータサイエンスの知識
- C言語の
#pragma
ディレクティブに関する情報