[インデックス 14767] ファイルの概要
このコミットは、GoランタイムにおけるFreeBSD/ARMアーキテクチャでの命令キャッシュフラッシュの不具合を修正するものです。具体的には、sysarch
システムコールが引数をレジスタではなくスタックで渡すことを要求するというFreeBSD/ARMの特性に対応し、runtime·cacheflush
関数が正しく動作するように修正しています。
コミット
commit c2d2bfcc4908ca7ab94abf5479f28694b2d1efa8
Author: Dave Cheney <dave@cheney.net>
Date: Tue Jan 1 21:47:42 2013 +1100
runtime: fix freebsd/arm instruction cache flush
sysarch requires arguments to be passed on the stack, not in registers.
Credit to Shenghou Ma (minux) for the fix.
R=minux.ma, devon.odell
CC=golang-dev
https://golang.org/cl/7037043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c2d2bfcc4908ca7ab94abf5479f28694b2d1efa8
元コミット内容
runtime: fix freebsd/arm instruction cache flush
sysarch requires arguments to be passed on the stack, not in registers.
Credit to Shenghou Ma (minux) for the fix.
R=minux.ma, devon.odell
CC=golang-dev
https://golang.org/cl/7037043
変更の背景
このコミットの背景には、FreeBSDオペレーティングシステムがARMアーキテクチャ上で提供するsysarch
システムコールの引数渡し規約に関する問題がありました。Goランタイムのcacheflush
関数は、命令キャッシュをフラッシュするためにsysarch
システムコールを利用していました。しかし、FreeBSD/ARM環境では、sysarch
システムコールが引数をレジスタではなくスタック経由で受け取るという特定の規約を持っていました。
元の実装では、cacheflush
関数が引数(キャッシュフラッシュの開始アドレスと終了アドレス)をARMのレジスタ(R1とR2)にロードしてsysarch
を呼び出していました。このレジスタ渡しのアプローチは、FreeBSD/ARMのsysarch
の期待する引数渡し規約と一致せず、結果として命令キャッシュが正しくフラッシュされないというバグを引き起こしていました。命令キャッシュの不適切なフラッシュは、特にJITコンパイルされたコードや動的に生成されたコードを実行する際に、古い命令が実行されてしまうなどの深刻な問題につながる可能性があります。
この問題は、Shenghou Ma (minux) によって特定され、修正が提案されました。このコミットは、その修正をGoランタイムに適用するものです。
前提知識の解説
ARMアーキテクチャにおけるレジスタとスタック
ARMプロセッサは、汎用レジスタ(R0-R15など)と特殊レジスタ(プログラムカウンタPC、スタックポインタSPなど)を持ちます。関数呼び出し規約(Calling Convention)は、関数間で引数をどのように渡し、戻り値をどのように返すかを定義します。一般的なARMのABI(Application Binary Interface)では、少数の引数はレジスタ(通常R0-R3)で渡され、それ以上の引数や複雑な構造体などはスタックにプッシュして渡されます。
システムコール (System Call)
システムコールは、ユーザー空間のプログラムがオペレーティングシステム(カーネル)のサービスを要求するためのメカニズムです。ファイルI/O、メモリ管理、プロセス制御など、特権的な操作を行うために使用されます。システムコールを呼び出す際には、引数をカーネルに渡す必要がありますが、その渡し方はOSやアーキテクチャによって異なります。
sysarch
システムコール
sysarch
は、特定のアーキテクチャ固有の操作を行うための汎用システムコールです。FreeBSDでは、sysarch
は様々なアーキテクチャ固有の機能を提供するために使用されます。例えば、CPUのキャッシュ制御、FPU(浮動小数点演算ユニット)の状態管理、デバッグ関連の操作などが含まれます。sysarch
の具体的な動作は、渡されるコマンド引数によって決定されます。
命令キャッシュ (Instruction Cache)
現代のCPUは、メインメモリへのアクセス速度が遅いため、高速なキャッシュメモリを利用します。命令キャッシュは、CPUが次に実行する可能性のある命令のコピーを保持し、メモリからの命令フェッチを高速化します。
キャッシュフラッシュ (Cache Flush)
キャッシュフラッシュは、キャッシュメモリの内容を無効化したり、メインメモリに書き戻したりする操作です。命令キャッシュの場合、プログラムが実行中に新しいコードを動的に生成したり、既存のコードを書き換えたりした場合(例えば、JITコンパイラやデバッガがこれを行います)、キャッシュ内の古い命令が実行されないように、命令キャッシュをフラッシュして最新の命令をメモリから再ロードさせる必要があります。キャッシュがフラッシュされないと、CPUは古い(無効な)命令を実行し続け、プログラムの誤動作やクラッシュにつながる可能性があります。
技術的詳細
このコミットの技術的詳細の中心は、FreeBSD/ARMにおけるsysarch
システムコールの引数渡し規約への対応です。
ARMアーキテクチャでは、通常、関数呼び出しの最初の数個の引数はレジスタ(R0, R1, R2, R3)で渡されます。しかし、FreeBSDのsysarch
システムコールは、その実装において、引数をレジスタではなくスタックから読み取るように設計されていました。これは、システムコールハンドラが引数を処理する際に、特定のスタックフレームレイアウトを期待しているためと考えられます。
元のruntime·cacheflush
関数は、命令キャッシュフラッシュに必要な開始アドレスと終了アドレスをそれぞれR1とR2レジスタにロードし、その後SWI $165
(sysarch
システムコールを呼び出すためのソフトウェア割り込み命令)を実行していました。
TEXT runtime·cacheflush(SB),7,$0
MOVW $0, R0 // icacheflush
MOVW 0(FP), R1 // start
MOVW 4(FP), R2 // end
SUB R1, R2 // R2 = length
SWI $165 // sysarch
RET
このコードでは、sysarch
のコマンド(icacheflush
を示す0
)がR0に、開始アドレスがR1に、長さがR2に設定されています。しかし、FreeBSD/ARMのsysarch
はこれらの値をスタックから期待するため、レジスタに設定された値は無視され、結果としてキャッシュフラッシュが正しく行われませんでした。
このコミットによる修正は、以下のステップでこの問題を解決します。
- スタックフレームの拡張:
TEXT runtime·cacheflush(SB),7,$0
がTEXT runtime·cacheflush(SB),7,$8
に変更されています。これは、この関数のスタックフレームサイズを8バイト(2ワード)拡張することを意味します。この追加のスペースは、sysarch
に渡す引数をスタックに配置するために使用されます。 drain_writebuf
の追加:
この2行が追加されています。MOVW $1, R0 // drain_writebuf SWI $165 // sysarch
$1
をR0にロードしてsysarch
を呼び出すことで、drain_writebuf
(ライトバッファのドレイン)操作を実行しています。これは、命令キャッシュをフラッシュする前に、CPUのライトバッファに保留されている書き込み操作を完了させるために重要です。これにより、メモリ上のコードが最新の状態であることを保証し、キャッシュフラッシュがその最新の状態に対して行われるようにします。- 引数のスタックへの配置:
元のコードではMOVW 0(FP), R1 // start MOVW R1, 4(R13) // startをスタックにプッシュ (R13はスタックポインタ) MOVW 4(FP), R2 // end SUB R1, R2 // R2 = length MOVW R2, 8(R13) // lengthをスタックにプッシュ MOVW $4(R13), R1 // スタック上の引数へのポインタをR1に設定
start
とend
がレジスタR1とR2に直接ロードされていましたが、修正後では、これらの値はまず0(FP)
と4(FP)
から読み込まれ、その後4(R13)
と8(R13)
というスタック上のオフセットに書き込まれます。R13
は通常、スタックポインタ(SP)として使用されます。これにより、start
とlength
がスタックに配置されます。 さらに、MOVW $4(R13), R1
という命令で、スタックに配置された引数の先頭アドレス(4(R13)
)をR1レジスタにロードしています。これは、sysarch
システムコールが、引数へのポインタをレジスタで受け取ることを期待している場合があるためです。つまり、引数自体はスタックにあり、そのスタック上の場所へのポインタがレジスタで渡されるという二段階の渡し方をしている可能性があります。 sysarch
の再呼び出し: 最後に、SWI $165
が再度呼び出され、今度はスタックに正しく配置された引数と、R1に設定された引数へのポインタを使用して、命令キャッシュフラッシュが実行されます。
この修正により、GoランタイムはFreeBSD/ARM環境で命令キャッシュを確実にフラッシュできるようになり、動的に生成されるコードの正確な実行が保証されます。
コアとなるコードの変更箇所
変更はsrc/pkg/runtime/sys_freebsd_arm.s
ファイルに集中しています。
--- a/src/pkg/runtime/sys_freebsd_arm.s
+++ b/src/pkg/runtime/sys_freebsd_arm.s
@@ -244,11 +244,16 @@ TEXT runtime·sigprocmask(SB),7,$0
MOVW.CS R9, (R9)
RET
-TEXT runtime·cacheflush(SB),7,$0
+TEXT runtime·cacheflush(SB),7,$8
+\tMOVW $1, R0 // drain_writebuf
+\tSWI $165 // sysarch
MOVW $0, R0 // icacheflush
MOVW 0(FP), R1 // start
+\tMOVW R1, 4(R13)
MOVW 4(FP), R2 // end
SUB R1, R2 // R2 = length
+\tMOVW R2, 8(R13)
+\tMOVW $4(R13), R1
SWI $165 // sysarch
RET
コアとなるコードの解説
TEXT runtime·cacheflush(SB),7,$8
TEXT runtime·cacheflush(SB)
:runtime
パッケージのcacheflush
関数を定義しています。SB
はStatic Baseで、グローバルシンボルであることを示します。,7
: この関数の引数と戻り値の合計サイズが7バイトであることを示します。これはGoのアセンブリ規約の一部です。$8
: この関数のスタックフレームサイズが8バイトであることを示します。元の$0
から$8
に変更されたことで、関数内でローカル変数や引数をスタックに保存するための領域が確保されました。この8バイトは、start
とlength
の2つのワード(各4バイト)をスタックに格納するために使用されます。
MOVW $1, R0 // drain_writebuf
SWI $165 // sysarch
MOVW $1, R0
: 即値1
をレジスタR0
に移動します。R0
は通常、システムコールの第一引数またはコマンドコードとして使用されます。この1
は、sysarch
システムコールに対して「ライトバッファをドレインする」というコマンドを意味します。SWI $165
: ソフトウェア割り込み命令です。$165
はFreeBSDにおけるsysarch
システムコールの番号です。この命令により、カーネルのsysarch
ハンドラが呼び出され、ライトバッファのドレイン処理が実行されます。これは、命令キャッシュをフラッシュする前に、メモリ上のコードが完全に書き込まれていることを保証するために重要です。
MOVW $0, R0 // icacheflush
MOVW $0, R0
: 即値0
をレジスタR0
に移動します。これは、次に呼び出すsysarch
システムコールに対して「命令キャッシュをフラッシュする」というコマンドを意味します。
MOVW 0(FP), R1 // start
MOVW R1, 4(R13)
MOVW 0(FP), R1
: フレームポインタFP
からのオフセット0
にある値(cacheflush
関数の第一引数であるstart
アドレス)をレジスタR1
にロードします。MOVW R1, 4(R13)
: レジスタR1
の値(start
アドレス)を、スタックポインタR13
からのオフセット4
のメモリ位置にストアします。これにより、start
アドレスがスタックにプッシュされます。
MOVW 4(FP), R2 // end
SUB R1, R2 // R2 = length
MOVW R2, 8(R13)
MOVW 4(FP), R2
: フレームポインタFP
からのオフセット4
にある値(cacheflush
関数の第二引数であるend
アドレス)をレジスタR2
にロードします。SUB R1, R2
: レジスタR2
からレジスタR1
の値を減算し、結果をR2
に格納します。これにより、R2
にはキャッシュフラッシュの対象となる領域の長さ(end - start
)が計算されます。MOVW R2, 8(R13)
: レジスタR2
の値(計算された長さ)を、スタックポインタR13
からのオフセット8
のメモリ位置にストアします。これにより、length
がスタックにプッシュされます。
MOVW $4(R13), R1
MOVW $4(R13), R1
: スタックポインタR13
からのオフセット4
のメモリアドレス(つまり、スタックにプッシュされたstart
アドレスの場所)をレジスタR1
にロードします。これは、sysarch
システムコールが、引数自体ではなく、スタック上の引数へのポインタをレジスタで受け取ることを期待しているためです。
SWI $165 // sysarch
RET
SWI $165
: 再びsysarch
システムコールを呼び出します。今回は、R0
にはicacheflush
コマンド(0
)が、R1
にはスタック上の引数へのポインタが設定されており、sysarch
が期待する規約に従って引数が渡されます。RET
: 関数から戻ります。
この一連の変更により、FreeBSD/ARM環境でのsysarch
システムコールへの引数渡しが正しく行われるようになり、命令キャッシュフラッシュの機能が正常に動作するようになりました。
関連リンク
- https://golang.org/cl/7037043 - このコミットに対応するGoのコードレビュー(Change List)ページ。
参考にした情報源リンク
- FreeBSD sysarch(2) man page - FreeBSDの
sysarch
システムコールに関する公式ドキュメント。 - ARM Procedure Call Standard (AAPCS) - ARMアーキテクチャにおける関数呼び出し規約に関する詳細。
- Go Assembly Language - Goのアセンブリ言語に関する公式ドキュメント。
- Instruction Cache and Data Cache - ARMのキャッシュに関する一般的な情報。
- Understanding ARM Assembly Language - ARMアセンブリ言語の基本に関する記事。
- Go issue 4609: runtime: fix freebsd/arm instruction cache flush - この修正に関連するGoのIssue。
- Go commit 7037043: runtime: fix freebsd/arm instruction cache flush - Goの公式Gitリポジトリにおけるこのコミットのページ。# [インデックス 14767] ファイルの概要
このコミットは、GoランタイムにおけるFreeBSD/ARMアーキテクチャでの命令キャッシュフラッシュの不具合を修正するものです。具体的には、sysarch
システムコールが引数をレジスタではなくスタックで渡すことを要求するというFreeBSD/ARMの特性に対応し、runtime·cacheflush
関数が正しく動作するように修正しています。
コミット
commit c2d2bfcc4908ca7ab94abf5479f28694b2d1efa8
Author: Dave Cheney <dave@cheney.net>
Date: Tue Jan 1 21:47:42 2013 +1100
runtime: fix freebsd/arm instruction cache flush
sysarch requires arguments to be passed on the stack, not in registers.
Credit to Shenghou Ma (minux) for the fix.
R=minux.ma, devon.odell
CC=golang-dev
https://golang.org/cl/7037043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/c2d2bfcc4908ca7ab94abf5479f28694b2d1efa8
元コミット内容
runtime: fix freebsd/arm instruction cache flush
sysarch requires arguments to be passed on the stack, not in registers.
Credit to Shenghou Ma (minux) for the fix.
R=minux.ma, devon.odell
CC=golang-dev
https://golang.org/cl/7037043
変更の背景
このコミットの背景には、FreeBSDオペレーティングシステムがARMアーキテクチャ上で提供するsysarch
システムコールの引数渡し規約に関する問題がありました。Goランタイムのcacheflush
関数は、命令キャッシュをフラッシュするためにsysarch
システムコールを利用していました。しかし、FreeBSD/ARM環境では、sysarch
システムコールが引数をレジスタではなくスタック経由で受け取るという特定の規約を持っていました。
元の実装では、cacheflush
関数が引数(キャッシュフラッシュの開始アドレスと終了アドレス)をARMのレジスタ(R1とR2)にロードしてsysarch
を呼び出していました。このレジスタ渡しのアプローチは、FreeBSD/ARMのsysarch
の期待する引数渡し規約と一致せず、結果として命令キャッシュが正しくフラッシュされないというバグを引き起こしていました。命令キャッシュの不適切なフラッシュは、特にJITコンパイルされたコードや動的に生成されたコードを実行する際に、古い命令が実行されてしまうなどの深刻な問題につながる可能性があります。
この問題は、Shenghou Ma (minux) によって特定され、修正が提案されました。このコミットは、その修正をGoランタイムに適用するものです。
前提知識の解説
ARMアーキテクチャにおけるレジスタとスタック
ARMプロセッサは、汎用レジスタ(R0-R15など)と特殊レジスタ(プログラムカウンタPC、スタックポインタSPなど)を持ちます。関数呼び出し規約(Calling Convention)は、関数間で引数をどのように渡し、戻り値をどのように返すかを定義します。一般的なARMのABI(Application Binary Interface)では、少数の引数はレジスタ(通常R0-R3)で渡され、それ以上の引数や複雑な構造体などはスタックにプッシュして渡されます。
システムコール (System Call)
システムコールは、ユーザー空間のプログラムがオペレーティングシステム(カーネル)のサービスを要求するためのメカニズムです。ファイルI/O、メモリ管理、プロセス制御など、特権的な操作を行うために使用されます。システムコールを呼び出す際には、引数をカーネルに渡す必要がありますが、その渡し方はOSやアーキテクチャによって異なります。
sysarch
システムコール
sysarch
は、特定のアーキテクチャ固有の操作を行うための汎用システムコールです。FreeBSDでは、sysarch
は様々なアーキテクチャ固有の機能を提供するために使用されます。例えば、CPUのキャッシュ制御、FPU(浮動小数点演算ユニット)の状態管理、デバッグ関連の操作などが含まれます。sysarch
の具体的な動作は、渡されるコマンド引数によって決定されます。
命令キャッシュ (Instruction Cache)
現代のCPUは、メインメモリへのアクセス速度が遅いため、高速なキャッシュメモリを利用します。命令キャッシュは、CPUが次に実行する可能性のある命令のコピーを保持し、メモリからの命令フェッチを高速化します。
キャッシュフラッシュ (Cache Flush)
キャッシュフラッシュは、キャッシュメモリの内容を無効化したり、メインメモリに書き戻したりする操作です。命令キャッシュの場合、プログラムが実行中に新しいコードを動的に生成したり、既存のコードを書き換えたりした場合(例えば、JITコンパイラやデバッガがこれを行います)、キャッシュ内の古い命令が実行されないように、命令キャッシュをフラッシュして最新の命令をメモリから再ロードさせる必要があります。キャッシュがフラッシュされないと、CPUは古い(無効な)命令を実行し続け、プログラムの誤動作やクラッシュにつながる可能性があります。
技術的詳細
このコミットの技術的詳細の中心は、FreeBSD/ARMにおけるsysarch
システムコールの引数渡し規約への対応です。
ARMアーキテクチャでは、通常、関数呼び出しの最初の数個の引数はレジスタ(R0, R1, R2, R3)で渡されます。しかし、FreeBSDのsysarch
システムコールは、その実装において、引数をレジスタではなくスタックから読み取るように設計されていました。これは、システムコールハンドラが引数を処理する際に、特定のスタックフレームレイアウトを期待しているためと考えられます。
元のruntime·cacheflush
関数は、命令キャッシュフラッシュに必要な開始アドレスと終了アドレスをそれぞれR1とR2レジスタにロードし、その後SWI $165
(sysarch
システムコールを呼び出すためのソフトウェア割り込み命令)を実行していました。
TEXT runtime·cacheflush(SB),7,$0
MOVW $0, R0 // icacheflush
MOVW 0(FP), R1 // start
MOVW 4(FP), R2 // end
SUB R1, R2 // R2 = length
SWI $165 // sysarch
RET
このコードでは、sysarch
のコマンド(icacheflush
を示す0
)がR0に、開始アドレスがR1に、長さがR2に設定されています。しかし、FreeBSD/ARMのsysarch
はこれらの値をスタックから期待するため、レジスタに設定された値は無視され、結果としてキャッシュフラッシュが正しく行われませんでした。
このコミットによる修正は、以下のステップでこの問題を解決します。
- スタックフレームの拡張:
TEXT runtime·cacheflush(SB),7,$0
がTEXT runtime·cacheflush(SB),7,$8
に変更されています。これは、この関数のスタックフレームサイズを8バイト(2ワード)拡張することを意味します。この追加のスペースは、sysarch
に渡す引数をスタックに配置するために使用されます。 drain_writebuf
の追加:
この2行が追加されています。MOVW $1, R0 // drain_writebuf SWI $165 // sysarch
$1
をR0にロードしてsysarch
を呼び出すことで、drain_writebuf
(ライトバッファのドレイン)操作を実行しています。これは、命令キャッシュをフラッシュする前に、CPUのライトバッファに保留されている書き込み操作を完了させるために重要です。これにより、メモリ上のコードが最新の状態であることを保証し、キャッシュフラッシュがその最新の状態に対して行われるようにします。- 引数のスタックへの配置:
元のコードではMOVW 0(FP), R1 // start MOVW R1, 4(R13) // startをスタックにプッシュ (R13はスタックポインタ) MOVW 4(FP), R2 // end SUB R1, R2 // R2 = length MOVW R2, 8(R13) // lengthをスタックにプッシュ MOVW $4(R13), R1 // スタック上の引数へのポインタをR1に設定
start
とend
がレジスタR1とR2に直接ロードされていましたが、修正後では、これらの値はまず0(FP)
と4(FP)
から読み込まれ、その後4(R13)
と8(R13)
というスタック上のオフセットに書き込まれます。R13
は通常、スタックポインタ(SP)として使用されます。これにより、start
とlength
がスタックにプッシュされます。 さらに、MOVW $4(R13), R1
という命令で、スタックに配置された引数の先頭アドレス(4(R13)
)をR1レジスタにロードしています。これは、sysarch
システムコールが、引数へのポインタをレジスタで受け取ることを期待している場合があるためです。つまり、引数自体はスタックにあり、そのスタック上の場所へのポインタがレジスタで渡されるという二段階の渡し方をしている可能性があります。 sysarch
の再呼び出し: 最後に、SWI $165
が再度呼び出され、今度はスタックに正しく配置された引数と、R1に設定された引数へのポインタを使用して、命令キャッシュフラッシュが実行されます。
この修正により、GoランタイムはFreeBSD/ARM環境で命令キャッシュを確実にフラッシュできるようになり、動的に生成されるコードの正確な実行が保証されます。
コアとなるコードの変更箇所
変更はsrc/pkg/runtime/sys_freebsd_arm.s
ファイルに集中しています。
--- a/src/pkg/runtime/sys_freebsd_arm.s
+++ b/src/pkg/runtime/sys_freebsd_arm.s
@@ -244,11 +244,16 @@ TEXT runtime·sigprocmask(SB),7,$0
MOVW.CS R9, (R9)
RET
-TEXT runtime·cacheflush(SB),7,$0
+TEXT runtime·cacheflush(SB),7,$8
+\tMOVW $1, R0 // drain_writebuf
+\tSWI $165 // sysarch
MOVW $0, R0 // icacheflush
MOVW 0(FP), R1 // start
+\tMOVW R1, 4(R13)
MOVW 4(FP), R2 // end
SUB R1, R2 // R2 = length
+\tMOVW R2, 8(R13)
+\tMOVW $4(R13), R1
SWI $165 // sysarch
RET
コアとなるコードの解説
TEXT runtime·cacheflush(SB),7,$8
TEXT runtime·cacheflush(SB)
:runtime
パッケージのcacheflush
関数を定義しています。SB
はStatic Baseで、グローバルシンボルであることを示します。,7
: この関数の引数と戻り値の合計サイズが7バイトであることを示します。これはGoのアセンブリ規約の一部です。$8
: この関数のスタックフレームサイズが8バイトであることを示します。元の$0
から$8
に変更されたことで、関数内でローカル変数や引数をスタックに保存するための領域が確保されました。この8バイトは、start
とlength
の2つのワード(各4バイト)をスタックに格納するために使用されます。
MOVW $1, R0 // drain_writebuf
SWI $165 // sysarch
MOVW $1, R0
: 即値1
をレジスタR0
に移動します。R0
は通常、システムコールの第一引数またはコマンドコードとして使用されます。この1
は、sysarch
システムコールに対して「ライトバッファをドレインする」というコマンドを意味します。SWI $165
: ソフトウェア割り込み命令です。$165
はFreeBSDにおけるsysarch
システムコールの番号です。この命令により、カーネルのsysarch
ハンドラが呼び出され、ライトバッファのドレイン処理が実行されます。これは、命令キャッシュをフラッシュする前に、メモリ上のコードが完全に書き込まれていることを保証するために重要です。
MOVW $0, R0 // icacheflush
MOVW $0, R0
: 即値0
をレジスタR0
に移動します。これは、次に呼び出すsysarch
システムコールに対して「命令キャッシュをフラッシュする」というコマンドを意味します。
MOVW 0(FP), R1 // start
MOVW R1, 4(R13)
MOVW 0(FP), R1
: フレームポインタFP
からのオフセット0
にある値(cacheflush
関数の第一引数であるstart
アドレス)をレジスタR1
にロードします。MOVW R1, 4(R13)
: レジスタR1
の値(start
アドレス)を、スタックポインタR13
からのオフセット4
のメモリ位置にストアします。これにより、start
アドレスがスタックにプッシュされます。
MOVW 4(FP), R2 // end
SUB R1, R2 // R2 = length
MOVW R2, 8(R13)
MOVW 4(FP), R2
: フレームポインタFP
からのオフセット4
にある値(cacheflush
関数の第二引数であるend
アドレス)をレジスタR2
にロードします。SUB R1, R2
: レジスタR2
からレジスタR1
の値を減算し、結果をR2
に格納します。これにより、R2
にはキャッシュフラッシュの対象となる領域の長さ(end - start
)が計算されます。MOVW R2, 8(R13)
: レジスタR2
の値(計算された長さ)を、スタックポインタR13
からのオフセット8
のメモリ位置にストアします。これにより、length
がスタックにプッシュされます。
MOVW $4(R13), R1
MOVW $4(R13), R1
: スタックポインタR13
からのオフセット4
のメモリアドレス(つまり、スタックにプッシュされたstart
アドレスの場所)をレジスタR1
にロードします。これは、sysarch
システムコールが、引数自体ではなく、スタック上の引数へのポインタをレジスタで受け取ることを期待しているためです。
SWI $165 // sysarch
RET
SWI $165
: 再びsysarch
システムコールを呼び出します。今回は、R0
にはicacheflush
コマンド(0
)が、R1
にはスタック上の引数へのポインタが設定されており、sysarch
が期待する規約に従って引数が渡されます。RET
: 関数から戻ります。
この一連の変更により、FreeBSD/ARM環境でのsysarch
システムコールへの引数渡しが正しく行われるようになり、命令キャッシュフラッシュの機能が正常に動作するようになりました。
関連リンク
- https://golang.org/cl/7037043 - このコミットに対応するGoのコードレビュー(Change List)ページ。
参考にした情報源リンク
- FreeBSD sysarch(2) man page - FreeBSDの
sysarch
システムコールに関する公式ドキュメント。 - ARM Procedure Call Standard (AAPCS) - ARMアーキテクチャにおける関数呼び出し規約に関する詳細。
- Go Assembly Language - Goのアセンブリ言語に関する公式ドキュメント。
- Instruction Cache and Data Cache - ARMのキャッシュに関する一般的な情報。
- Understanding ARM Assembly Language - ARMアセンブリ言語の基本に関する記事。
- Go issue 4609: runtime: fix freebsd/arm instruction cache flush - この修正に関連するGoのIssue。
- Go commit 7037043: runtime: fix freebsd/arm instruction cache flush - Goの公式Gitリポジトリにおけるこのコミットのページ。