[インデックス 16543] ファイルの概要
このコミットは、Goランタイムにおけるエントリポイントの命名規則に関する変更です。具体的には、各アーキテクチャ(例: 386
, amd64
, arm
)に依存していたランタイムのエントリポイント名 _rt0_$GOARCH
を、アーキテクチャに依存しない統一された名前 _rt0_go
に変更しています。
コミット
commit 6120ef079948340d6819b8f9a9d526ad4292e26e
Author: Russ Cox <rsc@golang.org>
Date: Tue Jun 11 16:49:24 2013 -0400
runtime: rename _rt0_$GOARCH to _rt0_go
There's no reason to use a different name on each architecture,
and doing so makes it impossible for portable code to refer to
the original Go runtime entry point. Rename it _rt0_go everywhere.
This is a global search and replace only.
R=golang-dev, bradfitz, minux.ma
CC=golang-dev
https://golang.org/cl/10196043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/6120ef079948340d6819b8f9a9d526ad4292e26e
元コミット内容
runtime: rename _rt0_$GOARCH to _rt0_go
各アーキテクチャで異なる名前を使用する理由はなく、そうすることでポータブルなコードが元のGoランタイムのエントリポイントを参照することが不可能になる。これを_rt0_go
に everywhere (どこでも) リネームする。
これはグローバルな検索と置換のみである。
変更の背景
Go言語のプログラムが実行される際、オペレーティングシステムによって最初に呼び出されるのは、Goランタイムの初期エントリポイントです。このエントリポイントは、プログラムの実行環境をセットアップし、Goのスケジューラを起動し、最終的にユーザーが記述したmain
関数を呼び出す役割を担います。
このコミット以前は、この初期エントリポイントのシンボル名が、ターゲットとなるCPUアーキテクチャ(例: 386
、amd64
、arm
)によって異なっていました。例えば、x86アーキテクチャでは_rt0_386
、x86-64アーキテクチャでは_rt0_amd64
といった具合です。
このようなアーキテクチャ固有の命名は、Goランタイムの内部実装においては問題ありませんでしたが、Goのコードベース全体、特にポータブルな(異なるアーキテクチャ間で共通して動作する)コードが、このランタイムの初期エントリポイントを直接参照する必要がある場合に、不便や複雑さをもたらしていました。具体的には、_rt0_$GOARCH
という形式では、$GOARCH
の部分を動的に解決する必要があり、静的な参照が困難でした。
この変更の背景には、Goの設計哲学である「シンプルさ」と「ポータビリティ」の追求があります。ランタイムの内部シンボルであっても、可能な限り統一されたインターフェースを提供することで、将来的な拡張性やメンテナンス性を向上させることが目的でした。
前提知識の解説
このコミットを理解するためには、以下の前提知識が役立ちます。
- Goランタイム (Go Runtime): Goプログラムの実行を管理する低レベルのコンポーネント群です。ガベージコレクション、スケジューラ、メモリ管理、システムコールインターフェースなどが含まれます。Goプログラムは、OSによって直接実行されるのではなく、まずランタイムが起動し、その上でGoのコードが実行されます。
- エントリポイント (Entry Point): プログラムが実行を開始する最初の場所です。C言語のプログラムにおける
main
関数に相当しますが、Goの場合、ユーザーが記述するmain
関数の前に、ランタイムの初期化処理を行うための低レベルなエントリポイントが存在します。 - アセンブリ言語 (Assembly Language): CPUが直接理解できる機械語に非常に近い低レベルのプログラミング言語です。Goランタイムの初期エントリポイントや、特定のパフォーマンスが要求される部分は、C言語ではなくアセンブリ言語で記述されることがあります。これは、OSとのインターフェースや、特定のCPUレジスタの操作など、非常に低レベルな制御が必要なためです。
_rt0_
プレフィックス: Goランタイムの初期化ルーチンに関連するシンボルに付けられる慣例的なプレフィックスです。rt0
は "runtime 0" を意味し、ランタイムの最も初期の段階で実行されるコードを示します。$GOARCH
: Goのビルドシステムで使用される環境変数で、ターゲットとなるCPUアーキテクチャ(例:386
,amd64
,arm
)を示します。- ポータブルコード (Portable Code): 特定のハードウェアやオペレーティングシステムに依存せず、様々な環境で動作するように設計されたコードのことです。
- シンボル (Symbol): プログラム内の関数や変数などの名前を指します。コンパイラやリンカは、これらのシンボルを使ってコード内の参照を解決します。
技術的詳細
このコミットの技術的な詳細は、Goランタイムの初期化プロセスにおけるシンボル解決と、クロスプラットフォーム開発の利便性向上に焦点を当てています。
Goプログラムがコンパイルされると、最終的な実行可能ファイルには、Goランタイムのコードとユーザーが記述したGoコードが結合されます。OSがこの実行可能ファイルをロードし、実行を開始する際、OSはプログラムの「エントリポイント」に制御を渡します。Goの場合、このエントリポイントは通常、Goランタイムの初期化ルーチンへのジャンプ命令を含んでいます。
コミット以前は、この初期化ルーチンのシンボル名が、ビルドターゲットのアーキテクチャによって異なっていました。例えば、src/pkg/runtime/asm_386.s
(32ビットx86アセンブリ)では_rt0_386
、src/pkg/runtime/asm_amd64.s
(64ビットx86アセンブリ)では_rt0_amd64
というシンボルが定義されていました。これらのシンボルは、それぞれのアーキテクチャ固有の初期化処理(スタックの設定、引数のコピーなど)を実行するアセンブリコードの開始点でした。
このアーキテクチャ固有の命名は、ランタイムの内部では問題なく機能していましたが、Goのツールチェインや、Goランタイムの挙動を理解しようとする外部ツール、あるいはGoの内部でランタイムのエントリポイントを抽象的に参照する必要がある場合に、不便さを生じていました。例えば、Goのリンカやデバッガが、特定のアーキテクチャに依存しない形でランタイムの初期エントリポイントを識別しようとする場合、_rt0_386
、_rt0_amd64
、_rt0_arm
といった複数の名前を考慮する必要がありました。
このコミットでは、この問題を解決するために、すべてのアーキテクチャで共通のシンボル名_rt0_go
を使用するように変更しました。これにより、Goランタイムの初期エントリポイントは、どのアーキテクチャでビルドされたGoプログラムであっても、常に_rt0_go
という統一された名前で参照できるようになります。
この変更は、主にアセンブリコード内のシンボル名の置換によって行われました。具体的には、TEXT _rt0_$GOARCH(SB)
という形式で定義されていたエントリポイントがTEXT _rt0_go(SB)
に変更され、それに伴い、他のアセンブリファイルやランタイムのCコード(当時はまだC言語で書かれた部分も存在しました)からこのエントリポイントを参照している箇所もすべて_rt0_go
に更新されました。
この変更は「グローバルな検索と置換のみ」と説明されており、ランタイムの機能的な挙動そのものには影響を与えません。しかし、シンボル名の統一は、Goのツールチェインの簡素化、ランタイムコードの可読性向上、そして将来的なポータブルなランタイム機能の開発において重要な基盤となります。例えば、Goのデバッガがランタイムの初期化処理にブレークポイントを設定する際、アーキテクチャを意識することなく_rt0_go
に設定できるようになります。
コアとなるコードの変更箇所
このコミットは、Goランタイムの様々なアーキテクチャおよびOS固有のアセンブリファイルにおける、エントリポイントのシンボル名を変更しています。具体的には、以下のファイルが変更されています。
src/pkg/runtime/asm_386.s
src/pkg/runtime/asm_amd64.s
src/pkg/runtime/asm_arm.s
src/pkg/runtime/rt0_darwin_386.s
src/pkg/runtime/rt0_darwin_amd64.s
src/pkg/runtime/rt0_freebsd_386.s
src/pkg/runtime/rt0_freebsd_amd64.s
src/pkg/runtime/rt0_freebsd_arm.s
src/pkg/runtime/rt0_linux_386.s
src/pkg/runtime/rt0_linux_amd64.s
src/pkg/runtime/rt0_linux_arm.s
src/pkg/runtime/rt0_netbsd_386.s
src/pkg/runtime/rt0_netbsd_amd64.s
src/pkg/runtime/rt0_netbsd_arm.s
src/pkg/runtime/rt0_openbsd_386.s
src/pkg/runtime/rt0_openbsd_amd64.s
src/pkg/runtime/rt0_plan9_386.s
src/pkg/runtime/rt0_plan9_amd64.s
src/pkg/runtime/rt0_windows_386.s
src/pkg/runtime/rt0_windows_amd64.s
これらのファイルはすべて、Goランタイムの初期化処理を記述したアセンブリ言語のソースファイルです。変更内容は、主に以下の2つのパターンに集約されます。
-
エントリポイントの定義箇所の変更:
--- a/src/pkg/runtime/asm_386.s +++ b/src/pkg/runtime/asm_386.s @@ -4,7 +4,7 @@ #include "zasm_GOOS_GOARCH.h" -TEXT _rt0_386(SB),7,$0 +TEXT _rt0_go(SB),7,$0 // copy arguments forward on an even stack MOVL argc+0(FP), AX MOVL argv+4(FP), BX
これは、
_rt0_386
というシンボル名で定義されていたエントリポイントが、_rt0_go
という名前に変更されたことを示しています。同様の変更が、asm_amd64.s
では_rt0_amd64
から_rt0_go
へ、asm_arm.s
では_rt0_arm
から_rt0_go
へと行われています。 -
エントリポイントへのジャンプ/参照箇所の変更:
--- a/src/pkg/runtime/rt0_darwin_386.s +++ b/src/pkg/runtime/rt0_darwin_386.s @@ -11,4 +11,4 @@ TEXT _rt0_386_darwin(SB),7,$8 INT $3 TEXT main(SB),7,$0 - JMP _rt0_386(SB) + JMP _rt0_go(SB)
これは、OS固有の初期化ルーチン(この場合はDarwin/386)から、Goランタイムの共通エントリポイントへジャンプする命令が、
_rt0_386
から_rt0_go
に変更されたことを示しています。MOVQ
命令でアドレスをレジスタにロードしてからジャンプするパターン(例:rt0_darwin_amd64.s
)も同様に変更されています。
コアとなるコードの解説
Goのプログラムが実行される際、OSはまず特定の初期化コードに制御を渡します。この初期化コードは、OSやアーキテクチャに依存する部分(例: コマンドライン引数の取得、環境変数の設定など)を処理した後、Goランタイムの共通エントリポイントにジャンプします。
変更前のコードでは、この共通エントリポイントのシンボル名が、_rt0_386
、_rt0_amd64
、_rt0_arm
のように、ターゲットアーキテクチャを示すサフィックスを持っていました。これは、各アーキテクチャのアセンブリファイル内で、それぞれのアーキテクチャに特化した初期化処理を行うTEXT
セクションのラベルとして定義されていました。
例えば、src/pkg/runtime/asm_386.s
のTEXT _rt0_386(SB),7,$0
は、32ビットx86アーキテクチャにおけるGoランタイムの初期エントリポイントを定義していました。SB
は "Static Base" を意味し、シンボルが静的セグメントに属することを示します。7
はフラグ、$0
はフレームサイズです。このエントリポイントでは、OSから渡されたコマンドライン引数(argc
とargv
)をGoランタイムが扱える形式にコピーするなどの初期処理が行われます。
このコミットによって、これらのアーキテクチャ固有のシンボル名がすべて_rt0_go
に統一されました。これにより、Goランタイムの初期化フローは、どのアーキテクチャにおいても_rt0_go
という単一のシンボルを介して開始されることになります。
この変更の意義は、Goのビルドシステム、リンカ、デバッガ、そしてGoランタイムの内部コードが、アーキテクチャの違いを意識することなく、Goプログラムの「真の」エントリポイントを_rt0_go
として一貫して参照できるようになった点にあります。これにより、Goのツールチェインの設計が簡素化され、将来的に新しいアーキテクチャが追加された場合でも、既存のツールやコードを変更することなく対応できるようになります。
例えば、src/pkg/runtime/rt0_darwin_386.s
のようなOS固有のランタイム初期化ファイルでは、OSがプログラムに制御を渡した後、最終的にGoランタイムの共通エントリポイントにジャンプします。変更前はJMP _rt0_386(SB)
のようにアーキテクチャ固有のシンボルにジャンプしていましたが、変更後はJMP _rt0_go(SB)
と統一されたシンボルにジャンプするようになりました。これは、OS固有の初期化とGoランタイムの共通初期化の間のインターフェースが、より抽象化され、クリーンになったことを意味します。
この変更は、Goのランタイムがよりモジュール化され、アーキテクチャ間の差異を内部で吸収しつつ、外部に対しては統一されたインターフェースを提供するという設計思想を反映しています。
関連リンク
- Goのランタイムに関する公式ドキュメント(Goのバージョンによって内容は異なりますが、ランタイムの役割について概説されています): https://go.dev/doc/effective_go#runtime
- Goのソースコードリポジトリ: https://github.com/golang/go
- GoのIssue Tracker (Goの機能要望やバグ報告): https://github.com/golang/go/issues
参考にした情報源リンク
- Goの公式ドキュメント
- Goのソースコード
- Goのコミット履歴
- アセンブリ言語の一般的な知識
- オペレーティングシステムにおけるプログラムのロードと実行に関する一般的な知識