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

[インデックス 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アーキテクチャ(例: 386amd64arm)によって異なっていました。例えば、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_386src/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つのパターンに集約されます。

  1. エントリポイントの定義箇所の変更:

    --- 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へと行われています。

  2. エントリポイントへのジャンプ/参照箇所の変更:

    --- 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.sTEXT _rt0_386(SB),7,$0は、32ビットx86アーキテクチャにおけるGoランタイムの初期エントリポイントを定義していました。SBは "Static Base" を意味し、シンボルが静的セグメントに属することを示します。7はフラグ、$0はフレームサイズです。このエントリポイントでは、OSから渡されたコマンドライン引数(argcargv)を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のソースコード
  • Goのコミット履歴
  • アセンブリ言語の一般的な知識
  • オペレーティングシステムにおけるプログラムのロードと実行に関する一般的な知識