[インデックス 15525] ファイルの概要
このコミットは、Go言語のランタイムにおけるCgo(C言語との連携機能)のARMアーキテクチャ向けビルドに関する修正です。具体的には、グローバルな検索置換作業中に、大文字の.S
拡張子を持つアセンブリファイルが対象から漏れていたために発生したビルドエラーを修正しています。
コミット
commit 83c5d07c0e940b0ff3f9da23920f047c4dd6e6fe
Author: Russ Cox <rsc@golang.org>
Date: Fri Mar 1 00:30:19 2013 -0500
runtime/cgo: fix arm build
During my global search and replace I forgot to open *.S (capital).
R=golang-dev
TBR=golang-dev
CC=golang-dev
https://golang.org/cl/7415047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/83c5d07c0e940b0ff3f9da23920f047c4dd6e6fe
元コミット内容
このコミットの目的は、GoランタイムのCgo部分におけるARMアーキテクチャ向けのビルド問題を修正することです。コミットメッセージによると、以前行われたグローバルな検索置換操作において、大文字の.S
拡張子を持つアセンブリファイル(src/pkg/runtime/cgo/gcc_arm.S
)が処理対象から漏れてしまったことが原因で、ビルドが失敗するようになっていました。このコミットでは、その漏れを修正し、適切な関数名に置換することで、ARMビルドが再び正常に行われるようにしています。
変更の背景
Go言語のランタイムは、様々なアーキテクチャやオペレーティングシステムに対応するために、多くのプラットフォーム固有のコードを含んでいます。Cgoは、GoプログラムからC言語の関数を呼び出すためのメカニズムであり、その実装には低レベルのアセンブリコードが関与することがあります。
このコミットが行われた2013年3月1日という時期は、Go言語がまだ活発に開発され、様々なアーキテクチャへの対応が強化されていた時期にあたります。コミットメッセージにある「global search and replace(グローバルな検索置換)」は、おそらくGoランタイム内の特定のAPIや内部関数の命名規則の変更、あるいはリファクタリングの一環として行われたものと推測されます。
この検索置換作業において、ファイル名のパターンマッチングが不完全であったため、小文字の.s
拡張子を持つファイルは適切に処理されたものの、大文字の.S
拡張子を持つファイル(特にgcc_arm.S
)が見落とされてしまいました。その結果、gcc_arm.S
ファイル内の古い関数名が残ってしまい、ビルド時に未定義シンボルエラーなどが発生し、ARMアーキテクチャ向けのビルドが失敗するようになったと考えられます。
このコミットは、この見落としを修正し、GoランタイムのCgo部分がARM環境で正しく動作するようにするためのものです。
前提知識の解説
このコミットを理解するためには、以下の前提知識が必要です。
-
Goランタイム (Go Runtime): Goプログラムは、Goランタイムと呼ばれる軽量な実行環境上で動作します。ランタイムは、ガベージコレクション、スケジューリング(goroutineの管理)、メモリ管理、Cgoとの連携など、Goプログラムの実行に必要な低レベルな機能を提供します。
-
Cgo (C Foreign Function Interface): Cgoは、GoプログラムからC言語の関数を呼び出したり、C言語のコードからGoの関数を呼び出したりするためのGo言語の機能です。これにより、既存のCライブラリをGoから利用したり、パフォーマンスが重要な部分をCで記述したりすることが可能になります。Cgoの内部では、GoとCのスタックフレームの切り替えや、スレッドローカルストレージ(TLS)を介したGoランタイムの状態(
g
とm
)の保存・復元など、低レベルな処理が行われます。 -
ARMアーキテクチャ: ARM(Advanced RISC Machine)は、モバイルデバイスや組み込みシステムで広く使用されているRISC(Reduced Instruction Set Computer)ベースのCPUアーキテクチャです。Go言語はARMアーキテクチャもサポートしており、そのためにARM固有のアセンブリコードがランタイムに含まれています。
-
アセンブリ言語 (.Sファイル):
.S
または.s
拡張子を持つファイルは、アセンブリ言語のソースコードファイルです。Goランタイムのパフォーマンスが重要な部分や、OS/アーキテクチャ固有の低レベルな処理(例: コンテキストスイッチ、システムコール、Cgoのスタック切り替え)は、アセンブリ言語で記述されることがあります。これらのファイルは、Goのツールチェーンによってコンパイルされ、最終的なバイナリに組み込まれます。大文字の.S
は、通常、Cプリプロセッサによって処理されるアセンブリファイルを指すことが多いですが、この文脈では単にファイル拡張子のケースの違いが問題となっています。 -
Goランタイムの
g
とm
: Goランタイムには、g
(goroutine)とm
(machine/thread)という重要な概念があります。g
: Go言語の並行処理の単位であるgoroutineを表す構造体です。各goroutineは独自のスタックを持ちます。m
: オペレーティングシステムのスレッド(OSスレッド)を表す構造体です。Goランタイムは、複数のg
を少数のm
上でスケジューリングして実行します。 Cgo呼び出しが行われる際、Goランタイムは現在のg
とm
の情報を保存し、Cコードの実行中はOSスレッドがCのスタックで動作するように切り替えます。Cコードの実行が完了し、Goコードに戻る際には、保存されたg
とm
の情報を復元する必要があります。
-
TLS (Thread Local Storage): スレッドローカルストレージ(TLS)は、各スレッドがそれぞれ独立したデータを保持できるメモリ領域です。Cgoの文脈では、Goランタイムは現在の
g
とm
のポインタをTLSに保存することがあります。これにより、CコードからGoコードにコールバックする際や、Cgo呼び出しの前後で、正しいg
とm
のコンテキストを復元することが可能になります。 -
ARMアセンブリ命令
bl
:bl
は"Branch with Link"の略で、ARMアセンブリにおける関数呼び出し命令です。指定されたアドレスにジャンプし、同時に現在のプログラムカウンタ(PC)の次の命令のアドレスをリンクレジスタ(LR)に保存します。これにより、関数呼び出し後に元の場所に戻ることができます。
技術的詳細
このコミットは、src/pkg/runtime/cgo/gcc_arm.S
ファイル内の2つのアセンブリ命令の変更に焦点を当てています。
変更前:
bl EXT(cgo_tls_set_gm) // save current g and m into TLS variable
変更後:
bl EXT(x_cgo_save_gm) // save current g and m into TLS variable
変更前:
bl EXT(cgo_tls_get_gm) // set up g and m from TLS
変更後:
bl EXT(x_cgo_load_gm) // set up g and m from TLS
この変更は、cgo_tls_set_gm
とcgo_tls_get_gm
という関数名が、それぞれx_cgo_save_gm
とx_cgo_load_gm
にリネームされたことを示しています。
-
cgo_tls_set_gm
->x_cgo_save_gm
: この関数は、現在のgoroutine (g
) とOSスレッド (m
) の情報をスレッドローカルストレージ(TLS)に保存する役割を担っていました。Cgo呼び出しの直前、GoランタイムのコンテキストをCコードが破壊しないように、この情報が保存されます。新しい名前のx_cgo_save_gm
は、その役割をより明確に示しています。x_
プレフィックスは、Goランタイムの内部的な、特にクロスコンパイルやCgoのような特殊なコンテキストで使用される関数であることを示唆している可能性があります。 -
cgo_tls_get_gm
->x_cgo_load_gm
: この関数は、TLSに保存されたg
とm
の情報を読み込み、Goランタイムのコンテキストを復元する役割を担っていました。Cコードの実行が終わり、Goコードに戻る際に、この関数が呼び出されます。新しい名前のx_cgo_load_gm
も、その役割をより明確にしています。
これらの関数は、CgoがGoとCの間でスタックを切り替え、Goランタイムの重要な状態(g
とm
)を維持するために不可欠な低レベルのルーチンです。グローバルな検索置換作業でこれらの関数名が変更されたにもかかわらず、gcc_arm.S
ファイルがその対象から漏れたため、古い関数名が残ってしまい、ビルドエラーを引き起こしていました。このコミットは、その不整合を解消するものです。
コアとなるコードの変更箇所
diff --git a/src/pkg/runtime/cgo/gcc_arm.S b/src/pkg/runtime/cgo/gcc_arm.S
index fc6d34cae9..d788d42488 100644
--- a/src/pkg/runtime/cgo/gcc_arm.S
+++ b/src/pkg/runtime/cgo/gcc_arm.S
@@ -24,7 +24,7 @@ EXT(crosscall_arm2):
mov r10, r1 // g
mov r9, r2 // m
mov r3, r0 // save r0, cgo_tls_set_gm will clobber it
- bl EXT(cgo_tls_set_gm) // save current g and m into TLS variable
+ bl EXT(x_cgo_save_gm) // save current g and m into TLS variable
mov lr, pc
mov pc, r3
pop {r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}
@@ -45,7 +45,7 @@ EXT(crosscall2):
* nevertheless.
*/
push {r0, r1, r2, r4, r5, r6, r7, r8, r9, r10, r11, ip, lr}
- bl EXT(cgo_tls_get_gm) // set up g and m from TLS
+ bl EXT(x_cgo_load_gm) // set up g and m from TLS
mov lr, pc
ldr pc, [sp, #0]
pop {r0, r1, r2, r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}
コアとなるコードの解説
この変更は、src/pkg/runtime/cgo/gcc_arm.S
ファイル内の2つの異なるアセンブリ関数(crosscall_arm2
とcrosscall2
)内で発生しています。これらの関数は、Cgo呼び出しの際にGoとCのコンテキストを切り替えるためのアセンブリルーチンの一部です。
-
crosscall_arm2
関数内:mov r10, r1 // g
とmov r9, r2 // m
: これは、現在のgoroutine (g
) とOSスレッド (m
) のポインタをレジスタr10
とr9
にそれぞれ移動していることを示しています。mov r3, r0 // save r0, cgo_tls_set_gm will clobber it
: レジスタr0
の値をr3
に一時的に保存しています。これは、次に呼び出す関数cgo_tls_set_gm
(またはx_cgo_save_gm
)がr0
を上書きする可能性があるためです。- bl EXT(cgo_tls_set_gm) // save current g and m into TLS variable
+ bl EXT(x_cgo_save_gm) // save current g and m into TLS variable
ここが変更点です。bl
命令は関数呼び出しを行います。EXT()
マクロは、外部シンボルへの参照を解決するために使用されます。この行は、現在のg
とm
の情報をTLSに保存するための関数を呼び出しています。古い関数名cgo_tls_set_gm
が、新しい関数名x_cgo_save_gm
に修正されました。
-
crosscall2
関数内:push {r0, r1, r2, r4, r5, r6, r7, r8, r9, r10, r11, ip, lr}
: 複数のレジスタをスタックにプッシュして保存しています。これは、関数呼び出しの規約に従い、呼び出し元が使用するレジスタの値を保護するためです。- bl EXT(cgo_tls_get_gm) // set up g and m from TLS
+ bl EXT(x_cgo_load_gm) // set up g and m from TLS
ここも変更点です。この行は、TLSからg
とm
の情報を読み込み、Goランタイムのコンテキストを復元するための関数を呼び出しています。古い関数名cgo_tls_get_gm
が、新しい関数名x_cgo_load_gm
に修正されました。mov lr, pc
とldr pc, [sp, #0]
: これは、スタックに保存された戻りアドレスをロードし、関数から戻るための一般的なアセンブリパターンです。pop {r0, r1, r2, r4, r5, r6, r7, r8, r9, r10, r11, ip, pc}
: 保存したレジスタをスタックからポップして復元しています。
これらの変更は、Goランタイムの内部的な関数名のリファクタリングに追従するためのものであり、CgoがARMアーキテクチャ上で正しく動作するために不可欠な修正です。アセンブリコードレベルでの関数名の不一致は、リンカエラーや実行時エラーを引き起こす可能性があります。
関連リンク
- Go CL 7415047: https://golang.org/cl/7415047
参考にした情報源リンク
- Go言語のCgoに関する公式ドキュメント: https://go.dev/blog/c-go-is-not-c
- Goランタイムのスケジューラに関する解説(
g
とm
について): https://go.dev/doc/articles/go_scheduler.html - ARMアセンブリの基本(
bl
命令など): https://developer.arm.com/documentation/dui0489/c/arm-and-thumb-instructions/branch-instructions - スレッドローカルストレージ (TLS) の概念: https://ja.wikipedia.org/wiki/%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%83%AD%E3%83%BC%E3%82%AB%E3%83%AB%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8