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

[インデックス 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という静的解析ツールが重要な役割を果たしています。

  1. go vetによる検出: go vetは、Goプログラムにおける潜在的なバグや疑わしいコード構造を検出するために設計されたツールです。このコミットでは、go vetsyscallパッケージ内のアセンブリコードにおけるスタックフレームサイズの不一致を検出しました。アセンブリコードは低レベルであり、スタックフレームの管理は非常に重要です。誤ったスタックフレームサイズは、スタックオーバーフロー、データ破損、セキュリティ脆弱性など、深刻な問題を引き起こす可能性があります。
  2. 正確性の追求: Goランタイムは、OSとのインタラクションにおいてシステムコールを多用します。これらのシステムコールは、パフォーマンスと正確性を確保するために、多くの場合アセンブリ言語で実装されています。アセンブリコードの正確性は、Goプログラム全体の安定性と信頼性に直結します。
  3. 将来的なgo vetの改善: コミットメッセージには、「go vetはいくつかの重要な問題も特定した」とあり、このコミットは「些細なgo vetの結果を取り除く」ためのものであると明記されています。これは、より複雑で重要な問題に取り組む前に、簡単な修正を先行して適用することで、go vetの出力ノイズを減らし、今後の解析作業を効率化する意図があったことを示唆しています。また、アセンブリコード内のnn(SP)参照をname+(nn-8)(FP)形式に書き換えるというTODOコメントが追加されており、これはgo vetがスタックフレーム参照をより正確にチェックできるようにするための将来的な改善計画を示しています。
  4. NOSPLITの存在: コミットメッセージでは、これらの修正が「NOSPLITが関数に付与されているため、今日問題を引き起こすことはないはずだ」と述べられています。NOSPLITは、Goコンパイラに対して、その関数がスタックを拡張しないことを指示するアセンブリディレクティブです。これにより、スタックフレームサイズの不一致が即座にクラッシュなどの問題を引き起こす可能性が低減されますが、それでも潜在的なバグであることには変わりありません。

これらの背景から、このコミットは、Goランタイムの堅牢性を高め、静的解析ツールの有効性を向上させるための、体系的な品質保証プロセスの一環として行われたものと理解できます。

前提知識の解説

このコミットを理解するためには、以下の技術的な概念について理解しておく必要があります。

  1. 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ベースの参照の方がコンパイラにとって解析しやすいという背景があります。
  2. スタックフレーム:

    • 関数が呼び出されるたびに、その関数専用のメモリ領域がスタック上に確保されます。これをスタックフレームと呼びます。
    • スタックフレームには、関数の引数、ローカル変数、呼び出し元のリターンアドレス、レジスタの退避領域などが格納されます。
    • $framesizeは、このスタックフレームの合計サイズをバイト単位で指定します。このサイズが正確でないと、スタックの破損や、他の関数のスタック領域への不正なアクセスが発生する可能性があります。
  3. go vetツール:

    • go vetは、Go言語のソースコードを静的に解析し、疑わしい構造や潜在的なエラーを報告するツールです。コンパイルエラーにはならないが、実行時に問題を引き起こす可能性のあるコードパターンを検出します。
    • このコミットの文脈では、go vetがアセンブリコードにおけるスタックフレームサイズの不一致を検出する能力を持っていることが示されています。
  4. NOSPLITフラグ:

    • Goのアセンブリ関数に付与されるフラグの一つです。
    • このフラグが付いている関数は、実行中にスタックを拡張しない(スタックの分割を行わない)ことをコンパイラに保証します。
    • 通常、Goの関数は必要に応じてスタックを自動的に拡張しますが、NOSPLITが付いている関数は、呼び出し時に十分なスタック領域が確保されていることを前提とします。これは、システムコールのように非常に短い時間で実行され、スタック拡張のオーバーヘッドを避けたい場合に利用されます。
    • NOSPLITが付いているため、スタックフレームサイズのわずかな不一致が直ちにクラッシュを引き起こす可能性は低いとコミットメッセージで述べられています。これは、関数がスタックを拡張しようとしないため、誤ったサイズが指定されていても、その関数が必要とする最小限のスタック領域が常に確保されている限り、問題が顕在化しにくいことを意味します。
  5. syscallパッケージ:

    • Go言語の標準ライブラリの一つで、オペレーティングシステムの低レベルな機能(システムコール)へのアクセスを提供します。
    • ファイル操作、ネットワーク通信、プロセス管理など、OSカーネルが提供する様々な機能を利用するために使用されます。
    • OS固有のシステムコールは、Goのポータビリティを維持しつつ、各OSのネイティブな機能にアクセスするために、多くの場合アセンブリ言語で実装されています。

これらの概念を理解することで、このコミットがGoランタイムの安定性と品質向上にどのように貢献しているかを深く把握することができます。

技術的詳細

このコミットの技術的詳細は、主にGoのアセンブリ言語におけるスタックフレームの管理と、go vetによる静的解析の連携にあります。

  1. スタックフレームサイズの修正:

    • Goのアセンブリ関数は、TEXT symbol(SB),flags,$framesize-argsizeという形式で定義されます。ここで$framesizeは、その関数が使用するスタックフレームの合計サイズ(ローカル変数、レジスタ退避領域、呼び出す他の関数の引数領域など)をバイト単位で指定します。
    • このコミットでは、多くのsyscall関連のアセンブリ関数(例: Syscall, Syscall6, RawSyscallなど)において、この$framesizeの値が減少しています。具体的には、多くのケースで4バイトまたは8バイト減少しています。
      • 例: TEXT ·Syscall(SB),NOSPLIT,$0-32TEXT ·Syscall(SB),NOSPLIT,$0-28 に変更(4バイト減少)。
      • 例: TEXT ·Syscall(SB),NOSPLIT,$0-64TEXT ·Syscall(SB),NOSPLIT,$0-56 に変更(8バイト減少)。
    • この減少は、おそらく以前の$framesizeが過剰に確保されていたか、またはGoコンパイラやリンカの最適化、あるいはアセンブリコードの変更によって、実際に必要なスタック領域が減少したためと考えられます。go vetがこの不一致を検出したことで、より正確なサイズに修正されました。
    • スタックフレームサイズが過剰に確保されていても、通常はメモリの無駄遣いにとどまりますが、厳密なスタック管理が求められる低レベルコードでは、このような不正確さも修正の対象となります。
  2. go vetの役割:

    • go vetは、Goのソースコードだけでなく、アセンブリコードに対しても静的解析を実行します。このコミットは、go vetがアセンブリ関数におけるスタックフレームサイズの宣言と実際の使用状況との不一致を検出できることを示しています。
    • これは、Goツールチェインが低レベルなアセンブリコードの正確性も検証しようとしていることの表れです。
  3. NOSPLITフラグの影響:

    • 修正されたほとんどの関数にはNOSPLITフラグが付与されています。これは、これらの関数が実行中にスタックを拡張しないことを意味します。
    • コミットメッセージにあるように、NOSPLITが付いているため、スタックフレームサイズの不一致が直ちにクラッシュなどの問題を引き起こす可能性は低いとされています。これは、関数がスタックを拡張しようとしないため、誤ったサイズが指定されていても、その関数が必要とする最小限のスタック領域が常に確保されている限り、問題が顕在化しにくいことを意味します。しかし、これはあくまで「問題が顕在化しにくい」だけであり、潜在的なバグであることには変わりありません。
  4. errnoからerrへの変更(NaClアーキテクチャ):

    • src/pkg/syscall/asm_nacl_386.ssrc/pkg/syscall/asm_nacl_amd64p32.sにおいて、スタック上の変数参照がerrno+24(FP)からerr+24(FP)に変更されています。
    • これは、Goの慣習的な命名規則(エラー変数はerr)に合わせるか、またはgo vetが検出した別の警告(例えば、未定義のシンボル参照や型不一致)を修正するためのものと考えられます。NaCl(Native Client)はGoogle Chromeブラウザ内でネイティブコードを実行するためのサンドボックス技術であり、特定のプラットフォームに特化したアセンブリコードが存在します。
  5. 将来の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 vetnn(SP)形式のスタック参照を完全に正確に解析できない可能性があることを示唆しています。name+(nn-8)(FP)形式は、フレームポインタ(FP)を基準とした参照であり、引数や戻り値へのアクセスをより明示的にします。この形式に統一することで、go vetがスタックフレームのレイアウトと参照の正確性をより厳密に検証できるようになるという、将来的なツール改善の方向性を示しています。

これらの技術的詳細は、Goランタイムの低レベルな実装における厳密なメモリ管理と、静的解析ツールを用いた継続的な品質保証の重要性を浮き彫りにしています。

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

このコミットにおけるコアとなるコードの変更箇所は、主にsrc/pkg/syscall/ディレクトリ内の様々なOSおよびアーキテクチャ向けのアセンブリファイル(.s拡張子)におけるTEXTディレクティブのスタックフレームサイズ($framesize)の変更です。

以下に、代表的な変更パターンを示します。

  1. スタックフレームサイズの減少:

    • 多くの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などのアーキテクチャの組み合わせで多数見られます。

  2. 変数名の修正(NaClアーキテクチャ):

    • src/pkg/syscall/asm_nacl_386.ssrc/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に変更されています。
  3. 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に変更されています。

なぜこの修正が必要だったのか?:

  1. go vetによる検出: go vetツールは、アセンブリコードを解析し、宣言された$framesizeと、実際にコード内で使用されているスタック領域の量との間に不一致があることを検出しました。これは、アセンブリコードが手書きであるため、人間が誤って過剰なスタックサイズを宣言してしまっていた可能性を示唆しています。
  2. 正確性の向上: スタックフレームサイズが過剰に宣言されていても、直接的なクラッシュには繋がりにくい場合があります(特にNOSPLITが付いている場合)。しかし、これはメモリの無駄遣いであり、低レベルなランタイムコードにおいては、可能な限り正確なリソース管理が求められます。この修正は、Goランタイムのメモリフットプリントをわずかに最適化し、より正確なスタック管理を実現します。
  3. 将来的な解析の準備: コミットメッセージと追加されたTODOコメントが示唆するように、この修正は、go vetがアセンブリコードのスタック参照をより厳密にチェックできるようにするための準備段階でもあります。nn(SP)形式の参照をname+(nn-8)(FP)形式に統一することで、go vetがスタックフレームのレイアウトをより正確に推論し、潜在的なバグをさらに検出できるようになることが期待されます。

NOSPLITの影響: 修正された関数にはNOSPLITフラグが付与されています。これは、これらの関数が実行中にスタックを自動的に拡張しないことを意味します。そのため、宣言された$framesizeが実際の必要量よりも大きくても、スタックオーバーフローなどの問題がすぐに発生するわけではありませんでした。しかし、これはあくまで「問題が顕在化しにくい」だけであり、宣言と実態の不一致は潜在的なバグとして修正されるべきものです。

NaClアーキテクチャでの変数名修正: src/pkg/syscall/asm_nacl_386.ssrc/pkg/syscall/asm_nacl_amd64p32.sにおけるerrnoからerrへの変更は、Goの慣習的な命名規則への準拠、またはgo vetが検出した別の警告(例えば、シンボル解決に関するもの)の修正と考えられます。これは、コードの一貫性と可読性を高めるためのクリーンアップ作業の一部です。

このコミットは、Goランタイムの基盤となるアセンブリコードの正確性を高め、静的解析ツールを活用してコード品質を継続的に改善していくGo開発チームの姿勢を反映しています。

関連リンク

参考にした情報源リンク

  • 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)