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

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

このコミットは、Go言語のsyscallパッケージ内のアセンブリファイル群にわたる変更です。具体的には、以下のファイルが影響を受けています。

  • src/pkg/syscall/asm_darwin_386.s
  • src/pkg/syscall/asm_darwin_amd64.s
  • src/pkg/syscall/asm_freebsd_386.s
  • src/pkg/syscall/asm_freebsd_amd64.s
  • src/pkg/syscall/asm_freebsd_arm.s
  • src/pkg/syscall/asm_linux_386.s
  • src/pkg/syscall/asm_linux_amd64.s
  • src/pkg/syscall/asm_linux_arm.s
  • src/pkg/syscall/asm_netbsd_386.s
  • src/pkg/syscall/asm_netbsd_amd64.s
  • src/pkg/syscall/asm_netbsd_arm.s
  • src/pkg/syscall/asm_openbsd_386.s
  • src/pkg/syscall/asm_openbsd_amd64.s
  • src/pkg/syscall/asm_plan9_386.s
  • src/pkg/syscall/asm_plan9_amd64.s

これらのファイルは、Goプログラムが各オペレーティングシステム(Darwin, FreeBSD, Linux, NetBSD, OpenBSD, Plan 9)およびアーキテクチャ(386, AMD64, ARM)上でシステムコールを実行するためのアセンブリコードを含んでいます。

コミット

  • コミットハッシュ: db324ccd72c93d34effa24e6f1a2a12c0cd39f04
  • Author: Keith Randall khr@golang.org
  • Date: Mon Aug 12 10:24:30 2013 -0700

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

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

元コミット内容

syscall: Convert textflags from numbers to symbols.

R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/12773043

変更の背景

このコミットの背景には、Goのアセンブリコードにおける関数定義の可読性と保守性の向上が挙げられます。以前のGoのアセンブリでは、TEXTディレクティブに続くフラグが数値で表現されていました。これらの数値は、関数の特定の属性(例: スタックフレームの扱い、スケジューラの介入の有無など)をビットマスクとしてエンコードしていました。

しかし、数値フラグは直感的ではなく、その意味を理解するためにはドキュメントやソースコードを参照する必要がありました。また、新しいフラグが追加されたり、既存のフラグの意味が変更されたりする際に、数値の衝突や誤解を招く可能性がありました。

このコミットでは、これらの数値フラグをNOSPLITのようなシンボリックな定数に置き換えることで、コードの意図をより明確にし、将来的な拡張や変更を容易にすることを目指しています。これにより、アセンブリコードの可読性が向上し、開発者がコードの挙動をより迅速に理解できるようになります。特に、syscallパッケージのような低レベルでOS固有のアセンブリコードでは、正確性と明確性が極めて重要です。

前提知識の解説

Goアセンブリ

Go言語は、一部の低レベルな処理(システムコール、ランタイムのコア部分など)にアセンブリ言語を使用しています。Goのアセンブリは、一般的なアセンブリ言語とは異なり、Goツールチェーン(特にリンカcmd/link)によって解釈される独自の擬似命令と構文を持っています。これはPlan 9アセンブラの構文に似ています。

Goアセンブリの関数定義は通常、TEXTディレクティブで始まります。 TEXT symbol(SB), flags, $framesize-argsizes

  • symbol(SB): 関数のシンボル名。SBは「Static Base」を意味し、グローバルシンボルであることを示します。
  • flags: 関数の属性を定義するフラグ。これがこのコミットの主要な変更点です。
  • $framesize: 関数のスタックフレームサイズ。
  • argsizes: 引数の合計サイズ。

TEXTディレクティブのフラグ(textflags)

TEXTディレクティブのフラグは、Goランタイムがその関数をどのように扱うべきかをリンカに指示します。これらはビットマスクとして定義されており、複数のフラグを組み合わせることができます。

このコミット以前は、これらのフラグは数値リテラルで直接指定されていました。例えば、7という数値は特定のフラグの組み合わせを表していました。

NOSPLITフラグ

NOSPLITは、Goアセンブリにおけるtextflagの一つです。このフラグが設定された関数は、スタックの拡張チェックを行いません。

Goランタイムは通常、関数呼び出し時にスタックの残量をチェックし、必要に応じてスタックを拡張します。しかし、システムコールのような非常に低レベルで、スタックを拡張する前に実行を完了する必要がある関数や、スタックの拡張が不適切なコンテキストで実行される関数(例: シグナルハンドラやスケジューラのコンテキスト)では、このスタック拡張チェックをスキップする必要があります。NOSPLITフラグは、このような場合にリンカに対してスタック拡張チェックコードを生成しないように指示します。

このコミットでは、以前数値で表現されていたNOSPLITに相当するフラグが、明示的にNOSPLITというシンボルで記述されるようになりました。

../../cmd/ld/textflag.h

このヘッダーファイルは、Goリンカ(cmd/ld)が使用するアセンブリフラグの定義を含んでいます。アセンブリファイル内で#includeディレクティブを使用してこのファイルをインクルードすることで、シンボリックなフラグ定数(例: NOSPLIT)を使用できるようになります。これにより、アセンブリコードとリンカの間のインターフェースがより明確になり、コンパイル時の整合性が保証されます。

技術的詳細

このコミットの技術的な核心は、Goのアセンブリリンカ(cmd/link)が、TEXTディレクティブのフラグを数値リテラルではなく、シンボリックな定数として解釈するように変更された点にあります。

具体的には、以下の変更が行われています。

  1. #include "../../cmd/ld/textflag.h" の追加: 各アセンブリファイルの冒頭に、textflag.hヘッダーファイルをインクルードする行が追加されました。このヘッダーファイルには、NOSPLITなどのシンボリックなフラグ定数が定義されています。これにより、アセンブリコード内でこれらのシンボルを直接使用できるようになります。

  2. 数値フラグからシンボリックフラグへの置換: 既存のTEXTディレクティブにおいて、数値で指定されていたフラグ(例: 7)が、対応するシンボリックなフラグ(例: NOSPLIT)に置き換えられました。

    例えば、多くのシステムコールラッパー関数では、以前はTEXT ·Syscall(SB),7,$0-32のように記述されていました。ここで7という数値は、NOSPLITフラグを含む特定のビットマスクを表していました。このコミットにより、これがTEXT ·Syscall(SB),NOSPLIT,$0-32のように変更されました。

この変更は、アセンブリコードの可読性を大幅に向上させます。数値の7が何を意味するのかを知るためには、リンカのソースコードやドキュメントを参照する必要がありましたが、NOSPLITというシンボルを見れば、その関数がスタック拡張チェックを行わないことが一目で理解できます。

また、この変更はGoツールチェーンの進化の一部でもあります。アセンブリコードの記述方法をより現代的で、Goの他の部分と整合性の取れたものにすることで、将来的なメンテナンスや機能追加が容易になります。例えば、新しいフラグが導入される場合でも、数値の割り当てを気にすることなく、新しいシンボルを追加するだけで済みます。

この変更は、Goのクロスプラットフォーム対応にも影響します。各OSとアーキテクチャ固有のアセンブリファイルがこの変更の対象となっていることから、Goランタイムの低レベルな部分における一貫性と保守性が向上していることがわかります。

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

以下は、src/pkg/syscall/asm_darwin_386.sにおける変更の抜粋です。他のファイルでも同様のパターンで変更が行われています。

--- a/src/pkg/syscall/asm_darwin_386.s
+++ b/src/pkg/syscall/asm_darwin_386.s
@@ -2,6 +2,8 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+#include "../../cmd/ld/textflag.h"
+
 //
 // System call support for 386, Darwin
 //
@@ -10,7 +12,7 @@
 // func Syscall6(trap int32, a1, a2, a3, a4, a5, a6 int32) (r1, r2, err int32);\n // Trap # in AX, args on stack above caller pc.\n \n-TEXT	·Syscall(SB),7,$0-32
+TEXT	·Syscall(SB),NOSPLIT,$0-32
  	CALL	runtime·entersyscall(SB)
  	MOVL	4(SP), AX	// syscall entry
  	// slide args down on top of system call number
@@ -34,7 +36,7 @@ ok:
  	CALL	runtime·exitsyscall(SB)
  	RET
  
-TEXT	·Syscall6(SB),7,$0-44
+TEXT	·Syscall6(SB),NOSPLIT,$0-44
  	CALL	runtime·entersyscall(SB)
  	MOVL	4(SP), AX	// syscall entry
  	// slide args down on top of system call number
@@ -61,7 +63,7 @@ ok6:
  	CALL	runtime·exitsyscall(SB)
  	RET
  
-TEXT	·Syscall9(SB),7,$0-56
+TEXT	·Syscall9(SB),NOSPLIT,$0-56
  	CALL	runtime·entersyscall(SB)
  	MOVL	4(SP), AX	// syscall entry
  	// slide args down on top of system call number
@@ -91,7 +93,7 @@ ok9:
  	CALL	runtime·exitsyscall(SB)
  	RET
  
-TEXT ·RawSyscall(SB),7,$0-32
+TEXT ·RawSyscall(SB),NOSPLIT,$0-32
  	MOVL	4(SP), AX	// syscall entry
  	// slide args down on top of system call number
  	LEAL		8(SP), SI
@@ -112,7 +114,7 @@ ok1:\n  	MOVL	$0, 28(SP)	// errno\n  	RET\n  
-TEXT	·RawSyscall6(SB),7,$0-44
+TEXT	·RawSyscall6(SB),NOSPLIT,$0-44
  	MOVL	4(SP), AX	// syscall entry
  	// slide args down on top of system call number
  	LEAL		8(SP), SI

コアとなるコードの解説

上記の差分は、Goのアセンブリファイルにおける関数定義のTEXTディレクティブの変更を示しています。

  1. #include "../../cmd/ld/textflag.h" の追加: これは、C言語のプリプロセッサディレクティブに似たGoアセンブラの機能です。../../cmd/ld/textflag.hというパスにあるファイルを現在のファイルに読み込みます。このヘッダーファイルには、NOSPLITのようなシンボリックな定数が定義されており、これらをアセンブリコード内で使用できるようにします。これにより、数値リテラルではなく、意味のある名前でフラグを指定できるようになります。

  2. TEXT ·Syscall(SB),7,$0-32 から TEXT ·Syscall(SB),NOSPLIT,$0-32 への変更:

    • TEXT ·Syscall(SB): Syscallという名前の関数を定義していることを示します。SBは静的ベースレジスタを意味し、このシンボルがグローバルであることを示します。
    • ,7: 変更前のフラグ指定です。7という数値は、特定のビットマスクを表していました。この場合、7NOSPLITフラグ(ビット値1)と、おそらく他の内部的なフラグ(例えば、RODATADUPOKなど、リンカが関数をどのように扱うかに関するもの)の組み合わせを意味していました。しかし、この数値だけではその意図が不明確でした。
    • ,NOSPLIT: 変更後のフラグ指定です。NOSPLITは、この関数がスタック拡張チェックを行わないことを明示的に示します。システムコールラッパーは、OSのカーネルに直接移行するため、Goランタイムのスタック拡張ロジックをバイパスする必要があります。このシンボルを使用することで、コードの意図が非常に明確になります。
    • ,$0-32: $0はスタックフレームサイズが0バイトであることを示し、-32は引数の合計サイズが32バイトであることを示します。これはこのコミットの変更点ではありません。

この変更は、Goのアセンブリコードの可読性と保守性を向上させるための重要なステップです。数値の羅列から意味のあるシンボルへの移行は、開発者がコードを理解し、デバッグし、将来的に変更する際の負担を軽減します。

関連リンク

参考にした情報源リンク