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

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

このコミットは、Go言語のランタイムにおけるCGO(C言語との相互運用機能)のARMアーキテクチャ向けビルドに関する修正と、関連するテストの再有効化を目的としています。具体的には、ARMアセンブリコードの修正と、CGOランタイムのC言語ソースファイルの追加、そしてテストスクリプトからのARMアーキテクチャ固有のCGOテスト無効化の解除が含まれています。

コミット

  • コミットハッシュ: 0598114a90306b9dc8cc0e38a7566448308946d4
  • 作者: Russ Cox rsc@golang.org
  • コミット日時: 2013年3月1日 金曜日 16:24:23 -0500

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

https://github.com/golang/go/commit/0598114a90306b9dc8cc0e38a7566448308946d4

元コミット内容

runtime/cgo: fix arm build, re-enable test

Fixes #4961.

R=golang-dev, r, minux.ma
CC=golang-dev
https://golang.org/cl/7443048

変更の背景

このコミットの主な目的は、Go言語のCGO機能がARMアーキテクチャ上で正しく動作しない問題を修正することです。コミットメッセージにある Fixes #4961 は、この変更が特定のバグ報告(Issue 4961)に対応するものであることを示しています。

GoのCGOは、GoプログラムからC言語のコードを呼び出すためのメカニズムを提供します。これは、既存のCライブラリを利用したり、パフォーマンスが重要な部分をCで記述したりする場合に非常に有用です。しかし、異なるアーキテクチャ(この場合はARM)やオペレーティングシステムでは、CGOがGoのランタイムとCコードの間で正しく連携するために、アーキテクチャ固有のアセンブリコードやCコードの調整が必要になることがあります。

このコミットが行われた2013年当時、ARMアーキテクチャはモバイルデバイスを中心に普及が進んでおり、Goがこれらの環境で安定して動作することは重要でした。Issue #4961は、おそらくARM環境でのCGOビルドまたは実行時に発生する問題(例えば、クラッシュ、不正な動作、ビルドエラーなど)を報告していたと考えられます。この問題のため、src/run.bash スクリプトではARMアーキテクチャでのCGOテストが一時的に無効化されていました。

このコミットは、問題の根本原因を特定し、アセンブリコードとCコードの両方で必要な修正を適用することで、ARM上でのCGOの安定性を回復し、関連するテストを再度有効にすることを目指しています。

前提知識の解説

Go言語のCGO

CGOは、GoプログラムがC言語の関数を呼び出したり、C言語のデータ構造を利用したりするためのGoの機能です。Goのソースファイル内で import "C" と記述することでCGOが有効になり、GoとCのコードを混在させることができます。CGOは、Goのビルドプロセス中にCコンパイラ(通常はGCCやClang)を呼び出し、CコードをコンパイルしてGoの実行可能ファイルにリンクします。

CGOの仕組みは、GoのランタイムとCのランタイムの間でスタック、レジスタ、メモリなどを適切に管理する必要があります。特に、GoのGoroutineとCのスレッドは異なるスケジューリングモデルを持つため、GoからC関数を呼び出す際には、GoのスケジューラがC関数が完了するまでそのGoroutineをブロックし、必要に応じてOSスレッドを割り当てるなどの処理が行われます。

Goのランタイムとアセンブリ言語

Goのランタイムは、ガベージコレクション、スケジューリング、メモリ管理など、Goプログラムの実行を支える基盤です。Goのランタイムの一部は、パフォーマンスや低レベルの操作のためにアセンブリ言語で記述されています。特に、異なるアーキテクチャ(x86, ARMなど)やOS(Linux, Windows, macOSなど)に特化した処理(システムコール、コンテキストスイッチ、CGOの呼び出し規約の調整など)は、アセンブリ言語で実装されることが多いです。

Goのアセンブリ言語は、一般的なアセンブリ言語とは異なる独自の構文(Plan 9アセンブラの派生)を持っています。ファイル拡張子は通常 .s です。

ARMアーキテクチャ

ARM(Advanced RISC Machine)は、モバイルデバイス、組み込みシステム、最近ではサーバーやデスクトップPCでも広く使用されているRISC(Reduced Instruction Set Computer)ベースのプロセッサアーキテクチャです。ARMプロセッサは、その電力効率の高さと性能のバランスから、スマートフォンやタブレットのSoC(System on a Chip)としてデファクトスタンダードとなっています。

ARMアーキテクチャには、レジスタセット、命令セット、メモリモデル、割り込み処理など、x86などの他のアーキテクチャとは異なる独自の特性があります。CGOのような低レベルの相互運用機能では、これらのアーキテクチャ固有の特性を考慮したアセンブリコードの記述が不可欠です。

スタックポインタとフレームポインタ (ARM)

ARMアーキテクチャでは、SP (Stack Pointer) レジスタが現在のスタックのトップを指します。関数呼び出し時には、引数やローカル変数、リターンアドレスなどがスタックにプッシュされ、関数終了時にはポップされます。FP (Frame Pointer) レジスタは、現在のスタックフレームのベースを指すことがありますが、最適化のために省略されることもあります。

スタック操作は、関数呼び出し規約(Calling Convention)の一部であり、関数間で引数を渡し、戻り値を返し、レジスタを保存・復元する方法を定義します。CGOでは、Goの呼び出し規約とCの呼び出し規約の間でレジスタやスタックの状態を変換する必要があり、この部分でアセンブリコードが重要な役割を果たします。

cgo_import_static プラグマ

#pragma cgo_import_static は、CGOで使用される特別なディレクティブです。これは、GoのランタイムがCコードから特定のシンボル(関数や変数)を静的にインポートすることを示します。これにより、GoのコードからCのシンボルを参照できるようになります。

技術的詳細

このコミットは、主に以下の3つのファイルに影響を与えています。

  1. src/pkg/runtime/cgo/asm_arm.s: ARMアーキテクチャ向けCGOのアセンブリコード。
  2. src/pkg/runtime/cgo/cgo_arm.c: ARMアーキテクチャ向けCGOのC言語コード。
  3. src/run.bash: Goのテスト実行スクリプト。

src/pkg/runtime/cgo/asm_arm.s の変更

このファイルは、ARMアーキテクチャにおけるCGOのクロスコール(GoからC、またはCからGoへの呼び出し)を処理するアセンブリコードを含んでいます。変更点は以下の1行です。

--- a/src/pkg/runtime/cgo/asm_arm.s
+++ b/src/pkg/runtime/cgo/asm_arm.s
@@ -19,5 +19,5 @@ TEXT crosscall2(SB),7,$-4
  	MOVW	_cgo_load_gm(SB), R0
  	BL	(R0)
  	MOVW	PC, R14
-	MOVW	-4(R13), PC
+	MOVW	0(R13), PC
  	MOVM.IAW	(R13), [R0, R1, R2, R4, R5, R6, R7, R8, R9, R10, R11, R12, PC]

変更された行は MOVW -4(R13), PC から MOVW 0(R13), PC です。

  • R13 はARMのスタックポインタ(SP)レジスタを指します。
  • PC はプログラムカウンタ(Program Counter)レジスタを指し、次に実行される命令のアドレスを保持します。
  • MOVW はワード(4バイト)を移動する命令です。
  • -4(R13) は、スタックポインタが指すアドレスから4バイト手前(スタックの成長方向によるが、通常はスタックにプッシュされた直前の値)のメモリ位置を意味します。
  • 0(R13) は、スタックポインタが指す現在のアドレスのメモリ位置を意味します。

この変更は、スタックポインタのオフセットの修正を示唆しています。GoのランタイムとCのランタイムの間でコンテキストを切り替える際、リターンアドレスやレジスタの状態がスタックに保存されます。以前のコードでは、スタックポインタから-4バイトのオフセットにある値をプログラムカウンタにロードしていましたが、これが誤っていた可能性があります。0(R13) に変更することで、スタックポインタが指す正確な位置にリターンアドレスが保存されていることを期待し、正しい実行フローに戻るように修正しています。これは、スタックフレームのレイアウトや、GoとCの呼び出し規約の間の整合性の問題に関連している可能性が高いです。

src/pkg/runtime/cgo/cgo_arm.c の追加

このファイルは新規追加されており、ARMアーキテクチャ向けのCGOランタイムのC言語部分を定義しています。

--- /dev/null
+++ b/src/pkg/runtime/cgo/cgo_arm.c
@@ -0,0 +1,12 @@
+// Copyright 2013 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.
+
+#pragma cgo_import_static x_cgo_load_gm
+extern void x_cgo_load_gm(void);\nvoid (*_cgo_load_gm)(void) = x_cgo_load_gm;
+
+#pragma cgo_import_static x_cgo_save_gm
+extern void x_cgo_save_gm(void);\nvoid (*_cgo_save_gm)(void) = x_cgo_save_gm;

このファイルは、x_cgo_load_gmx_cgo_save_gm という2つのシンボルを静的にインポートし、それらを関数ポインタ _cgo_load_gm_cgo_save_gm に割り当てています。

  • x_cgo_load_gmx_cgo_save_gm は、GoのランタイムがCGO呼び出しの際に使用する内部関数であると推測されます。
  • _cgo_load_gm は、GoのGoroutineのコンテキスト(特にGoroutineのスタックとレジスタの状態)をCのコンテキストに切り替える際に、GoのGoroutineのG(Goroutine構造体)ポインタをロードする役割を果たす可能性があります。
  • _cgo_save_gm は、CからGoに戻る際に、GoのGoroutineのGポインタを保存する役割を果たす可能性があります。

これらの関数は、GoのスケジューラがCコードの実行中にGoroutineの状態を管理するために不可欠です。ARMアーキテクチャ固有のCファイルとしてこれらが追加されたことは、ARM環境でのCGOのコンテキストスイッチングに特有の処理が必要であったことを示しています。

src/run.bash の変更

このスクリプトは、Goのテストスイートを実行するためのものです。変更点は以下の1行の削除です。

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

削除された行は [ "$GOARCH" == arm ] || # issue 4961 です。 これは、GoのアーキテクチャがARMである場合に、CGOのテスト(../misc/cgo/test ディレクトリ内のテスト)をスキップするという条件でした。この行が削除されたことにより、ARMアーキテクチャ上でもCGOのテストが実行されるようになります。これは、前述のアセンブリとCコードの修正によって、ARM上でのCGOが安定して動作するようになったため、テストを再有効化できるようになったことを意味します。

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

src/pkg/runtime/cgo/asm_arm.s

--- a/src/pkg/runtime/cgo/asm_arm.s
+++ b/src/pkg/runtime/cgo/asm_arm.s
@@ -19,5 +19,5 @@ TEXT crosscall2(SB),7,$-4
  	MOVW	_cgo_load_gm(SB), R0
  	BL	(R0)
  	MOVW	PC, R14
-	MOVW	-4(R13), PC
+	MOVW	0(R13), PC
  	MOVM.IAW	(R13), [R0, R1, R2, R4, R5, R6, R7, R8, R9, R10, R11, R12, PC]

src/pkg/runtime/cgo/cgo_arm.c

--- /dev/null
+++ b/src/pkg/runtime/cgo/cgo_arm.c
@@ -0,0 +1,12 @@
+// Copyright 2013 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.
+
+#pragma cgo_import_static x_cgo_load_gm
+extern void x_cgo_load_gm(void);\nvoid (*_cgo_load_gm)(void) = x_cgo_load_gm;
+
+#pragma cgo_import_static x_cgo_save_gm
+extern void x_cgo_save_gm(void);\nvoid (*_cgo_save_gm)(void) = x_cgo_save_gm;

src/run.bash

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

コアとなるコードの解説

src/pkg/runtime/cgo/asm_arm.s の修正

このアセンブリコードの修正は、GoのランタイムがC関数からGoのコードに制御を戻す際のリターンアドレスの処理に関するものです。MOVW -4(R13), PC から MOVW 0(R13), PC への変更は、スタックポインタ R13 を基準としたリターンアドレスのオフセットが誤っていたことを示しています。

GoとCの呼び出し規約の違いにより、スタックフレームのレイアウトが異なる場合があります。CGOのクロスコールでは、GoのスタックフレームからCのスタックフレームへ、またはその逆へと切り替える際に、スタックポインタの調整やレジスタの保存・復元が正確に行われる必要があります。

この修正は、ARMアーキテクチャにおける特定のCGO呼び出しパスにおいて、リターンアドレスがスタック上のどこに正確に配置されるべきかという問題に対処しています。-4(R13) は、スタックポインタが指すアドレスから4バイト手前の位置を意味しますが、これが誤ったアドレスを指していたため、Goのコードに正しく戻れず、クラッシュや不正な動作を引き起こしていた可能性があります。0(R13) に変更することで、スタックポインタが指す現在のアドレスにリターンアドレスが正しく配置されていることを期待し、Goの実行フローが正常に再開されるようにしています。

src/pkg/runtime/cgo/cgo_arm.c の追加

このCファイルは、ARMアーキテクチャに特化したCGOのランタイムサポートを提供します。特に、x_cgo_load_gmx_cgo_save_gm という2つの関数ポインタを定義し、それぞれを静的にインポートされた同名の関数に割り当てています。

これらの関数は、GoのGoroutineのコンテキスト(特にGポインタ、つまり現在のGoroutineの構造体へのポインタ)をCGO呼び出しの前後で管理するために使用されます。

  • _cgo_load_gm: GoからCへ呼び出す際に、現在のGoroutineのGポインタをロードする役割を担うと考えられます。これにより、Cコードが実行されている間もGoのランタイムがどのGoroutineがCGO呼び出しを行っているかを追跡できます。これは、GoのスケジューラがCコードの実行中にGoroutineをブロックし、必要に応じてOSスレッドを割り当てるために重要です。
  • _cgo_save_gm: CからGoへ戻る際に、現在のGoroutineのGポインタを保存する役割を担うと考えられます。これにより、GoのランタイムはCコードの実行が完了した後、正しいGoroutineのコンテキストに復元し、Goの実行を再開できます。

ARMアーキテクチャ固有のCファイルとしてこれらの関数が追加されたことは、ARMのレジスタ使用規約やスタックフレームの特性に合わせて、Gポインタのロード・保存方法を調整する必要があったことを示唆しています。これにより、ARM環境でのCGOのコンテキストスイッチングがより堅牢になります。

src/run.bash の修正

src/run.bash から [ "$GOARCH" == arm ] || # issue 4961 の行が削除されたことは、ARMアーキテクチャにおけるCGOのビルドと実行に関する問題が解決されたことを明確に示しています。

以前は、Issue 4961で報告された問題のために、ARM環境でのCGOテストが意図的にスキップされていました。これは、テストが失敗するか、CGO機能が正しく動作しないことが既知であったためです。

今回のコミットでアセンブリコードとCコードの修正が適用されたことにより、ARM上でのCGOが安定して動作するようになったため、このテストスキップの条件が不要になりました。これにより、GoのCI/CDパイプラインにおいて、ARMアーキテクチャ上でもCGO機能が継続的にテストされ、将来的な回帰バグが防止されるようになります。

関連リンク

参考にした情報源リンク

  • 提供されたコミットデータ (./commit_data/15546.txt)
  • Go言語のCGOに関する一般的な知識
  • ARMアーキテクチャのアセンブリ言語と呼び出し規約に関する一般的な知識
  • Go言語のランタイムとスケジューラに関する一般的な知識
  • (注: Issue #4961は公開されているGoのIssueトラッカーでは見つかりませんでした。これは非常に古いIssueであるか、内部的なトラッカーで管理されていた可能性があります。)