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

[インデックス 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のシンボル構造体 scgoexport フィールドにエクスポートタイプを示すフラグを設定します。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) という条件は、「もしシンボル scgoexport フラグがまだ何も設定されていない場合のみ、エクスポートタイプを設定する」という意味になります。

このロジックの問題点は、もし runtime/cgo/callbacks.c のように、同じシンボル(例: crosscall2)に対して静的エクスポートと動的エクスポートの両方を指示する #pragma ディレクティブが存在した場合に顕在化します。

  1. 最初の #pragma ディレクティブ(例: cgo_export_static)が処理されると、s->cgoexportCgoExportStatic に設定されます。
  2. 次に、同じシンボルに対する2番目の #pragma ディレクティブ(例: cgo_export_dynamic)が処理される際、s->cgoexport は既に CgoExportStatic が設定されているため、s->cgoexport == 0 の条件が偽となり、このブロック内のコードは実行されません。
  3. 結果として、リンカは 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->cgoexport0 でない限り、エクスポートタイプを設定するコードが実行されなかったためです。

変更後、エクスポートタイプを設定する以下のコードは、常に実行されるようになりました。

			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 が処理される場合:

  1. cgo_export_static が処理されると、s->cgoexportCgoExportStatic になります。
  2. 次に cgo_export_dynamic が処理される際、s->cgoexport は既に CgoExportStatic が設定されていますが、新しいロジックでは if(s->cgoexport == 0) のチェックがないため、s->cgoexport |= CgoExportDynamic; が実行されます。
  3. 結果として、s->cgoexportCgoExportStatic | CgoExportDynamic となり、シンボルが静的および動的の両方でエクスポートされることをリンカが正しく認識するようになります。

この修正により、crosscall2 のようなシンボルが、runtime/cgo/callbacks.c で意図されていた通り、静的・動的両方のエクスポート属性を持つことができるようになり、SWIGを介したGoのコールバック機能が正常に動作するようになりました。これは、GoとC/C++間のより柔軟な相互運用性を実現するための重要な改善です。

関連リンク

参考にした情報源リンク

  • 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のシンボル構造体 scgoexport フィールドにエクスポートタイプを示すフラグを設定します。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) という条件は、「もしシンボル scgoexport フラグがまだ何も設定されていない場合のみ、エクスポートタイプを設定する」という意味になります。

このロジックの問題点は、もし runtime/cgo/callbacks.c のように、同じシンボル(例: crosscall2)に対して静的エクスポートと動的エクスポートの両方を指示する #pragma ディレクティブが存在した場合に顕在化します。

  1. 最初の #pragma ディレクティブ(例: cgo_export_static)が処理されると、s->cgoexportCgoExportStatic に設定されます。
  2. 次に、同じシンボルに対する2番目の #pragma ディレクティブ(例: cgo_export_dynamic)が処理される際、s->cgoexport は既に CgoExportStatic が設定されているため、s->cgoexport == 0 の条件が偽となり、このブロック内のコードは実行されません。
  3. 結果として、リンカは 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->cgoexport0 でない限り、エクスポートタイプを設定するコードが実行されなかったためです。

変更後、エクスポートタイプを設定する以下のコードは、常に実行されるようになりました。

			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 が処理される場合:

  1. cgo_export_static が処理されると、s->cgoexportCgoExportStatic になります。
  2. 次に cgo_export_dynamic が処理される際、s->cgoexport は既に CgoExportStatic が設定されていますが、新しいロジックでは if(s->cgoexport == 0) のチェックがないため、s->cgoexport |= CgoExportDynamic; が実行されます。
  3. 結果として、s->cgoexportCgoExportStatic | CgoExportDynamic となり、シンボルが静的および動的の両方でエクスポートされることをリンカが正しく認識するようになります。

この修正により、crosscall2 のようなシンボルが、runtime/cgo/callbacks.c で意図されていた通り、静的・動的両方のエクスポート属性を持つことができるようになり、SWIGを介したGoのコールバック機能が正常に動作するようになりました。これは、GoとC/C++間のより柔軟な相互運用性を実現するための重要な改善です。

関連リンク

参考にした情報源リンク

  • GoのCgoに関するブログ記事やドキュメント
  • SWIGのドキュメント
  • Go言語のソースコード (src/cmd/ld/go.c, runtime/cgo/callbacks.c)
  • GoのIssueトラッカーやコードレビューシステム (Gerrit) の関連する議論 (コミットメッセージの https://golang.org/cl/7817048 など)
  • 一般的な静的・動的リンクに関するコンピュータサイエンスの知識
  • C言語の #pragma ディレクティブに関する情報