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

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

このコミットは、Go言語のリンカ (src/cmd/ld/go.c) と、OpenBSD向けのCgoランタイム (src/pkg/runtime/cgo/gcc_openbsd_386.c, src/pkg/runtime/cgo/gcc_openbsd_amd64.c)、そしてテストスクリプト (src/run.bash) に変更を加えています。主な目的は、GoとCの相互運用機能であるCgoにおいて、あるシンボルがcgo_exportcgo_importの両方として扱われる場合に発生する問題を解決することです。

コミット

cmd/ld, runtime/cgo: allow a symbol to be both cgo_export and cgo_import.
Fixes #4878.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7420052

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

https://github.com/golang/go/commit/960d7082ee9b8fda91444167b3c253d5cf5e115d

元コミット内容

commit 960d7082ee9b8fda91444167b3c253d5cf5e115d
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Mon Mar 11 14:24:51 2013 +0800

    cmd/ld, runtime/cgo: allow a symbol to be both cgo_export and cgo_import.
    Fixes #4878.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/7420052
---
 src/cmd/ld/go.c                         | 11 ++++++++---\n src/pkg/runtime/cgo/gcc_openbsd_386.c   |  6 +++---\n src/pkg/runtime/cgo/gcc_openbsd_amd64.c |  6 +++---\n src/run.bash                            |  1 -\n 4 files changed, 14 insertions(+), 10 deletions(-)\n

変更の背景

このコミットは、Go言語のIssue 4878「cmd/ld: symbol is both imported and exported: _cgo_thread_start」を修正するために行われました。この問題は、特にOpenBSD環境において、Cgoを使用する際に特定のシンボル(例: _cgo_thread_start)がGoリンカによって「インポートとエクスポートの両方」として誤って検出され、エラーとなることが原因でした。

通常、シンボルは外部からインポートされるか(cgo_import)、外部にエクスポートされるか(cgo_export)のいずれか一方であるべきです。しかし、OpenBSDのCgoランタイムの特定の設計や、リンカのシンボル解決ロジックの不備により、この二重の定義が検出され、リンカがエラーを発生させていました。これにより、OpenBSD上でのCgoプログラムのビルドが妨げられていました。

この問題は、GoのランタイムがCのコードを呼び出し、CのコードがGoのコードをコールバックするような複雑なCgoのシナリオで顕在化しやすかったと考えられます。リンカは、シンボルが外部ライブラリからインポートされるべきか、それともGoプログラム自身が提供するべきかについて明確な判断ができず、矛盾として扱っていました。

前提知識の解説

Go言語のCgo

Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。これにより、既存のCライブラリをGoから利用したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。

Cgoを使用する際、Goのビルドプロセスは以下のようになります。

  1. Goのソースコード内のimport "C"ブロックを解析し、Cのコードを抽出します。
  2. Cのコンパイラ(通常はGCC)を使用して、抽出されたCのコードをコンパイルします。
  3. GoのコンパイラはGoのコードをコンパイルします。
  4. Goリンカ(cmd/ld)は、コンパイルされたGoのオブジェクトファイルとCのオブジェクトファイルをリンクし、最終的な実行可能ファイルを生成します。この際、GoとCの間でやり取りされるシンボル(関数や変数)の解決が行われます。

cgo_exportcgo_import

Cgoにおいて、GoとCの間でシンボルをやり取りするために、特別なディレクティブが使用されます。

  • cgo_export: Goの関数や変数をCのコードから呼び出せるように、外部にエクスポートする際に使用されます。Goのコードで//export MyGoFunctionのように記述すると、MyGoFunctionというGoの関数がCから呼び出し可能なシンボルとしてエクスポートされます。
  • cgo_import: Cの関数や変数をGoのコードから呼び出せるように、外部からインポートする際に使用されます。Goのコードでimport "C"ブロック内にCの関数宣言を記述すると、その関数は外部のCライブラリからインポートされるシンボルとして扱われます。

リンカは、これらのcgo_exportおよびcgo_importの情報を基に、GoとCのオブジェクトファイル間でシンボルを正しく解決します。

Goリンカ (cmd/ld)

cmd/ldはGo言語のリンカであり、コンパイルされたGoのオブジェクトファイルと、Cgoによって生成されたCのオブジェクトファイル、およびその他のライブラリを結合して実行可能ファイルを生成する役割を担っています。リンカは、プログラム内のすべてのシンボル参照を解決し、それぞれのシンボルがどこで定義されているかを特定します。

Thread Local Storage (TLS)

Thread Local Storage (TLS) は、各スレッドが独自のデータコピーを持つことを可能にするメカニズムです。これにより、グローバル変数のようにアクセスできるが、実際にはスレッドごとに独立した値を持つ変数を定義できます。Cgoのコンテキストでは、GoランタイムとCランタイムがスレッド固有の情報を管理するためにTLSを使用することがあります。OpenBSDのような特定のOSでは、TLSのサポートが他のOSと異なる場合があり、それがCgoランタイムの複雑さや問題の原因となることがあります。

技術的詳細

このコミットの技術的詳細は、主にGoリンカのシンボル解決ロジックと、OpenBSDにおけるCgoランタイムの特殊性に起因します。

リンカのシンボル解決の競合

Goリンカは、loadcgo関数内でCgo関連のシンボルを処理します。以前のリンカのロジックでは、あるシンボルが既にdynimplib(動的インポートライブラリ)を持つ、つまり外部からインポートされるシンボルとしてマークされているにもかかわらず、cgo_exportとしても定義されようとすると、これをエラーとして扱っていました。

if(s->dynimplib != nil) {
    fprintf(2, "%s: symbol is both imported and exported: %s\\n", argv0, local);
    nerrors++;
}

このロジックは、一般的なケースではシンボル定義の矛盾を検出するのに役立ちますが、OpenBSDのCgoランタイムの特定のシナリオでは問題を引き起こしました。OpenBSDのCgoランタイムは、GoランタイムがCのコードを呼び出し、そのCのコードがGoのランタイムの一部(例えば、スレッドの初期化に関連する関数)をコールバックするような、より複雑な相互作用を必要とすることがあります。この際、特定のシンボル(例: _cgo_thread_start)が、Go側からCにエクスポートされると同時に、C側からGoにインポートされるかのようにリンカに認識されてしまうことがありました。これは、シンボルが実際に二重に定義されているわけではなく、リンカの視点から見た「インポート」と「エクスポート」の解釈が、OpenBSDのCgoの特殊なフローと合致しなかったためです。

修正アプローチ:「エクスポートがインポートを上書きする」

このコミットの主要な修正は、リンカのloadcgo関数において、シンボルが既にインポートされているとマークされていても、それがcgo_exportとして定義されようとする場合に、既存のインポート情報をクリアし、エクスポートを優先させるというロジックを追加したことです。

// export overrides import, for openbsd/cgo.
// see issue 4878.
if(s->dynimplib != nil) {
    s->dynimplib = nil;
    s->extname = nil;
    s->dynimpvers = nil;
    s->type = 0;
}

この変更により、リンカは「このシンボルは外部からインポートされるものだが、Go自身がエクスポートするものとしても定義されている。この場合はGoのエクスポートを優先しよう」と判断するようになります。これにより、OpenBSDのCgoランタイムが内部的に必要とするシンボルが、リンカによって矛盾として扱われることなく、正しく解決されるようになりました。

OpenBSD Cgoランタイムの変更

src/pkg/runtime/cgo/gcc_openbsd_386.c および src/pkg/runtime/cgo/gcc_openbsd_amd64.c の変更は、tcb_fixup関数内のコメントの修正です。以前のコメントでは、メインスレッドのTCB (Thread Control Block) は静的割り当てであるため解放すべきではないとされていましたが、新しいコメントでは、oldtcbを解放すると二重解放の問題を引き起こす可能性があり、newtcbがメモリリークを引き起こす可能性があると指摘しています。そして、OpenBSDがPT_TLS(POSIX Thread Local Storage)を適切にサポートするようになったら、この問題を解決すべきだと述べています。

この変更は、直接的にcgo_exportcgo_importの競合を解決するものではありませんが、OpenBSDのCgoランタイムにおけるスレッドローカルストレージの管理に関する既存の課題を明確にしています。これは、Issue 4878の根本原因の一部、またはその修正によって顕在化した別の問題に関連している可能性があります。リンカの変更がシンボル解決の矛盾を解消したことで、ランタイムレベルでのメモリ管理の課題がより明確になったのかもしれません。

テストスクリプトの変更

src/run.bashの変更は、OpenBSD環境でmisc/cgo/testのテストがスキップされていた行を削除しています。これは、Issue 4878が修正されたため、OpenBSD上でもCgoのテストが正常に実行できるようになったことを示しています。これにより、OpenBSD環境でのCgoの安定性が向上したことが確認できます。

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

src/cmd/ld/go.c

--- a/src/cmd/ld/go.c
+++ b/src/cmd/ld/go.c
@@ -499,11 +499,16 @@ loadcgo(char *file, char *pkg, char *p, int n)
 			remote = local;
 			local = expandpkg(local, pkg);
 			s = lookup(local, 0);
+
+			// export overrides import, for openbsd/cgo.
+			// see issue 4878.
 			if(s->dynimplib != nil) {
-				fprintf(2, "%s: symbol is both imported and exported: %s\\n", argv0, local);
-				nerrors++;
+				s->dynimplib = nil;
+				s->extname = nil;
+				s->dynimpvers = nil;
+				s->type = 0;
 			}
-			
+
 			if(s->cgoexport == 0) {
 				if(strcmp(f[0], "cgo_export_static") == 0)
 					s->cgoexport |= CgoExportStatic;

src/pkg/runtime/cgo/gcc_openbsd_386.c および src/pkg/runtime/cgo/gcc_openbsd_amd64.c

--- a/src/pkg/runtime/cgo/gcc_openbsd_386.c
+++ b/src/pkg/runtime/cgo/gcc_openbsd_386.c
@@ -48,9 +48,9 @@ tcb_fixup(int mainthread)
 	bcopy(oldtcb, newtcb + TLS_SIZE, TCB_SIZE);
 	__set_tcb(newtcb + TLS_SIZE);
 
-	// The main thread TCB is a static allocation - do not try to free it.
-	if(!mainthread)
-		free(oldtcb);
+	// NOTE(jsing, minux): we can't free oldtcb without causing double-free
+	// problem. so newtcb will be memory leaks. Get rid of this when OpenBSD
+	// has proper support for PT_TLS.
 }
 
 static void *

gcc_openbsd_amd64.cも同様の変更)

src/run.bash

--- a/src/run.bash
+++ b/src/run.bash
@@ -74,7 +74,6 @@ go run $GOROOT/test/run.go - .
 ) || exit $?\n
 [ "$CGO_ENABLED" != 1 ] ||
-[ "$GOHOSTOS" == openbsd ] || # issue 4878
 (xcd ../misc/cgo/test
 go test
 case "$GOHOSTOS-$GOARCH" in

コアとなるコードの解説

src/cmd/ld/go.c の変更

この変更は、Goリンカのloadcgo関数内で行われています。loadcgoは、Cgoによって生成されたシンボル情報を処理する部分です。

変更前のコードでは、s->dynimplib != nil(シンボルsが動的インポートライブラリを持つ、つまり外部からインポートされるシンボルとしてマークされている)の場合に、そのシンボルが既にエクスポートされていると判断されると、エラーとして処理し、リンカを停止させていました。

変更後のコードでは、s->dynimplib != nilの場合でも、エラーを発生させる代わりに、シンボルsのインポート関連の情報をクリアしています。具体的には、s->dynimplibs->extnames->dynimpverss->typeをリセットしています。

このロジックは、コメントにあるように「export overrides import, for openbsd/cgo.」(OpenBSD/Cgoの場合、エクスポートがインポートを上書きする)という原則に基づいています。これにより、OpenBSDのCgoランタイムが内部的に必要とするシンボルが、リンカによって「インポートとエクスポートの競合」として誤って検出されることなく、Goプログラムがそのシンボルをエクスポートする定義を優先して処理できるようになりました。結果として、リンカのエラーが回避され、OpenBSD上でのCgoプログラムのビルドが可能になります。

src/pkg/runtime/cgo/gcc_openbsd_386.c および src/pkg/runtime/cgo/gcc_openbsd_amd64.c の変更

これらのファイルは、OpenBSDアーキテクチャ(386とAMD64)向けのCgoランタイムの一部です。tcb_fixup関数は、スレッドのTCB(Thread Control Block)の修正に関連するものです。

変更はコードのロジック自体ではなく、コメントの修正です。以前のコメントは、メインスレッドのTCBは静的割り当てであるため解放すべきではないと述べていました。しかし、新しいコメントでは、oldtcbを解放すると二重解放の問題が発生する可能性があり、その結果newtcbがメモリリークを引き起こす可能性があると指摘しています。そして、OpenBSDがPT_TLS(POSIX Thread Local Storage)を適切にサポートするようになったら、この問題に対処すべきだと明記しています。

このコメントの変更は、OpenBSDのCgoランタイムにおけるスレッドローカルストレージの管理が複雑であり、メモリリークや二重解放のリスクを伴うことを開発者に注意喚起するものです。これは、Issue 4878の直接的な修正ではありませんが、OpenBSD環境でのCgoの安定性に関連する情報を提供しています。リンカの変更によってビルドが可能になったことで、ランタイムレベルでのこれらの課題がより明確になった可能性があります。

src/run.bash の変更

このシェルスクリプトは、Goのテストを実行するためのものです。変更前は、[ "$GOHOSTOS" == openbsd ] || # issue 4878 という行があり、これはGOHOSTOSopenbsdの場合に、misc/cgo/testディレクトリ内のgo testの実行をスキップするという意味でした。

この行が削除されたことで、OpenBSD環境でもmisc/cgo/testのテストが実行されるようになりました。これは、Issue 4878が修正され、OpenBSD上でのCgoのビルドと実行に関する問題が解決されたため、テストをスキップする必要がなくなったことを示しています。これにより、OpenBSD環境でのCgoの機能がGoのCI/CDパイプラインで継続的にテストされるようになり、将来的な回帰を防ぐのに役立ちます。

関連リンク

参考にした情報源リンク

  • Go言語のCgoに関する公式ドキュメントや関連するGoのソースコード
  • Go Issue 4878の議論スレッド
  • Goのリンカ(cmd/ld)の一般的な動作に関する情報
  • Thread Local Storage (TLS) に関する一般的な情報# [インデックス 15674] ファイルの概要

このコミットは、Go言語のリンカ (src/cmd/ld/go.c) と、OpenBSD向けのCgoランタイム (src/pkg/runtime/cgo/gcc_openbsd_386.c, src/pkg/runtime/cgo/gcc_openbsd_amd64.c)、そしてテストスクリプト (src/run.bash) に変更を加えています。主な目的は、GoとCの相互運用機能であるCgoにおいて、あるシンボルがcgo_exportcgo_importの両方として扱われる場合に発生する問題を解決することです。

コミット

cmd/ld, runtime/cgo: allow a symbol to be both cgo_export and cgo_import.
Fixes #4878.

R=golang-dev, rsc
CC=golang-dev
https://golang.org/cl/7420052

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

https://github.com/golang/go/commit/960d7082ee9b8fda91444167b3c253d5cf5e115d

元コミット内容

commit 960d7082ee9b8fda91444167b3c253d5cf5e115d
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Mon Mar 11 14:24:51 2013 +0800

    cmd/ld, runtime/cgo: allow a symbol to be both cgo_export and cgo_import.
    Fixes #4878.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/7420052
---
 src/cmd/ld/go.c                         | 11 ++++++++---\n src/pkg/runtime/cgo/gcc_openbsd_386.c   |  6 +++---\n src/pkg/runtime/cgo/gcc_openbsd_amd64.c |  6 +++---\n src/run.bash                            |  1 -\n 4 files changed, 14 insertions(+), 10 deletions(-)\n

変更の背景

このコミットは、Go言語のIssue 4878「cmd/ld: symbol is both imported and exported: _cgo_thread_start」を修正するために行われました。この問題は、特にOpenBSD環境において、Cgoを使用する際に特定のシンボル(例: _cgo_thread_start)がGoリンカによって「インポートとエクスポートの両方」として誤って検出され、エラーとなることが原因でした。

通常、シンボルは外部からインポートされるか(cgo_import)、外部にエクスポートされるか(cgo_export)のいずれか一方であるべきです。しかし、OpenBSDのCgoランタイムの特定の設計や、リンカのシンボル解決ロジックの不備により、この二重の定義が検出され、リンカがエラーを発生させていました。これにより、OpenBSD上でのCgoプログラムのビルドが妨げられていました。

この問題は、GoのランタイムがCのコードを呼び出し、CのコードがGoのコードをコールバックするような複雑なCgoのシナリオで顕在化しやすかったと考えられます。リンカは、シンボルが外部ライブラリからインポートされるべきか、それともGoプログラム自身が提供するべきかについて明確な判断ができず、矛盾として扱っていました。

前提知識の解説

Go言語のCgo

Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGoの機能です。これにより、既存のCライブラリをGoから利用したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。

Cgoを使用する際、Goのビルドプロセスは以下のようになります。

  1. Goのソースコード内のimport "C"ブロックを解析し、Cのコードを抽出します。
  2. Cのコンパイラ(通常はGCC)を使用して、抽出されたCのコードをコンパイルします。
  3. GoのコンパイラはGoのコードをコンパイルします。
  4. Goリンカ(cmd/ld)は、コンパイルされたGoのオブジェクトファイルとCのオブジェクトファイルをリンクし、最終的な実行可能ファイルを生成します。この際、GoとCの間でやり取りされるシンボル(関数や変数)の解決が行われます。

cgo_exportcgo_import

Cgoにおいて、GoとCの間でシンボルをやり取りするために、特別なディレクティブが使用されます。

  • cgo_export: Goの関数や変数をCのコードから呼び出せるように、外部にエクスポートする際に使用されます。Goのコードで//export MyGoFunctionのように記述すると、MyGoFunctionというGoの関数がCから呼び出し可能なシンボルとしてエクスポートされます。
  • cgo_import: Cの関数や変数をGoのコードから呼び出せるように、外部からインポートする際に使用されます。Goのコードでimport "C"ブロック内にCの関数宣言を記述すると、その関数は外部のCライブラリからインポートされるシンボルとして扱われます。

リンカは、これらのcgo_exportおよびcgo_importの情報を基に、GoとCのオブジェクトファイル間でシンボルを正しく解決します。

Goリンカ (cmd/ld)

cmd/ldはGo言語のリンカであり、コンパイルされたGoのオブジェクトファイルと、Cgoによって生成されたCのオブジェクトファイル、およびその他のライブラリを結合して実行可能ファイルを生成する役割を担っています。リンカは、プログラム内のすべてのシンボル参照を解決し、それぞれのシンボルがどこで定義されているかを特定します。

Thread Local Storage (TLS)

Thread Local Storage (TLS) は、各スレッドが独自のデータコピーを持つことを可能にするメカニズムです。これにより、グローバル変数のようにアクセスできるが、実際にはスレッドごとに独立した値を持つ変数を定義できます。Cgoのコンテキストでは、GoランタイムとCランタイムがスレッド固有の情報を管理するためにTLSを使用することがあります。OpenBSDのような特定のOSでは、TLSのサポートが他のOSと異なる場合があり、それがCgoランタイムの複雑さや問題の原因となることがあります。

技術的詳細

このコミットの技術的詳細は、主にGoリンカのシンボル解決ロジックと、OpenBSDにおけるCgoランタイムの特殊性に起因します。

リンカのシンボル解決の競合

Goリンカは、loadcgo関数内でCgo関連のシンボルを処理します。以前のリンカのロジックでは、あるシンボルが既にdynimplib(動的インポートライブラリ)を持つ、つまり外部からインポートされるシンボルとしてマークされているにもかかわらず、cgo_exportとしても定義されようとすると、これをエラーとして扱っていました。

if(s->dynimplib != nil) {
    fprintf(2, "%s: symbol is both imported and exported: %s\\n", argv0, local);
    nerrors++;
}

このロジックは、一般的なケースではシンボル定義の矛盾を検出するのに役立ちますが、OpenBSDのCgoランタイムの特定のシナリオでは問題を引き起こしました。OpenBSDのCgoランタイムは、GoランタイムがCのコードを呼び出し、そのCのコードがGoのランタイムの一部(例えば、スレッドの初期化に関連する関数)をコールバックするような、より複雑な相互作用を必要とすることがあります。この際、特定のシンボル(例: _cgo_thread_start)が、Go側からCにエクスポートされると同時に、C側からGoにインポートされるかのようにリンカに認識されてしまうことがありました。これは、シンボルが実際に二重に定義されているわけではなく、リンカの視点から見た「インポート」と「エクスポート」の解釈が、OpenBSDのCgoの特殊なフローと合致しなかったためです。

修正アプローチ:「エクスポートがインポートを上書きする」

このコミットの主要な修正は、リンカのloadcgo関数において、シンボルが既にインポートされているとマークされていても、それがcgo_exportとして定義されようとする場合に、既存のインポート情報をクリアし、エクスポートを優先させるというロジックを追加したことです。

// export overrides import, for openbsd/cgo.
// see issue 4878.
if(s->dynimplib != nil) {
    s->dynimplib = nil;
    s->extname = nil;
    s->dynimpvers = nil;
    s->type = 0;
}

この変更により、リンカは「このシンボルは外部からインポートされるものだが、Go自身がエクスポートするものとしても定義されている。この場合はGoのエクスポートを優先しよう」と判断するようになります。これにより、OpenBSDのCgoランタイムが内部的に必要とするシンボルが、リンカによって矛盾として扱われることなく、正しく解決されるようになりました。

OpenBSD Cgoランタイムの変更

src/pkg/runtime/cgo/gcc_openbsd_386.c および src/pkg/runtime/cgo/gcc_openbsd_amd64.c の変更は、tcb_fixup関数内のコメントの修正です。以前のコメントでは、メインスレッドのTCB (Thread Control Block) は静的割り当てであるため解放すべきではないとされていましたが、新しいコメントでは、oldtcbを解放すると二重解放の問題を引き起こす可能性があり、newtcbがメモリリークを引き起こす可能性があると指摘しています。そして、OpenBSDがPT_TLS(POSIX Thread Local Storage)を適切にサポートするようになったら、この問題を解決すべきだと述べています。

この変更は、直接的にcgo_exportcgo_importの競合を解決するものではありませんが、OpenBSDのCgoランタイムにおけるスレッドローカルストレージの管理に関する既存の課題を明確にしています。これは、Issue 4878の根本原因の一部、またはその修正によって顕在化した別の問題に関連している可能性があります。リンカの変更がシンボル解決の矛盾を解消したことで、ランタイムレベルでのメモリ管理の課題がより明確になったのかもしれません。

テストスクリプトの変更

src/run.bashの変更は、OpenBSD環境でmisc/cgo/testのテストがスキップされていた行を削除しています。これは、Issue 4878が修正されたため、OpenBSD上でもCgoのテストが正常に実行できるようになったことを示しています。これにより、OpenBSD環境でのCgoの安定性が向上したことが確認できます。

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

src/cmd/ld/go.c

--- a/src/cmd/ld/go.c
+++ b/src/cmd/ld/go.c
@@ -499,11 +499,16 @@ loadcgo(char *file, char *pkg, char *p, int n)
 			remote = local;
 			local = expandpkg(local, pkg);
 			s = lookup(local, 0);
+
+			// export overrides import, for openbsd/cgo.
+			// see issue 4878.
 			if(s->dynimplib != nil) {
-				fprintf(2, "%s: symbol is both imported and exported: %s\\n", argv0, local);
-				nerrors++;
+				s->dynimplib = nil;
+				s->extname = nil;
+				s->dynimpvers = nil;
+				s->type = 0;
 			}
-			
+
 			if(s->cgoexport == 0) {
 				if(strcmp(f[0], "cgo_export_static") == 0)
 					s->cgoexport |= CgoExportStatic;

src/pkg/runtime/cgo/gcc_openbsd_386.c および src/pkg/runtime/cgo/gcc_openbsd_amd64.c

--- a/src/pkg/runtime/cgo/gcc_openbsd_386.c
+++ b/src/pkg/runtime/cgo/gcc_openbsd_386.c
@@ -48,9 +48,9 @@ tcb_fixup(int mainthread)
 	bcopy(oldtcb, newtcb + TLS_SIZE, TCB_SIZE);
 	__set_tcb(newtcb + TLS_SIZE);
 
-	// The main thread TCB is a static allocation - do not try to free it.
-	if(!mainthread)
-		free(oldtcb);
+	// NOTE(jsing, minux): we can't free oldtcb without causing double-free
+	// problem. so newtcb will be memory leaks. Get rid of this when OpenBSD
+	// has proper support for PT_TLS.
 }
 
 static void *

gcc_openbsd_amd64.cも同様の変更)

src/run.bash

--- a/src/run.bash
+++ b/src/run.bash
@@ -74,7 +74,6 @@ go run $GOROOT/test/run.go - .
 ) || exit $?\n
 [ "$CGO_ENABLED" != 1 ] ||
-[ "$GOHOSTOS" == openbsd ] || # issue 4878
 (xcd ../misc/cgo/test
 go test
 case "$GOHOSTOS-$GOARCH" in

コアとなるコードの解説

src/cmd/ld/go.c の変更

この変更は、Goリンカのloadcgo関数内で行われています。loadcgoは、Cgoによって生成されたシンボル情報を処理する部分です。

変更前のコードでは、s->dynimplib != nil(シンボルsが動的インポートライブラリを持つ、つまり外部からインポートされるシンボルとしてマークされている)の場合に、そのシンボルが既にエクスポートされていると判断されると、エラーとして処理し、リンカを停止させていました。

変更後のコードでは、s->dynimplib != nilの場合でも、エラーを発生させる代わりに、シンボルsのインポート関連の情報をクリアしています。具体的には、s->dynimplibs->extnames->dynimpverss->typeをリセットしています。

このロジックは、コメントにあるように「export overrides import, for openbsd/cgo.」(OpenBSD/Cgoの場合、エクスポートがインポートを上書きする)という原則に基づいています。これにより、OpenBSDのCgoランタイムが内部的に必要とするシンボルが、リンカによって「インポートとエクスポートの競合」として誤って検出されることなく、Goプログラムがそのシンボルをエクスポートする定義を優先して処理できるようになりました。結果として、リンカのエラーが回避され、OpenBSD上でのCgoプログラムのビルドが可能になります。

src/pkg/runtime/cgo/gcc_openbsd_386.c および src/pkg/runtime/cgo/gcc_openbsd_amd64.c の変更

これらのファイルは、OpenBSDアーキテクチャ(386とAMD64)向けのCgoランタイムの一部です。tcb_fixup関数は、スレッドのTCB(Thread Control Block)の修正に関連するものです。

変更はコードのロジック自体ではなく、コメントの修正です。以前のコメントは、メインスレッドのTCBは静的割り当てであるため解放すべきではないと述べていました。しかし、新しいコメントでは、oldtcbを解放すると二重解放の問題が発生する可能性があり、その結果newtcbがメモリリークを引き起こす可能性があると指摘しています。そして、OpenBSDがPT_TLS(POSIX Thread Local Storage)を適切にサポートするようになったら、この問題に対処すべきだと明記しています。

このコメントの変更は、OpenBSDのCgoランタイムにおけるスレッドローカルストレージの管理が複雑であり、メモリリークや二重解放のリスクを伴うことを開発者に注意喚起するものです。これは、Issue 4878の直接的な修正ではありませんが、OpenBSD環境でのCgoの安定性に関連する情報を提供しています。リンカの変更によってビルドが可能になったことで、ランタイムレベルでのこれらの課題がより明確になった可能性があります。

src/run.bash の変更

このシェルスクリプトは、Goのテストを実行するためのものです。変更前は、[ "$GOHOSTOS" == openbsd ] || # issue 4878 という行があり、これはGOHOSTOSopenbsdの場合に、misc/cgo/testディレクトリ内のgo testの実行をスキップするという意味でした。

この行が削除されたことで、OpenBSD環境でもmisc/cgo/testのテストが実行されるようになりました。これは、Issue 4878が修正され、OpenBSD上でのCgoのビルドと実行に関する問題が解決されたため、テストをスキップする必要がなくなったことを示しています。これにより、OpenBSD環境でのCgoの機能がGoのCI/CDパイプラインで継続的にテストされるようになり、将来的な回帰を防ぐのに役立ちます。

関連リンク

参考にした情報源リンク

  • Go言語のCgoに関する公式ドキュメントや関連するGoのソースコード
  • Go Issue 4878の議論スレッド
  • Goのリンカ(cmd/ld)の一般的な動作に関する情報
  • Thread Local Storage (TLS) に関する一般的な情報