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

[インデックス 10534] ファイルの概要

このコミットは、GoランタイムのWindows/amd64アーキテクチャにおけるシステムコールエントリ時のスタックアライメントに関するバグ修正です。具体的には、Windows x64の呼び出し規約で要求される16バイトスタックアライメントを保証するために、maxargsマクロの値を15から16に変更しています。これにより、ビルドエラーが修正され、Windows上でのGoプログラムの安定性と互換性が向上します。

コミット

commit fbdec642a980d73cdd7dbfd18018f18c6d198ffc
Author: Alex Brainman <alex.brainman@gmail.com>
Date:   Tue Nov 29 12:57:20 2011 +1100

    runtime: make sure windows/amd64 stack is 16-byte aligned on syscall entry (fixes build)

    R=golang-dev, vcc.163
    CC=golang-dev
    https://golang.org/cl/5445051

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/fbdec642a980d73cdd7dbfd18018f18c6d198ffc

元コミット内容

runtime: make sure windows/amd64 stack is 16-byte aligned on syscall entry (fixes build)

R=golang-dev, vcc.163
CC=golang-dev
https://golang.org/cl/5445051

変更の背景

この変更は、Windows/amd64環境でGoプログラムをビルドする際に発生していた問題を修正するために行われました。問題の根源は、Windows x64の呼び出し規約がスタックの16バイトアライメントを厳密に要求するのに対し、Goランタイムがシステムコール(syscall)に入る際にこの要件を満たしていなかったことにあります。

Windows x64の呼び出し規約では、関数呼び出しが行われる直前には、スタックポインタ(RSP)が常に16バイト境界にアライメントされている必要があります。これは、特にSSE(Streaming SIMD Extensions)命令など、128ビットデータを扱う命令が効率的に動作するために重要です。Goランタイムがシステムコールを行う際、このアライメントが崩れていると、コンパイラやリンカが生成するコードが不正なメモリアクセスを引き起こしたり、パフォーマンスが低下したり、最悪の場合、ビルドエラーや実行時クラッシュにつながる可能性があります。

このコミットは、システムコールエントリ時のスタックアライメントを修正することで、Windows/amd64環境でのGoプログラムのビルドと実行の信頼性を確保することを目的としています。

前提知識の解説

Windows x64 呼び出し規約 (x64 Calling Convention)

Windows x64(64ビット版Windows)における関数呼び出し規約は、Microsoftが定義する標準的な規約であり、関数がどのように引数を受け渡し、スタックを管理するかを定めています。主要な特徴は以下の通りです。

  1. レジスタ渡し: 最初の4つの整数またはポインタ引数は、RCX, RDX, R8, R9 レジスタを介して渡されます。浮動小数点引数はXMM0-XMM3レジスタを介して渡されます。これらを超える引数はスタックにプッシュされます。
  2. シャドウスペース (Shadow Space): 呼び出し元(caller)は、関数を呼び出す前に、スタック上に32バイトの「シャドウスペース」を確保する責任があります。このスペースは、呼び出された関数(callee)がレジスタ引数(RCX, RDX, R8, R9)を保存するために使用できます。このシャドウスペースは、リターンアドレスのすぐ上に位置します。
  3. スタックアライメント (Stack Alignment): 最も重要な点として、関数呼び出しが行われる直前(CALL命令の直前)には、スタックポインタ(RSP)が常に16バイト境界にアライメントされている必要があります。 これは、CALL命令がリターンアドレス(8バイト)をスタックにプッシュするため、関数エントリ時にはRSPが16バイト境界から8バイトずれた位置になることを意味します。関数内部では、RSPは常に16バイトアライメントを維持する必要があります。このアライメントは、特にSSE命令が128ビットデータを効率的に処理するために不可欠です。

システムコール (Syscall)

システムコールは、ユーザーモードのプログラムがオペレーティングシステム(OS)のカーネルモードの機能にアクセスするためのメカニズムです。ファイル操作、ネットワーク通信、メモリ管理など、OSが提供する低レベルなサービスを利用する際にシステムコールが使用されます。システムコールは、通常の関数呼び出しとは異なり、特権レベルの変更を伴うため、特定の規約や手順に従って実行されます。Windows/amd64では、通常、syscall命令を使用してカーネルに制御を移します。

技術的詳細

このコミットは、GoランタイムがWindows/amd64上でシステムコールを行う際のスタックアライメントの問題に対処しています。

Goのランタイムは、システムコールを行う際に、引数をスタックに配置し、特定のレジスタを設定してからsyscall命令を実行します。Windows x64の呼び出し規約では、syscall命令が実行される直前のスタックポインタが16バイトアライメントされている必要があります。

元のコードでは、maxargsというマクロが15と定義されていました。このmaxargsは、システムコールに渡される引数の最大数を制御するために使用されていたと考えられます。しかし、Windowsのスタックアライメント要件を考慮すると、スタックにプッシュされる引数の総バイト数が16の倍数になるように調整する必要があります。

もしmaxargsが奇数(例: 15)である場合、スタックにプッシュされる引数の総サイズが16バイトの倍数にならない可能性があり、結果としてシステムコールエントリ時のスタックアライメントが崩れることになります。これは、syscall命令が実行される際に、RSPが16バイト境界にない状態を引き起こし、Windowsの呼び出し規約違反となります。

このコミットでは、maxargs16に変更することで、スタックにプッシュされる引数の総サイズが常に16バイトの倍数になるように保証しています。コメントにも「Windows stack must be kept 16-byte aligned on syscall entry」と明記されており、maxargsが2で割り切れる必要があることが示されています。これにより、システムコールが呼び出される直前のスタックポインタが適切に16バイトアライメントされ、Windows x64の呼び出し規約に準拠するようになります。

この修正は、GoランタイムがWindows上で外部のCライブラリやOS APIと連携する際に、スタックアライメントの不一致による潜在的な問題を回避するために不可欠です。

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

--- a/src/pkg/runtime/windows/amd64/sys.s
+++ b/src/pkg/runtime/windows/amd64/sys.s
@@ -4,7 +4,9 @@

 #include "amd64/asm.h"\n

-#define maxargs 15
+// maxargs should be divisible by 2, as Windows stack
+// must be kept 16-byte aligned on syscall entry.
+#define maxargs 16

 // void runtime·asmstdcall(void *c);\n
 TEXT runtime·asmstdcall(SB),7,$0

コアとなるコードの解説

変更はsrc/pkg/runtime/windows/amd64/sys.sファイル内で行われています。このファイルは、GoランタイムのWindows/amd64アーキテクチャにおける低レベルなシステムコール関連のアセンブリコードを含んでいます。

変更点は以下の通りです。

-#define maxargs 15
+// maxargs should be divisible by 2, as Windows stack
+// must be kept 16-byte aligned on syscall entry.
+#define maxargs 16
  • - #define maxargs 15: 変更前のmaxargsの値は15でした。
  • + // maxargs should be divisible by 2, as Windows stack: 新しく追加されたコメントで、maxargsが2で割り切れる必要がある理由が説明されています。これは、Windowsのスタックがシステムコールエントリ時に16バイトアライメントされている必要があるためです。
  • + // must be kept 16-byte aligned on syscall entry.: 上記の理由をさらに補足しています。
  • + #define maxargs 16: maxargsの値が16に変更されました。

この変更により、システムコールに渡される引数の最大数が16に設定され、スタックにプッシュされる引数の総サイズが常に16バイトの倍数になることが保証されます。これにより、syscall命令が実行される直前のスタックポインタが適切に16バイトアライメントされ、Windows x64の呼び出し規約に準拠するようになります。結果として、Windows/amd64環境でのGoプログラムのビルドが成功し、実行時の安定性が向上します。

関連リンク

参考にした情報源リンク