[インデックス 15386] ファイルの概要
このコミットは、Go言語のランタイムにおけるARMアーキテクチャ向けビルドの問題を修正するものです。具体的には、src/pkg/runtime/asm_arm.s
ファイル内のCgoコールバック処理に関連するアセンブリコードにおいて、MOVL
命令をMOVW
命令に置き換えることで、ARMビルドが正しく行われるように修正しています。
コミット
commit 56a0bafdb66c95f31de86d9a713fa819911d50a5
Author: Russ Cox <rsc@golang.org>
Date: Fri Feb 22 16:38:44 2013 -0500
runtime: fix arm build
R=ken2
CC=golang-dev
https://golang.org/cl/7399050
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/56a0bafdb66c95f31de86d9a713fa819911d50a5
元コミット内容
runtime: fix arm build
R=ken2
CC=golang-dev
https://golang.org/cl/7399050
変更の背景
このコミットの背景には、Go言語のランタイムがARMアーキテクチャ上で正しく動作しないという問題がありました。Goはクロスプラットフォーム対応を重視しており、様々なアーキテクチャで動作するように設計されています。しかし、特定のアセンブリ命令の使用方法がARMの仕様と合致していなかったため、ビルドエラーや実行時エラーが発生していたと考えられます。
特に、GoのランタイムはC言語で書かれたコード(Cgo)との連携をサポートしており、GoコードからCコードを呼び出したり、CコードからGoコードを呼び出したりする機能を提供しています。このCgoコールバックのメカニズムにおいて、ARMアセンブリコードが特定のレジスタにアドレスをロードする際に誤った命令を使用していたことが、問題の根本原因でした。
MOVL
命令は、ARMアセンブリにおいて32ビットの即値またはアドレスをレジスタにロードするために使用されますが、その使用には特定の制約があります。特に、大きなアドレス値を直接ロードする場合には、MOVW
とMOVT
の組み合わせを使用するなど、より柔軟な命令が必要となることがあります。このコミットは、runtime·cgocallback_gofunc
というGo関数のアドレスをロードする際に、MOVL
が適切でなかったために発生したビルド問題を解決することを目的としています。
前提知識の解説
Goランタイム (Go Runtime)
Goランタイムは、Goプログラムの実行を管理する低レベルのコンポーネントです。これには、ガベージコレクション、スケジューラ(ゴルーチンの管理)、メモリ管理、システムコールインターフェースなどが含まれます。Goプログラムは、コンパイル時にGoランタイムとリンクされ、実行可能ファイルを形成します。ランタイムの一部は、パフォーマンスや特定のハードウェアとのインタラクションのためにアセンブリ言語で書かれています。
ARMアーキテクチャ (ARM Architecture)
ARM(Advanced RISC Machine)は、モバイルデバイス、組み込みシステム、最近ではサーバーやデスクトップPCなど、幅広いデバイスで使用されているRISC(Reduced Instruction Set Computer)ベースのプロセッサアーキテクチャです。ARMプロセッサは、その電力効率と性能のバランスから広く採用されています。
アセンブリ言語 (Assembly Language)
アセンブリ言語は、特定のコンピュータアーキテクチャの命令セットに対応する低レベルのプログラミング言語です。人間が読めるニーモニック(例: MOVW
, BL
)を使用して、プロセッサが直接実行できる機械語命令を表現します。Goランタイムの一部は、パフォーマンス最適化やハードウェアとの直接的なインタラクションのためにアセンブリ言語で記述されています。
Goのアセンブリ構文
Goのアセンブリ言語は、Plan 9アセンブラの構文に基づいています。一般的なアセンブラとは異なる独自の記法を持っています。
TEXT
: 関数の開始を宣言します。例:TEXT runtime·cgocallback(SB),7,$12
runtime·cgocallback
: 関数名。Goのパッケージ名と関数名は·
で区切られます。(SB)
:SB
は「Static Base」レジスタを表し、グローバルシンボルや静的データへのオフセットを計算するための基準点として使用されます。7
: 関数フラグ。ここではスタックフレームのサイズやレジスタの使用に関する情報を示します。$12
: スタックフレームのサイズ(バイト単位)。この関数が使用するローカル変数のためのスタック領域を示します。
SB
(Static Base): グローバルシンボルや静的データのアドレスを指す擬似レジスタ。FP
(Frame Pointer): 現在の関数のスタックフレームの開始位置を指す擬似レジスタ。引数やローカル変数へのアクセスに使用されます。R0
,R13
など: ARMプロセッサの汎用レジスタ。R0
は通常、関数の戻り値や引数に使用されます。R13
は通常、スタックポインタ(SP)として使用されます。MOVW
(Move Word): ARMアセンブリ命令の一つで、32ビットの値をレジスタにロードします。即値またはメモリからのロードに使用されます。MOVL
(Move Long): ARMアセンブリ命令の一つで、32ビットの値をレジスタにロードします。MOVW
と似ていますが、特定のコンテキストや古いARMアーキテクチャでは異なる動作や制約を持つことがあります。特に、大きなアドレス値を直接ロードする際には、MOVW
とMOVT
の組み合わせが推奨されることがあります。BL
(Branch with Link): サブルーチンコールを行うための命令。指定されたアドレスにジャンプし、現在のプログラムカウンタ(PC)の次の命令のアドレスをリンクレジスタ(LR, 通常はR14)に保存します。これにより、サブルーチンから戻ることができます。RET
(Return): 関数から戻るための命令。
Cgoコールバック (Cgo Callback)
Cgoは、GoプログラムがC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのメカニズムです。Cgoコールバックは、CコードがGoの関数を呼び出す際に使用されるパスです。このプロセスには、GoとCのスタックフレームの切り替え、レジスタの保存と復元など、低レベルの処理が伴います。runtime·cgocallback
は、このコールバック処理のエントリポイントとなるGoランタイム内のアセンブリ関数です。
技術的詳細
このコミットの核心は、src/pkg/runtime/asm_arm.s
ファイル内の以下の変更です。
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -319,7 +319,7 @@ TEXT runtime·cgocallback(SB),7,$12
MOVW R0, 8(R13)
MOVW framesize+8(FP), R0
MOVW R0, 12(R13)
- MOVL $runtime·cgocallback_gofunc(SB), R0
+ MOVW $runtime·cgocallback_gofunc(SB), R0
BL (R0)
RET
変更された行は、runtime·cgocallback_gofunc
というGo関数のアドレスをレジスタR0
にロードする部分です。
- 変更前:
MOVL $runtime·cgocallback_gofunc(SB), R0
- 変更後:
MOVW $runtime·cgocallback_gofunc(SB), R0
ARMアーキテクチャにおいて、MOVL
は通常、32ビットの即値またはアドレスをレジスタにロードするために使用されます。しかし、ARMの命令セットには、ロードできる即値の範囲に制約があります。特に、大きなアドレス値(例えば、プログラムのメモリ空間のどこかにある関数のアドレス)を直接MOVL
でロードしようとすると、コンパイラやアセンブラがエラーを出すか、正しくないコードを生成する可能性があります。
一方、MOVW
命令は、ARMv7-A以降のThumb-2命令セットで導入された命令で、16ビットの即値をレジスタにロードし、必要に応じてMOVT
(Move Top)命令と組み合わせて32ビットの値をロードすることができます。しかし、Goのアセンブリでは、MOVW $symbol(SB), R0
のような形式は、アセンブラがシンボルのアドレスをR0
にロードするための適切な命令シーケンス(例えば、MOVW
とMOVT
の組み合わせ、またはLDR
命令など)を自動的に選択して生成することを意味します。
この修正は、runtime·cgocallback_gofunc
のアドレスがMOVL
命令の即値範囲を超えていたか、またはMOVL
が特定のARMビルド環境で問題を引き起こしていたことを示唆しています。MOVW
を使用することで、アセンブラがより柔軟かつ正確にアドレスをロードする命令を生成できるようになり、ARMビルドの問題が解決されました。これは、Goのクロスコンパイル環境や特定のARMプロセッサの命令セットの差異に対応するための重要な修正です。
コアとなるコードの変更箇所
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -319,7 +319,7 @@ TEXT runtime·cgocallback(SB),7,$12
MOVW R0, 8(R13)
MOVW framesize+8(FP), R0
MOVW R0, 12(R13)
- MOVL $runtime·cgocallback_gofunc(SB), R0
+ MOVW $runtime·cgocallback_gofunc(SB), R0
BL (R0)
RET
コアとなるコードの解説
変更された行は、TEXT runtime·cgocallback(SB),7,$12
関数内にあります。この関数は、Cgoコールバックの処理を担当するGoランタイムのアセンブリ関数です。
MOVW R0, 8(R13)
MOVW framesize+8(FP), R0
MOVW R0, 12(R13)
これらの命令は、Cgoコールバックに必要な引数やコンテキスト情報をレジスタやスタックに設定している部分です。
-
- MOVL $runtime·cgocallback_gofunc(SB), R0
- 変更前のコードでは、
runtime·cgocallback_gofunc
というGo関数のアドレスをR0
レジスタにロードするためにMOVL
命令を使用していました。$symbol(SB)
は、symbol
のアドレスを意味します。
- 変更前のコードでは、
-
+ MOVW $runtime·cgocallback_gofunc(SB), R0
- 変更後のコードでは、同じ目的で
MOVW
命令を使用しています。この変更により、アセンブラはruntime·cgocallback_gofunc
のアドレスをR0
にロードするためのより適切なARM命令シーケンスを生成できるようになりました。これにより、特定のアドレス範囲の問題や、特定のARMコンパイラ/アセンブラの挙動の違いが吸収され、ARMビルドが成功するようになりました。
- 変更後のコードでは、同じ目的で
-
BL (R0)
R0
にロードされたアドレス(runtime·cgocallback_gofunc
のアドレス)にジャンプし、サブルーチンコールを実行します。BL
命令は、呼び出し元の次の命令のアドレスをリンクレジスタ(LR)に保存するため、呼び出された関数から戻ることができます。
-
RET
- 関数から戻ります。
この修正は、GoランタイムがCgoコールバックを処理する際に、ARMアーキテクチャの特性をより正確に考慮し、安定したビルドと実行を保証するために不可欠でした。
関連リンク
- Go CL 7399050: https://golang.org/cl/7399050
参考にした情報源リンク
- Go Assembly Language
- ARM Architecture Reference Manual (一般的なARM命令セットの理解のため)
- Go runtime source code (Goランタイムの構造理解のため)
- Cgo documentation (Cgoの仕組み理解のため)
- Stack Overflow and other technical forums discussing ARM assembly and Go (特定の命令やGoアセンブリの挙動に関する情報収集のため)