[インデックス 11285] ファイルの概要
このコミットは、Go言語のCGOテストスイートがWindows環境で正しく動作するようにするための修正を目的としています。特に、Windows固有のCGOの挙動、コンパイラの呼び出し規約、環境変数の扱い、およびスタックサイズに関する問題に対処しています。これにより、Goのクロスプラットフォーム互換性が向上し、Windows上でのCGOを利用した開発とテストがより信頼性の高いものになります。
コミット
commit 8d6958fc041eee42e78ba3c20569c71c35795b8b
Author: Alex Brainman <alex.brainman@gmail.com>
Date: Fri Jan 20 12:59:44 2012 +1100
misc/cgo/test: make tests run on windows
- use proper Win64 gcc calling convention when
calling initcgo on amd64
- increase g0 stack size to 64K on amd64 to make
it the same as 386
- implement C.sleep
- do not use C.stat, since it is renamed to C._stat by mingw
- use fopen to implement TestErrno, since C.strtol
always succeeds on windows
- skip TestSetEnv on windows, because os.Setenv
sets windows process environment, while C.getenv
inspects internal C runtime variable instead
R=golang-dev, vcc.163, rsc
CC=golang-dev
https://golang.org/cl/5500094
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/8d6958fc041eee42e78ba3c20569c71c35795b8b
元コミット内容
commit 8d6958fc041eee42e78ba3c20569c71c35795b8b
Author: Alex Brainman <alex.brainman@gmail.com>
Date: Fri Jan 20 12:59:44 2012 +1100
misc/cgo/test: make tests run on windows
- use proper Win64 gcc calling convention when
calling initcgo on amd64
- increase g0 stack size to 64K on amd64 to make
it the same as 386
- implement C.sleep
- do not use C.stat, since it is renamed to C._stat by mingw
- use fopen to implement TestErrno, since C.strtol
always succeeds on windows
- skip TestSetEnv on windows, because os.Setenv
sets windows process environment, while C.getenv
inspects internal C runtime variable instead
R=golang-dev, vcc.163, rsc
CC=golang-dev
https://golang.org/cl/5500094
変更の背景
Go言語のCGO(C言語との相互運用機能)は、GoプログラムからCライブラリを呼び出すための重要な機能です。しかし、異なるオペレーティングシステム(特にWindows)やアーキテクチャ(amd64)では、C言語のコンパイラやランタイムの挙動に差異があるため、CGOを利用したテストが正しく動作しないという問題が発生していました。
このコミットの主な背景は、GoのCGOテストスイートがWindows環境で失敗する問題を解決することです。具体的には、以下の点が課題となっていました。
- 呼び出し規約の不一致: Windows上のamd64アーキテクチャにおけるGCCの呼び出し規約が、Goのランタイムが期待するものと異なっていたため、
initcgo
関数の呼び出しで問題が発生していました。 - スタックサイズの差異:
g0
(Goランタイムの初期ゴルーチン)のスタックサイズが、amd64と386アーキテクチャで異なっており、amd64で不足する可能性がありました。 - C標準ライブラリ関数の差異:
sleep
やstat
といったC標準ライブラリ関数が、Windows(MinGW)環境で異なる名前や挙動を示すことがありました。特にstat
は_stat
にリネームされることが一般的です。 - エラーハンドリングの差異:
strtol
のような関数がWindowsでは常に成功し、期待されるエラーを返さない場合がありました。 - 環境変数の管理:
os.Setenv
とC言語のgetenv
が、Windows上で異なる環境変数のスコープを参照するため、テストが意図した通りに動作しませんでした。os.Setenv
はOSプロセス全体の環境変数を設定するのに対し、Cランタイムのgetenv
はCランタイムが起動時にコピーした内部の環境変数セットを参照します。
これらの問題を解決し、GoのCGO機能がWindows環境でも安定して動作し、テストがパスするようにすることが、このコミットの重要な目的でした。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念について理解しておく必要があります。
-
CGO:
- Go言語とC言語の相互運用を可能にするGoの機能です。GoコードからC関数を呼び出したり、CコードからGo関数を呼び出したりできます。
import "C"
という特殊なインポート宣言を使用し、Goコード内にCコードを直接記述したり、既存のCライブラリをリンクしたりします。- CGOは、Goのビルドプロセス中にCコンパイラ(通常はGCC)を呼び出してCコードをコンパイルし、Goの実行可能ファイルにリンクします。
-
呼び出し規約 (Calling Convention):
- 関数が呼び出される際に、引数がどのようにレジスタやスタックに渡され、戻り値がどのように返されるか、スタックがどのようにクリーンアップされるかなどを定義するルールセットです。
- 異なるアーキテクチャ(例: x86、amd64)や異なるコンパイラ(例: MSVC、GCC)では、異なる呼び出し規約が使用されることがあります。
- Win64 GCC Calling Convention: Windows上の64ビットGCCコンパイラが使用する呼び出し規約です。これは、LinuxやmacOSのSystem V AMD64 ABIとは異なります。例えば、最初の4つの整数/ポインタ引数を渡すレジスタが異なります(WindowsではRCX, RDX, R8, R9、System VではRDI, RSI, RDX, RCX, R8, R9)。
-
g0
(Goランタイムの初期ゴルーチン):- Goランタイムが起動する際に最初に作成される特別なゴルーチンです。
- Goのスケジューラやメモリ管理など、ランタイムの低レベルな処理を実行するために使用されます。
g0
は、Goのユーザーコードが実行される通常のゴルーチンとは異なり、システムコールやCGO呼び出しの際に使用されるスタックを持っています。このスタックサイズが不足すると、スタックオーバーフローなどの問題が発生する可能性があります。
-
_chkstk.o
:- Windows環境で、スタックの拡張を処理するためのヘルパー関数を含むオブジェクトファイルです。
- 大きなスタックフレームを割り当てる際に、スタックガードページをチェックし、必要に応じてスタックをコミットするために使用されます。
- GCCコンパイラが生成するコードで、スタックを動的に拡張する必要がある場合(特に大きなローカル変数や再帰呼び出しが多い場合)にリンクされます。
-
C.stat
とC._stat
:stat
は、ファイルの状態(サイズ、パーミッション、最終更新時刻など)を取得するためのPOSIX標準のC関数です。- WindowsのMinGW(Minimalist GNU for Windows)環境では、MicrosoftのCランタイムライブラリとの互換性のために、
stat
関数が_stat
という名前にリネームされていることがよくあります。これは、Windows APIの命名規則に合わせるためです。CGOでC関数を呼び出す際には、この名前の差異を考慮する必要があります。
-
os.Setenv
とC.getenv
(Windowsにおける挙動):os.Setenv
(Go): Goの標準ライブラリ関数で、オペレーティングシステムレベルの環境変数を設定します。Windowsでは、SetEnvironmentVariable
というWin32 APIを呼び出し、プロセス全体の環境ブロックを変更します。C.getenv
(CGO経由のC関数): C標準ライブラリの関数で、環境変数の値を取得します。WindowsのCランタイムでは、プロセス起動時にOSの環境変数をコピーして内部的に保持することが一般的です。getenv
はこの内部コピーを参照するため、os.Setenv
で変更されたOSレベルの環境変数が、Cランタイムの内部コピーには即座に反映されない場合があります。これにより、GoとCの間で環境変数の認識に不一致が生じることがあります。
技術的詳細
このコミットは、Windows環境でのCGOテストの安定性を向上させるために、複数の技術的な側面から修正を加えています。
-
Win64 GCC 呼び出し規約の適用 (
src/pkg/runtime/asm_amd64.s
):- amd64アーキテクチャのWindows環境では、GCCの呼び出し規約がLinuxやmacOSとは異なります。特に、関数呼び出しの際に引数を渡すレジスタが異なります。
initcgo
関数を呼び出す際、Goランタイムは最初の引数(g0
のポインタ)を特定のレジスタに配置する必要があります。WindowsのWin64呼び出し規約では、最初の引数はRCX
レジスタに渡されます。- 修正前は、Goランタイムが
g0
をDI
レジスタに配置してCALL AX
(initcgo
)を呼び出していました。これはSystem V AMD64 ABI(Linux/macOSで一般的)に準拠しています。 - 修正では、
MOVQ DI, CX
という命令を追加し、DI
レジスタに格納されているg0
の値をCX
レジスタにコピーしてからCALL AX
を実行するように変更されました。これにより、WindowsのWin64呼び出し規約に適合し、initcgo
が正しく引数を受け取れるようになります。
-
g0
スタックサイズの増加 (src/pkg/runtime/asm_amd64.s
):g0
ゴルーチンは、Goランタイムの初期化やCGO呼び出しなど、重要な低レベル処理に使用されます。- 386アーキテクチャでは
g0
のスタックサイズが64KBでしたが、amd64では8KBに設定されていました。これは、CGO呼び出しなどでスタックが不足する可能性がありました。 - 修正では、
LEAQ (-8192+104)(SP), BX
をLEAQ (-64*1024+104)(SP), BX
に変更し、g0
のスタックサイズを8KBから64KBに増やしました。これにより、amd64環境でも386と同じスタックサイズが確保され、スタックオーバーフローのリスクが軽減されます。
-
C.sleep
の実装 (misc/cgo/test/sleep_windows.go
とmisc/cgo/test/issue1560.go
):- POSIXシステムでは
sleep
関数が利用できますが、Windowsには直接的なsleep
関数はありません。代わりにSleep
(大文字S)というWin32 API関数があります。 misc/cgo/test/sleep_windows.go
に新しいファイルが追加され、CGO経由でSleep
Win32 APIを呼び出すsleep
関数がC言語で実装されました。これにより、GoのテストコードからC.sleep
を呼び出せるようになります。misc/cgo/test/issue1560.go
では、この新しいsleep
関数のプロトタイプがCGOのコメントブロックに追加され、Goコードから利用可能になりました。
- POSIXシステムでは
-
C.stat
の使用回避とC._stat
への対応 (misc/cgo/test/basic.go
):- WindowsのMinGW環境では、
stat
関数が_stat
にリネームされているため、C.stat
を直接呼び出すとリンクエラーや未定義シンボルエラーが発生する可能性がありました。 - コミットでは、
Size
関数(C.stat
を使用していた)をbasic.go
から削除しました。これは、C.stat
の代わりにC._stat
を使用するように変更するのではなく、テストの目的上、stat
関数自体が必須ではなかったため、よりシンプルな解決策として削除が選択されたと考えられます。
- WindowsのMinGW環境では、
-
TestErrno
のC.fopen
を用いた実装 (misc/cgo/test/basic.go
):TestErrno
は、CGO呼び出しでエラーが発生した際に、Goのos.Errno
が正しく設定されるかをテストするものです。- 以前は
C.strtol
を使用していましたが、Windowsではstrtol
が常に成功し、無効な入力に対してもエラーを返さない場合があるため、テストが意図した通りに機能しませんでした。 - 修正では、存在しないファイルを
C.fopen
で開こうとすることでエラーを発生させるように変更されました。fopen
はファイルが見つからない場合にNULL
を返し、errno
をENOENT
に設定するため、Windowsでも確実にエラーを発生させ、os.ENOENT
が正しく取得できることをテストできます。
-
TestSetEnv
の Windows でのスキップ (misc/cgo/test/env.go
):TestSetEnv
は、Goのos.Setenv
で設定した環境変数がCGO経由でC.getenv
から正しく読み取れるかをテストするものです。- しかし、Windowsでは
os.Setenv
がOSプロセス環境を変更するのに対し、Cランタイムのgetenv
はプロセス起動時にコピーされた内部の環境変数セットを参照します。このため、os.Setenv
で変更してもC.getenv
には反映されないという不一致が生じます。 - この問題を回避するため、
runtime.GOOS == "windows"
の場合にTestSetEnv
をスキップするように変更されました。これにより、Windowsでのテストの誤った失敗を防ぎます。
-
Makefileの変更 (
misc/cgo/test/Makefile
):- Windows環境でのビルドをサポートするために、
Makefile
が更新されました。 GOOS
がwindows
の場合に、GCCのバージョンとアーキテクチャ(386またはamd64)に基づいて適切なGCCライブラリディレクトリ(GCCLIBDIR
)を設定します。_chkstk.o
または_chkstk_ms.o
(amd64の場合)をCGOのオブジェクトファイルに追加し、libgcc.a
から抽出するように指示しています。これは、Windowsで大きなスタックを扱う際に必要となるスタックチェックルーチンをリンクするためです。sleep_windows.go
がCGOFILES
に追加され、ビルドプロセスに含まれるようになりました。
- Windows環境でのビルドをサポートするために、
これらの変更により、GoのCGOテストスイートはWindows環境でもより堅牢になり、Goのクロスプラットフォーム開発の信頼性が向上しました。
コアとなるコードの変更箇所
このコミットで変更された主要なファイルとその内容は以下の通りです。
-
misc/cgo/test/Makefile
:- Windows (GOOS=windows) 環境でのビルド設定を追加。
- GCCのバージョンとアーキテクチャ (386/amd64) に応じて、適切なGCCライブラリディレクトリ (
GCCLIBDIR
) を設定。 _chkstk.o
または_chkstk_ms.o
をCGOのオブジェクトファイル (CGO_OFILES
) に追加し、libgcc.a
から抽出するルールを定義。sleep_windows.go
をCGOのソースファイル (CGOFILES
) に追加。
-
misc/cgo/test/basic.go
:Size
関数(C.stat
を使用していた)を削除。testErrno
関数を修正。C.strtol
の代わりにC.fopen
を使用してエラーをテストするように変更。存在しないファイルをオープンしようとすることでos.ENOENT
が発生することを確認。
-
misc/cgo/test/env.go
:testSetEnv
関数にWindows環境でのスキップロジックを追加。runtime.GOOS == "windows"
の場合、テストをスキップする。これは、os.Setenv
とC.getenv
のWindowsにおける挙動の差異によるもの。
-
misc/cgo/test/issue1560.go
:- CGOコメントブロック内に
unsigned int sleep(unsigned int seconds);
のプロトタイプ宣言を追加。これにより、GoコードからC.sleep
を呼び出せるようになる。
- CGOコメントブロック内に
-
misc/cgo/test/sleep_windows.go
(新規ファイル):- Windows環境で
C.sleep
を実装するための新しいファイル。 - CGOコメントブロック内で、Win32 APIの
Sleep
関数を呼び出すsleep
関数をC言語で定義。
- Windows環境で
-
src/pkg/runtime/asm_amd64.s
:_rt0_amd64
関数内で、initcgo
を呼び出す際の引数渡しを修正。MOVQ DI, CX
を追加し、g0
のポインタをDI
からCX
レジスタにコピー。これはWin64 GCCの呼び出し規約に合わせるため。
g0
のスタックサイズを8KBから64KBに増加。LEAQ (-8192+104)(SP), BX
をLEAQ (-64*1024+104)(SP), BX
に変更。
-
src/run.bash
:- CGOテストの実行条件から、
GOHOSTOS
がwindows
の場合にスキップする条件を削除。これにより、WindowsでもCGOテストが実行されるようになる。
- CGOテストの実行条件から、
コアとなるコードの解説
src/pkg/runtime/asm_amd64.s
の変更
--- a/src/pkg/runtime/asm_amd64.s
+++ b/src/pkg/runtime/asm_amd64.s
@@ -16,7 +16,7 @@ TEXT _rt0_amd64(SB),7,$-8
// create istack out of the given (operating system) stack.
// initcgo may update stackguard.
MOVQ $runtime·g0(SB), DI
- LEAQ (-8192+104)(SP), BX
+ LEAQ (-64*1024+104)(SP), BX
MOVQ BX, g_stackguard(DI)
MOVQ SP, g_stackbase(DI)
@@ -24,7 +24,9 @@ TEXT _rt0_amd64(SB),7,$-8
MOVQ initcgo(SB), AX
TESTQ AX, AX
JZ needtls
-\tCALL AX // g0 already in DI
+\t// g0 already in DI
+\tMOVQ DI, CX // Win64 uses CX for first parameter
+\tCALL AX
CMPL runtime·iswindows(SB), $0
JEQ ok
このアセンブリコードは、Goランタイムの初期化ルーチンの一部です。
-
LEAQ (-64*1024+104)(SP), BX
:LEAQ
(Load Effective Address Quadword) 命令は、指定されたアドレスを計算し、その結果をレジスタに格納します。SP
はスタックポインタです。(-64*1024+104)(SP)
は、現在のスタックポインタから64KB(64*1024
バイト)を引いたアドレスを計算しています。- これは、
g0
ゴルーチンのスタックガード(スタックオーバーフローを検出するための境界)を設定する部分です。以前は8KB (-8192
) でしたが、この変更によりg0
のスタックサイズが64KBに拡張されました。これにより、CGO呼び出しなどでより多くのスタック領域が必要な場合に、スタックオーバーフローを防ぎます。
-
MOVQ DI, CX // Win64 uses CX for first parameter
:initcgo
関数を呼び出す直前に追加された命令です。initcgo
は、Goランタイムの初期化に関連するCGOのセットアップを行う関数です。MOVQ DI, CX
は、DI
レジスタに格納されている値をCX
レジスタにコピーします。- コメントにあるように、Win64の呼び出し規約では、関数の最初の引数は
RCX
(64ビットレジスタの場合)またはCX
(16ビットレジスタの場合)レジスタに渡されます。Goのランタイムは通常、System V AMD64 ABI(Linux/macOSで一般的)に従い、最初の引数をRDI
レジスタに配置します。 - この変更により、Windows環境で
initcgo
が正しく引数を受け取れるようになり、CGOの初期化が正常に行われるようになります。
misc/cgo/test/basic.go
の変更
--- a/misc/cgo/test/basic.go
+++ b/misc/cgo/test/basic.go
@@ -69,17 +69,6 @@ func uuidgen() {\n \tC.uuid_generate(&uuid[0])\n }\n \n-func Size(name string) (int64, error) {\n-\tvar st C.struct_stat\n-\tp := C.CString(name)\n-\t_, err := C.stat(p, &st)\n-\tC.free(unsafe.Pointer(p))\n-\tif err != nil {\n-\t\treturn 0, err\n-\t}\n-\treturn int64(C.ulong(st.st_size)), nil\n-}\n-\n func Strtol(s string, base int) (int, error) {\n \tp := C.CString(s)\n \tn, err := C.strtol(p, nil, C.int(base))\n@@ -112,9 +101,17 @@ func testAtol(t *testing.T) {\n }\n \n func testErrno(t *testing.T) {\n-\tn, err := Strtol(\"asdf\", 123)\n-\tif n != 0 || err != os.EINVAL {\n-\t\tt.Error(\"Strtol: \", n, err)\n+\tp := C.CString(\"no-such-file\")\n+\tm := C.CString(\"r\")
+\tf, err := C.fopen(p, m)\n+\tC.free(unsafe.Pointer(p))\n+\tC.free(unsafe.Pointer(m))\n+\tif err == nil {\n+\t\tC.fclose(f)\n+\t\tt.Fatalf(\"C.fopen: should fail\")\n+\t}\n+\tif err != os.ENOENT {\n+\t\tt.Fatalf(\"C.fopen: unexpected error: \", err)\n \t}\n }\n \n```
1. **`Size` 関数の削除**:
* `Size`関数は、Cの`stat`関数を使用してファイルのサイズを取得していました。
* WindowsのMinGW環境では、`stat`関数が`_stat`にリネームされているため、`C.stat`を直接呼び出すと問題が発生する可能性がありました。このテストの目的上、`stat`関数自体が必須ではなかったため、削除されました。
2. **`testErrno` 関数の修正**:
* 以前は`Strtol`(Cの`strtol`をラップ)を使用してエラーをテストしていましたが、Windowsでは`strtol`が無効な入力に対してもエラーを返さない場合があるため、テストが機能しませんでした。
* 新しい実装では、`C.fopen`を使用して存在しないファイル(`"no-such-file"`)を読み取りモード(`"r"`)で開こうとします。
* `C.fopen`はファイルが見つからない場合に`NULL`を返し、`errno`を`ENOENT`(No such file or directory)に設定します。
* この変更により、Windows環境でも確実にエラーを発生させ、Goの`os.ENOENT`が正しく取得できることをテストできるようになりました。これは、CGO呼び出しにおけるエラー伝播の正確性を保証するために重要です。
### `misc/cgo/test/env.go` の変更
```diff
--- a/misc/cgo/test/env.go
+++ b/misc/cgo/test/env.go
@@ -10,12 +10,21 @@ package cgotest
import "C"\n import (\n \t"os"\n+\t"runtime"\n \t"testing"\n \t"unsafe"\n )\n \n // This is really an os package test but here for convenience.\n func testSetEnv(t *testing.T) {\n+\tif runtime.GOOS == "windows" {\n+\t\t// Go uses SetEnvironmentVariable on windows. Howerver,\n+\t\t// C runtime takes a *copy* at process startup of thei\n+\t\t// OS environment, and stores it in environ/envp.\n+\t\t// It is this copy that\tgetenv/putenv manipulate.\n+\t\tt.Logf("skipping test")\n+\t\treturn\n+\t}\n \tconst key = "CGO_OS_TEST_KEY"\n \tconst val = "CGO_OS_TEST_VALUE"\n \tos.Setenv(key, val)\n```
* `testSetEnv` 関数は、Goの`os.Setenv`で設定した環境変数がCGO経由で`C.getenv`から正しく読み取れるかをテストするものでした。
* 追加された`if runtime.GOOS == "windows"`ブロックは、Windows環境でのこのテストの実行をスキップします。
* コメントで説明されているように、Windowsでは`os.Setenv`がOSプロセス全体の環境変数を変更するのに対し、Cランタイムの`getenv`はプロセス起動時に作成された内部コピーを参照します。このため、Go側で設定した環境変数がC側から見えないという不一致が生じ、テストが失敗します。このスキップは、テストの誤った失敗を防ぐための実用的な解決策です。
### `misc/cgo/test/sleep_windows.go` (新規ファイル)
```go
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cgotest
/*
#include <windows.h>
unsigned int sleep(unsigned int seconds) {
Sleep(1000 * seconds);
return 0;
}
*/
import "C"
この新規ファイルは、Windows環境でCGO経由でsleep
関数を提供します。
- CGOコメントブロック内で、C言語の
sleep
関数が定義されています。 - この
sleep
関数は、WindowsのWin32 APIであるSleep
関数(ミリ秒単位で待機)を呼び出しています。Sleep(1000 * seconds)
とすることで、引数で渡された秒数をミリ秒に変換してSleep
に渡しています。 - これにより、Goのテストコードが
C.sleep
を呼び出した際に、Windows環境でも正しく指定された時間だけ実行を一時停止できるようになります。
関連リンク
- Go Change-list: https://golang.org/cl/5500094
参考にした情報源リンク
- Go言語のCGOに関する公式ドキュメントやチュートリアル
- WindowsのWin64呼び出し規約に関する情報 (Microsoft Docs, GCCドキュメントなど)
- MinGWにおけるC標準ライブラリ関数の挙動に関する情報
- Windowsにおける環境変数の管理に関する情報 (SetEnvironmentVariable, GetEnvironmentVariable Win32 APIなど)
- Goのランタイム(特に
g0
ゴルーチンとスタック管理)に関する情報 _chkstk.o
に関する情報 (GCCドキュメント、Windows開発者向けリソース)errno
とエラーハンドリングに関するC言語の標準fopen
関数の挙動に関するC言語の標準strtol
関数の挙動に関するC言語の標準stat
関数と_stat
関数の差異に関する情報os.Setenv
とC.getenv
のWindowsにおける相互作用に関する情報- https://learn.microsoft.com/ja-jp/cpp/build/x64-calling-convention?view=msvc-170 (Win64 呼び出し規約)
- https://learn.microsoft.com/ja-jp/windows/win32/api/winbase/nf-winbase-setenvironmentvariable (SetEnvironmentVariable)
- https://learn.microsoft.com/ja-jp/windows/win32/api/synchapi/nf-synchapi-sleep (Sleep function)
- https://gcc.gnu.org/onlinedocs/gcc/Windows-x86-64-Options.html (GCC Windows x86-64 Options)
- https://www.gnu.org/software/libc/manual/html_node/Environment-Variables.html (GNU C Library - Environment Variables)
- https://www.gnu.org/software/libc/manual/html_node/File-Status.html (GNU C Library - File Status)
- https://www.gnu.org/software/libc/manual/html_node/String-Conversion.html (GNU C Library - String Conversion)
- https://www.gnu.org/software/libc/manual/html_node/Opening-and-Closing-Files.html (GNU C Library - Opening and Closing Files)
- https://go.dev/doc/articles/cgo (Go and CGO)
- https://go.dev/src/runtime/asm_amd64.s (Go runtime assembly for amd64)
- https://go.dev/src/misc/cgo/test/ (Go cgo tests)