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

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

このコミットは、GoランタイムのCgo(C言語との相互運用機能)に関連する変更です。具体的には、_cgo_allocate_cgo_panic という内部シンボルが、内部リンクモードと外部リンクモードの両方でエクスポートされるように修正されています。これにより、Cgoが生成するコードが、Goプログラムがどのようにリンクされるかに関わらず、これらのランタイム関数に正しくアクセスできるようになります。

コミット

  • Author: Ian Lance Taylor iant@golang.org
  • Date: Wed May 1 14:30:51 2013 -0700
  • Commit Message:
    runtime/cgo: export symbols in both internal and external link mode
    
    R=rsc, r
    CC=golang-dev
    https://golang.org/cl/9060045
    

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

https://github.com/golang/go/commit/9de38d6788efec8d65c51d47e5a5b503be8df14f

元コミット内容

runtime/cgo: export symbols in both internal and external link mode

変更の背景

GoプログラムがC言語のコードと連携する際に使用されるCgoは、Goランタイムが提供する特定の関数(例: メモリ割り当てやパニック処理)をCコードから呼び出す必要があります。これらの関数はシンボルとしてエクスポートされ、Cコードから参照可能である必要があります。

Goのビルドシステムには、大きく分けて「内部リンクモード(internal linking)」と「外部リンクモード(external linking)」の2つのリンクモードが存在します。

  • 内部リンクモード: Goコンパイラが生成したオブジェクトファイルと、Goランタイムのライブラリを直接リンクするモードです。この場合、Goコンパイラがリンクプロセスを完全に制御します。
  • 外部リンクモード: Goコンパイラが生成したオブジェクトファイルを、システムのCコンパイラ(通常はGCCやClang)を使用してリンクするモードです。これは、Cgoを使用する場合や、Goプログラムを既存のC/C++プロジェクトに組み込む場合によく使用されます。

以前のCgoのシンボルエクスポートは、おそらく特定のリンクモード(例えば内部リンクモード)にのみ対応していたか、あるいは両方のモードで一貫してシンボルがエクスポートされることを保証していなかった可能性があります。この不整合が問題を引き起こし、Cgoが生成するCコードがGoランタイムの関数を正しく見つけられない状況が発生したと考えられます。

このコミットの目的は、_cgo_allocate_cgo_panic というCgoランタイムの重要な関数が、Goプログラムがどちらのリンクモードでビルドされても、Cコードから確実に利用できるようにすることです。

前提知識の解説

Cgo

Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。Goのソースファイル内に特別なimport "C"ブロックを記述することで、Cのヘッダーファイルをインクルードしたり、Cの関数をGoから呼び出したり、Goの関数をCから呼び出せるようにエクスポートしたりできます。

Cgoは、GoとCの間のデータ変換やスタック切り替えなど、複雑な処理を内部的に行います。この処理の一部として、Cgoが生成するCコードは、Goランタイムが提供する特定のヘルパー関数(例: メモリ割り当て、パニック処理)に依存します。

シンボルエクスポート

プログラムにおいて「シンボル」とは、関数名、変数名、型名など、コンパイラやリンカが参照する識別子のことです。シンボルエクスポートとは、あるモジュール(この場合はGoのランタイムライブラリ)が、その内部で定義されたシンボルを外部のモジュール(この場合はCgoが生成するCコード)から参照できるように公開するプロセスを指します。

_cgo_allocate_cgo_panic

これらはGoランタイムのCgo部分で定義されている内部関数です。

  • _cgo_allocate: CgoがCコードのためにメモリを割り当てる際に使用される関数です。Goのガベージコレクタと連携してメモリを管理するために、Goランタイムのメモリ割り当てメカニズムを利用します。
  • _cgo_panic: Cgo経由でCコードからGoのパニック機構をトリガーする際に使用される関数です。Cコードでエラーが発生し、それをGoのパニックとして処理したい場合に利用されます。

#pragma dynexport

これは、GoのCgoツールチェーンが使用する特別なディレクティブ(プラグマ)です。#pragma dynexport <GoSymbol> <CSymbol> の形式で記述され、GoのシンボルをCのシンボルとしてエクスポートするために使用されていました。しかし、このコミットでは、より新しい(またはより適切な)#pragma cgo_export_static#pragma cgo_export_dynamic に置き換えられています。

#pragma cgo_export_static#pragma cgo_export_dynamic

これらは、Go 1.1以降で導入されたCgoのプラグマで、シンボルのエクスポート方法をより細かく制御するために使用されます。

  • #pragma cgo_export_static <GoSymbol>: 指定されたGoシンボルを、静的リンク(internal linking)時にCコードから参照できるようにエクスポートします。これは、Goのオブジェクトファイルが直接リンカによって処理される場合に重要です。
  • #pragma cgo_export_dynamic <GoSymbol>: 指定されたGoシンボルを、動的リンク(external linking)時にCコードから参照できるようにエクスポートします。これは、GoのオブジェクトファイルがCコンパイラ/リンカによって処理され、共有ライブラリや実行可能ファイルに組み込まれる場合に重要です。

この2つのプラグマを併用することで、GoのシンボルがGoプログラムのリンクモードに関わらず、Cコードから確実に参照できるようになります。

技術的詳細

この変更の核心は、GoのCgoランタイムが提供する重要なヘルパー関数(_cgo_allocate_cgo_panic)のシンボルエクスポート戦略を改善することにあります。

以前は #pragma dynexport が使用されていましたが、これはおそらく特定のリンクモード(例えば、Goの内部リンカが処理する静的リンク)に最適化されていたか、あるいは外部リンカが関与する動的リンクのシナリオで問題を引き起こす可能性がありました。

Goのビルドプロセスでは、Cgoを使用する場合、GoコンパイラはGoコードをコンパイルし、CgoツールはGoとCの間のブリッジとなるCコードを生成します。この生成されたCコードは、Goランタイムの特定の関数を呼び出す必要があります。これらの関数がCコードから「見える」ためには、Goランタイムがそれらをシンボルとしてエクスポートする必要があります。

#pragma cgo_export_static#pragma cgo_export_dynamic を両方使用することで、以下のシナリオがカバーされます。

  1. 静的リンク(Internal Linking): GoのビルドツールがGoのオブジェクトファイルとランタイムライブラリを直接リンクして単一の実行可能ファイルを生成する場合。cgo_export_static は、このシナリオでCgoが生成するCコードがGoランタイムの関数を正しく解決できるようにします。
  2. 動的リンク(External Linking): GoのビルドツールがCコンパイラ(例: GCC)を呼び出してGoのオブジェクトファイルをリンクし、共有ライブラリ(.so.dylib)や実行可能ファイルを生成する場合。これは、GoプログラムがC/C++の既存のビルドシステムに統合される際によく発生します。cgo_export_dynamic は、このシナリオでCgoが生成するCコードがGoランタイムの関数を正しく解決できるようにします。

この変更により、GoのCgo機能がより堅牢になり、Goプログラムがどのようなリンクモードでビルドされても、Cgoが生成するCコードがGoランタイムの依存関数を確実に呼び出せるようになります。これにより、Cgoを使用するGoアプリケーションの互換性と安定性が向上します。

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

変更は src/pkg/runtime/cgo/callbacks.c ファイルにあります。

--- a/src/pkg/runtime/cgo/callbacks.c
+++ b/src/pkg/runtime/cgo/callbacks.c
@@ -45,7 +45,8 @@ _cgo_allocate_internal(uintptr len, byte *ret)
 	FLUSH(&ret);
 }
 
-#pragma dynexport _cgo_allocate _cgo_allocate
+#pragma cgo_export_static _cgo_allocate
+#pragma cgo_export_dynamic _cgo_allocate
 void
 _cgo_allocate(void *a, int32 n)
 {
@@ -73,7 +74,8 @@ _cgo_panic_internal(byte *p)
 	runtime·panic(err);\n
 }
 
-#pragma dynexport _cgo_panic _cgo_panic
+#pragma cgo_export_static _cgo_panic
+#pragma cgo_export_dynamic _cgo_panic
 void
 _cgo_panic(void *a, int32 n)
 {

具体的には、以下の2つの変更が行われています。

  1. _cgo_allocate 関数の定義の直前で、#pragma dynexport _cgo_allocate _cgo_allocate が削除され、代わりに #pragma cgo_export_static _cgo_allocate#pragma cgo_export_dynamic _cgo_allocate が追加されました。
  2. _cgo_panic 関数の定義の直前で、同様に #pragma dynexport _cgo_panic _cgo_panic が削除され、代わりに #pragma cgo_export_static _cgo_panic#pragma cgo_export_dynamic _cgo_panic が追加されました。

コアとなるコードの解説

この変更は、GoランタイムのCgo部分が、_cgo_allocate_cgo_panic という2つの重要な内部関数をどのように外部に公開するかを定義するプラグマを更新しています。

  • #pragma dynexport からの移行: 以前使用されていた #pragma dynexport は、GoのシンボルをCのシンボルとしてエクスポートするための一般的なメカニズムでした。しかし、GoのリンカとCのリンカが異なる方法でシンボルを処理する可能性があるため、すべてのリンクモードで一貫した動作を保証するのが難しい場合がありました。
  • #pragma cgo_export_staticcgo_export_dynamic の導入: これらの新しいプラグマは、Go 1.1で導入されたもので、シンボルのエクスポートをより明示的に制御します。
    • cgo_export_static: Goの内部リンカがGoのオブジェクトファイルを処理する際に、このシンボルが静的にエクスポートされることを保証します。
    • cgo_export_dynamic: GoがCコンパイラ(外部リンカ)を呼び出してGoのオブジェクトファイルをリンクする際に、このシンボルが動的にエクスポートされることを保証します。

この変更により、_cgo_allocate_cgo_panic は、Goプログラムが静的にリンクされるか動的にリンクされるかに関わらず、Cgoが生成するCコードから常に正しく参照できるようになります。これにより、Cgoの安定性と互換性が向上し、GoとCの相互運用がより信頼性の高いものになります。

関連リンク

参考にした情報源リンク