[インデックス 19370] ファイルの概要
このコミットは、Go言語のsyscall
パッケージ内のアセンブリコードにおけるスタックフレームサイズの修正を目的としています。具体的には、様々なオペレーティングシステム(GOOS)とアーキテクチャ(GOARCH)の組み合わせにおいて、システムコール関連のアセンブリ関数で定義されているスタックフレームのサイズが調整されています。これはgo vet
ツールによって検出された「些細な」間違いを修正するものであり、将来的なgo vet
の改善に向けた布石ともなっています。
コミット
commit 7ad60b72831e10373e765775b213c11a46af16bc
Author: Russ Cox <rsc@golang.org>
Date: Thu May 15 16:47:53 2014 -0400
syscall: fix stack frame sizes in assembly
for GOOS in darwin freebsd linux nacl netbsd openbsd plan9 solaris windows
do
for GOARCH in 386 amd64 amd64p32 arm
do
go vet
done
done
These are all real mistakes being corrected, but none
of them should be able to cause problems today
due to the NOSPLIT on the functions.
However, vet has also identified a few important problems.
I'm sending this CL to get rid of the trivial 'go vet' results
before attacking the real ones.
LGTM=r
R=golang-codereviews, r, bradfitz
CC=golang-codereviews
https://golang.org/cl/95460046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/7ad60b72831e10373e765775b213c11a46af16bc
元コミット内容
このコミットの元の内容は、syscall: fix stack frame sizes in assembly
です。これは、Goのsyscall
パッケージ内のアセンブリコードにおいて、スタックフレームのサイズが正しくない箇所を修正したことを示しています。コミットメッセージの本文では、go vet
ツールを様々なOSとアーキテクチャの組み合わせで実行し、その結果として検出された「些細な」間違いを修正したことが述べられています。これらの間違いは、関数にNOSPLIT
属性が付与されているため、現状では問題を引き起こす可能性は低いとされていますが、より重要な問題に取り組む前に、これらの簡単な修正を先行して適用する意図が示されています。
変更の背景
この変更の背景には、Go言語のコード品質と正確性を向上させるための継続的な取り組みがあります。特に、go vet
という静的解析ツールが重要な役割を果たしています。
go vet
による検出:go vet
は、Goプログラムにおける潜在的なバグや疑わしいコード構造を検出するために設計されたツールです。このコミットでは、go vet
がsyscall
パッケージ内のアセンブリコードにおけるスタックフレームサイズの不一致を検出しました。アセンブリコードは低レベルであり、スタックフレームの管理は非常に重要です。誤ったスタックフレームサイズは、スタックオーバーフロー、データ破損、セキュリティ脆弱性など、深刻な問題を引き起こす可能性があります。- 正確性の追求: Goランタイムは、OSとのインタラクションにおいてシステムコールを多用します。これらのシステムコールは、パフォーマンスと正確性を確保するために、多くの場合アセンブリ言語で実装されています。アセンブリコードの正確性は、Goプログラム全体の安定性と信頼性に直結します。
- 将来的な
go vet
の改善: コミットメッセージには、「go vet
はいくつかの重要な問題も特定した」とあり、このコミットは「些細なgo vet
の結果を取り除く」ためのものであると明記されています。これは、より複雑で重要な問題に取り組む前に、簡単な修正を先行して適用することで、go vet
の出力ノイズを減らし、今後の解析作業を効率化する意図があったことを示唆しています。また、アセンブリコード内のnn(SP)
参照をname+(nn-8)(FP)
形式に書き換えるというTODOコメントが追加されており、これはgo vet
がスタックフレーム参照をより正確にチェックできるようにするための将来的な改善計画を示しています。 NOSPLIT
の存在: コミットメッセージでは、これらの修正が「NOSPLIT
が関数に付与されているため、今日問題を引き起こすことはないはずだ」と述べられています。NOSPLIT
は、Goコンパイラに対して、その関数がスタックを拡張しないことを指示するアセンブリディレクティブです。これにより、スタックフレームサイズの不一致が即座にクラッシュなどの問題を引き起こす可能性が低減されますが、それでも潜在的なバグであることには変わりありません。
これらの背景から、このコミットは、Goランタイムの堅牢性を高め、静的解析ツールの有効性を向上させるための、体系的な品質保証プロセスの一環として行われたものと理解できます。
前提知識の解説
このコミットを理解するためには、以下の技術的な概念について理解しておく必要があります。
-
Go言語のアセンブリ:
- Go言語は、一部の低レベルな処理(特にシステムコールやランタイムのコア部分)にアセンブリ言語を使用します。Goのアセンブリは、AT&T構文に似ていますが、独自の擬似命令やレジスタの命名規則(例:
AX
,SP
,FP
,SB
)を持っています。 TEXT
ディレクティブ: Goのアセンブリ関数を定義するために使用されます。TEXT symbol(SB),flags,$framesize-argsize
の形式を取ります。symbol(SB)
: 関数のシンボル名。SB
は静的ベースポインタ(Static Base pointer)を表し、グローバルシンボルへの参照に使用されます。flags
: 関数の特性を示すフラグ(例:NOSPLIT
)。$framesize
: この関数が使用するスタックフレームのサイズ(ローカル変数やレジスタ退避領域など)。argsize
: この関数が引数として受け取るバイト数。
SP
(Stack Pointer): 現在のスタックトップを指すレジスタ。Goのアセンブリでは、nn(SP)
のようにオフセットを指定してスタック上のデータにアクセスします。FP
(Frame Pointer): 現在の関数のフレームの開始位置を指すレジスタ。name+nn(FP)
のように、引数や戻り値にアクセスするために使用されます。go vet
がスタックフレーム参照をチェックする際に、SP
ベースの参照よりもFP
ベースの参照の方がコンパイラにとって解析しやすいという背景があります。
- Go言語は、一部の低レベルな処理(特にシステムコールやランタイムのコア部分)にアセンブリ言語を使用します。Goのアセンブリは、AT&T構文に似ていますが、独自の擬似命令やレジスタの命名規則(例:
-
スタックフレーム:
- 関数が呼び出されるたびに、その関数専用のメモリ領域がスタック上に確保されます。これをスタックフレームと呼びます。
- スタックフレームには、関数の引数、ローカル変数、呼び出し元のリターンアドレス、レジスタの退避領域などが格納されます。
$framesize
は、このスタックフレームの合計サイズをバイト単位で指定します。このサイズが正確でないと、スタックの破損や、他の関数のスタック領域への不正なアクセスが発生する可能性があります。
-
go vet
ツール:go vet
は、Go言語のソースコードを静的に解析し、疑わしい構造や潜在的なエラーを報告するツールです。コンパイルエラーにはならないが、実行時に問題を引き起こす可能性のあるコードパターンを検出します。- このコミットの文脈では、
go vet
がアセンブリコードにおけるスタックフレームサイズの不一致を検出する能力を持っていることが示されています。
-
NOSPLIT
フラグ:- Goのアセンブリ関数に付与されるフラグの一つです。
- このフラグが付いている関数は、実行中にスタックを拡張しない(スタックの分割を行わない)ことをコンパイラに保証します。
- 通常、Goの関数は必要に応じてスタックを自動的に拡張しますが、
NOSPLIT
が付いている関数は、呼び出し時に十分なスタック領域が確保されていることを前提とします。これは、システムコールのように非常に短い時間で実行され、スタック拡張のオーバーヘッドを避けたい場合に利用されます。 NOSPLIT
が付いているため、スタックフレームサイズのわずかな不一致が直ちにクラッシュを引き起こす可能性は低いとコミットメッセージで述べられています。これは、関数がスタックを拡張しようとしないため、誤ったサイズが指定されていても、その関数が必要とする最小限のスタック領域が常に確保されている限り、問題が顕在化しにくいことを意味します。
-
syscall
パッケージ:- Go言語の標準ライブラリの一つで、オペレーティングシステムの低レベルな機能(システムコール)へのアクセスを提供します。
- ファイル操作、ネットワーク通信、プロセス管理など、OSカーネルが提供する様々な機能を利用するために使用されます。
- OS固有のシステムコールは、Goのポータビリティを維持しつつ、各OSのネイティブな機能にアクセスするために、多くの場合アセンブリ言語で実装されています。
これらの概念を理解することで、このコミットがGoランタイムの安定性と品質向上にどのように貢献しているかを深く把握することができます。
技術的詳細
このコミットの技術的詳細は、主にGoのアセンブリ言語におけるスタックフレームの管理と、go vet
による静的解析の連携にあります。
-
スタックフレームサイズの修正:
- Goのアセンブリ関数は、
TEXT symbol(SB),flags,$framesize-argsize
という形式で定義されます。ここで$framesize
は、その関数が使用するスタックフレームの合計サイズ(ローカル変数、レジスタ退避領域、呼び出す他の関数の引数領域など)をバイト単位で指定します。 - このコミットでは、多くの
syscall
関連のアセンブリ関数(例:Syscall
,Syscall6
,RawSyscall
など)において、この$framesize
の値が減少しています。具体的には、多くのケースで4バイトまたは8バイト減少しています。- 例:
TEXT ·Syscall(SB),NOSPLIT,$0-32
がTEXT ·Syscall(SB),NOSPLIT,$0-28
に変更(4バイト減少)。 - 例:
TEXT ·Syscall(SB),NOSPLIT,$0-64
がTEXT ·Syscall(SB),NOSPLIT,$0-56
に変更(8バイト減少)。
- 例:
- この減少は、おそらく以前の
$framesize
が過剰に確保されていたか、またはGoコンパイラやリンカの最適化、あるいはアセンブリコードの変更によって、実際に必要なスタック領域が減少したためと考えられます。go vet
がこの不一致を検出したことで、より正確なサイズに修正されました。 - スタックフレームサイズが過剰に確保されていても、通常はメモリの無駄遣いにとどまりますが、厳密なスタック管理が求められる低レベルコードでは、このような不正確さも修正の対象となります。
- Goのアセンブリ関数は、
-
go vet
の役割:go vet
は、Goのソースコードだけでなく、アセンブリコードに対しても静的解析を実行します。このコミットは、go vet
がアセンブリ関数におけるスタックフレームサイズの宣言と実際の使用状況との不一致を検出できることを示しています。- これは、Goツールチェインが低レベルなアセンブリコードの正確性も検証しようとしていることの表れです。
-
NOSPLIT
フラグの影響:- 修正されたほとんどの関数には
NOSPLIT
フラグが付与されています。これは、これらの関数が実行中にスタックを拡張しないことを意味します。 - コミットメッセージにあるように、
NOSPLIT
が付いているため、スタックフレームサイズの不一致が直ちにクラッシュなどの問題を引き起こす可能性は低いとされています。これは、関数がスタックを拡張しようとしないため、誤ったサイズが指定されていても、その関数が必要とする最小限のスタック領域が常に確保されている限り、問題が顕在化しにくいことを意味します。しかし、これはあくまで「問題が顕在化しにくい」だけであり、潜在的なバグであることには変わりありません。
- 修正されたほとんどの関数には
-
errno
からerr
への変更(NaClアーキテクチャ):src/pkg/syscall/asm_nacl_386.s
とsrc/pkg/syscall/asm_nacl_amd64p32.s
において、スタック上の変数参照がerrno+24(FP)
からerr+24(FP)
に変更されています。- これは、Goの慣習的な命名規則(エラー変数は
err
)に合わせるか、またはgo vet
が検出した別の警告(例えば、未定義のシンボル参照や型不一致)を修正するためのものと考えられます。NaCl(Native Client)はGoogle Chromeブラウザ内でネイティブコードを実行するためのサンドボックス技術であり、特定のプラットフォームに特化したアセンブリコードが存在します。
-
将来の
go vet
改善に向けたTODOコメント:- 多くの修正されたアセンブリファイルに、以下のTODOコメントが追加されています。
// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP) // so that go vet can check that they are correct.
- これは、現在の
go vet
がnn(SP)
形式のスタック参照を完全に正確に解析できない可能性があることを示唆しています。name+(nn-8)(FP)
形式は、フレームポインタ(FP
)を基準とした参照であり、引数や戻り値へのアクセスをより明示的にします。この形式に統一することで、go vet
がスタックフレームのレイアウトと参照の正確性をより厳密に検証できるようになるという、将来的なツール改善の方向性を示しています。
- 多くの修正されたアセンブリファイルに、以下のTODOコメントが追加されています。
これらの技術的詳細は、Goランタイムの低レベルな実装における厳密なメモリ管理と、静的解析ツールを用いた継続的な品質保証の重要性を浮き彫りにしています。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は、主にsrc/pkg/syscall/
ディレクトリ内の様々なOSおよびアーキテクチャ向けのアセンブリファイル(.s
拡張子)におけるTEXT
ディレクティブのスタックフレームサイズ($framesize
)の変更です。
以下に、代表的な変更パターンを示します。
-
スタックフレームサイズの減少:
-
多くの
TEXT
ディレクティブにおいて、$framesize
の値が減少しています。 -
例:
src/pkg/syscall/asm_darwin_386.s
-TEXT ·Syscall(SB),NOSPLIT,$0-32 +TEXT ·Syscall(SB),NOSPLIT,$0-28
Syscall
関数のスタックフレームサイズが32バイトから28バイトに減少しています。同様に、Syscall6
,Syscall9
,RawSyscall
,RawSyscall6
などの関数でも同様の変更が見られます。 -
例:
src/pkg/syscall/asm_darwin_amd64.s
-TEXT ·Syscall(SB),NOSPLIT,$0-64 +TEXT ·Syscall(SB),NOSPLIT,$0-56
Syscall
関数のスタックフレームサイズが64バイトから56バイトに減少しています。 -
このパターンは、
darwin
,freebsd
,linux
,netbsd
,openbsd
,plan9
などのOSと、386
,amd64
,amd64p32
などのアーキテクチャの組み合わせで多数見られます。
-
-
変数名の修正(NaClアーキテクチャ):
src/pkg/syscall/asm_nacl_386.s
とsrc/pkg/syscall/asm_nacl_amd64p32.s
では、スタック上のエラー変数の参照が修正されています。- 例:
src/pkg/syscall/asm_nacl_386.s
- MOVL AX, errno+24(FP) + MOVL AX, err+24(FP)
errno
という名前の参照がerr
に変更されています。
-
TODOコメントの追加:
- 多くの変更されたアセンブリファイルの先頭に、将来的な
go vet
の改善に向けたTODOコメントが追加されています。 - 例:
src/pkg/syscall/asm_darwin_386.s
+// TODO(rsc): Rewrite all nn(SP) references into name+(nn-8)(FP) +// so that go vet can check that they are correct.
- 多くの変更されたアセンブリファイルの先頭に、将来的な
これらの変更は、Goランタイムの低レベルな部分における正確性と保守性を向上させるためのものです。
コアとなるコードの解説
このコミットのコアとなるコードの解説は、Goのアセンブリ言語におけるTEXT
ディレクティブの$framesize
パラメータの役割と、それがどのように修正されたか、そしてその修正がなぜ重要であるかに焦点を当てます。
Goのアセンブリ関数は、以下のような形式で定義されます。
TEXT Symbol(SB),Flags,$framesize-argsize
Symbol(SB)
: 関数の名前。SB
は静的ベースポインタで、グローバルシンボルを示します。Flags
: 関数の動作を制御するフラグ。このコミットで重要なのはNOSPLIT
です。$framesize
: この関数が実行中に使用するスタックフレームの合計サイズをバイト単位で指定します。これには、ローカル変数、レジスタの退避領域、そしてこの関数が呼び出す可能性のある他の関数の引数領域などが含まれます。argsize
: この関数が呼び出し元から受け取る引数の合計サイズをバイト単位で指定します。
変更の核心:
このコミットの主要な変更は、多くのsyscall
関連のアセンブリ関数において、この$framesize
の値が減少したことです。例えば、$0-32
が$0-28
に、$0-64
が$0-56
に変更されています。
なぜこの修正が必要だったのか?:
go vet
による検出:go vet
ツールは、アセンブリコードを解析し、宣言された$framesize
と、実際にコード内で使用されているスタック領域の量との間に不一致があることを検出しました。これは、アセンブリコードが手書きであるため、人間が誤って過剰なスタックサイズを宣言してしまっていた可能性を示唆しています。- 正確性の向上: スタックフレームサイズが過剰に宣言されていても、直接的なクラッシュには繋がりにくい場合があります(特に
NOSPLIT
が付いている場合)。しかし、これはメモリの無駄遣いであり、低レベルなランタイムコードにおいては、可能な限り正確なリソース管理が求められます。この修正は、Goランタイムのメモリフットプリントをわずかに最適化し、より正確なスタック管理を実現します。 - 将来的な解析の準備: コミットメッセージと追加されたTODOコメントが示唆するように、この修正は、
go vet
がアセンブリコードのスタック参照をより厳密にチェックできるようにするための準備段階でもあります。nn(SP)
形式の参照をname+(nn-8)(FP)
形式に統一することで、go vet
がスタックフレームのレイアウトをより正確に推論し、潜在的なバグをさらに検出できるようになることが期待されます。
NOSPLIT
の影響:
修正された関数にはNOSPLIT
フラグが付与されています。これは、これらの関数が実行中にスタックを自動的に拡張しないことを意味します。そのため、宣言された$framesize
が実際の必要量よりも大きくても、スタックオーバーフローなどの問題がすぐに発生するわけではありませんでした。しかし、これはあくまで「問題が顕在化しにくい」だけであり、宣言と実態の不一致は潜在的なバグとして修正されるべきものです。
NaClアーキテクチャでの変数名修正:
src/pkg/syscall/asm_nacl_386.s
とsrc/pkg/syscall/asm_nacl_amd64p32.s
におけるerrno
からerr
への変更は、Goの慣習的な命名規則への準拠、またはgo vet
が検出した別の警告(例えば、シンボル解決に関するもの)の修正と考えられます。これは、コードの一貫性と可読性を高めるためのクリーンアップ作業の一部です。
このコミットは、Goランタイムの基盤となるアセンブリコードの正確性を高め、静的解析ツールを活用してコード品質を継続的に改善していくGo開発チームの姿勢を反映しています。
関連リンク
- Go言語の公式ドキュメント: https://golang.org/doc/
- Goのアセンブリについて(Goのソースコード内のドキュメント):
src/cmd/asm/doc.go
やsrc/cmd/compile/internal/gc/builtin.go
など go vet
コマンドのドキュメント: https://pkg.go.dev/cmd/vet- Goのシステムコールパッケージ(
syscall
): https://pkg.go.dev/syscall - GoのChange List (CL) 95460046: https://golang.org/cl/95460046
参考にした情報源リンク
- Go言語のソースコード(特に
src/pkg/syscall
ディレクトリ内のアセンブリファイル) - Go言語の公式ドキュメントおよび
go vet
のドキュメント - Goのアセンブリに関する一般的な情報源(例: Go Assembly by Example, Go's low-level calling conventions on x86-64)
- コミットメッセージと関連するGo CLの議論
- Goの
TEXT
ディレクティブとスタックフレームに関する情報 (例: https://go.dev/doc/asm) - Goの
NOSPLIT
に関する情報 (例: https://go.dev/src/runtime/asm_amd64.s 内のコメント) - Goの
go vet
に関する情報 (例: https://go.dev/blog/go-vet)