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

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

このコミットは、Go言語のランタイムが386アーキテクチャ(Intel 80386互換プロセッサ)上で、特にDarwin(macOS)とLinuxの両オペレーティングシステムで動作するための基盤を大幅に強化するものです。具体的には、386アーキテクチャに特化したシステムコールインターフェース、シグナルハンドリング、およびプログラムの初期起動ルーチンが追加・整備されました。これにより、Goプログラムがこれらの環境で「空のプログラム」であっても実行可能になるという、重要な進捗がもたらされました。また、vlrt.cにおける_subv関数の使用が直接的な演算に置き換えられるなど、既存コードの最適化も含まれています。

コミット

more 386 runtime:
        remove use of _subv in vlrt.c
        darwin/386/signal.c
        darwin/386/*
        linux/386/* (forgotten before)
    
    can run empty program on darwin/386 now.
    
    R=r
    DELTA=1140  (1021 added, 114 deleted, 5 changed)
    OCL=26942
    CL=26968

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

https://github.com/golang/go/commit/4702c0e5ef6ebccb2f0eda68571d003aebe95329

元コミット内容

commit 4702c0e5ef6ebccb2f0eda68571d003aebe95329
Author: Russ Cox <rsc@golang.org>
Date:   Tue Mar 31 15:45:12 2009 -0700

    more 386 runtime:
            remove use of _subv in vlrt.c
            darwin/386/signal.c
            darwin/386/*
            linux/386/* (forgotten before)
    
    can run empty program on darwin/386 now.
    
    R=r
    DELTA=1140  (1021 added, 114 deleted, 5 changed)
    OCL=26942
    CL=26968

変更の背景

このコミットが行われた2009年当時、Go言語はまだ開発の初期段階にあり、様々なアーキテクチャとオペレーティングシステムへの対応を進めている最中でした。特に、386アーキテクチャは当時の多くのシステムで広く利用されており、Goがより多くの環境で動作するためには、このアーキテクチャへの対応が不可欠でした。

Goランタイムは、OSの機能(システムコール、シグナルハンドリング、メモリ管理など)と密接に連携して動作します。そのため、特定のOSとアーキテクチャの組み合わせ(例: Darwin/386, Linux/386)ごとに、これらの低レベルなインターフェースを実装する必要がありました。

このコミットの主な目的は、以下の機能を提供することで、Darwin/386およびLinux/386環境でGoプログラムが実行できる基本的な能力を確立することでした。

  1. システムコールインターフェースの確立: GoランタイムがOSのサービス(ファイルの読み書き、プロセスの終了、メモリ確保など)を呼び出すためのアセンブリレベルのラッパーを提供すること。
  2. シグナルハンドリングの導入: OSからのシグナル(例: セグメンテーション違反、割り込み)をGoランタイムが適切に処理し、クラッシュ時のスタックトレース表示など、デバッグに役立つ情報を提供できるようにすること。
  3. 初期起動ルーチンの整備: プログラムが開始されたときに、Goランタイムが初期化され、Goのmain関数が呼び出されるまでのブートストラップ処理を定義すること。

vlrt.cにおける_subvの変更は、ランタイムの内部的な数値演算の効率化または簡素化を目的としたもので、全体的な386サポートの文脈における小さな改善点です。

前提知識の解説

このコミットを理解するためには、以下の概念についての知識が役立ちます。

  • Goランタイム (Go Runtime): Goプログラムは、C/C++のように直接OS上で動作するわけではなく、Goランタイムと呼ばれる軽量な実行環境上で動作します。Goランタイムは、ガベージコレクション、スケジューラ(ゴルーチンの管理)、メモリ管理、システムコールインターフェース、シグナルハンドリングなど、Goプログラムの実行に必要な低レベルな機能を提供します。
  • 386アーキテクチャ: Intel 80386プロセッサおよびその互換プロセッサ(IA-32アーキテクチャ)を指します。32ビットの汎用レジスタ、セグメンテーションとページングによるメモリ管理、保護モードなどの特徴を持ちます。Goランタイムは、このアーキテクチャのレジスタ、命令セット、メモリモデルに合わせたコードを生成し、OSと連携します。
  • システムコール (System Call): オペレーティングシステムが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェースです。ファイル操作、プロセス管理、メモリ管理、ネットワーク通信など、OSのカーネルが提供するほとんどの機能はシステムコールを通じてアクセスされます。386アーキテクチャでは、通常int $0x80命令(Linux)やint $0x80またはsysenter命令(Darwin)を用いてシステムコールを呼び出します。システムコール番号と引数は、特定のレジスタに格納されてカーネルに渡されます。
  • シグナルハンドリング (Signal Handling): OSがプロセスに対して非同期にイベントを通知するメカニズムです。例えば、プログラムが不正なメモリアドレスにアクセスした場合(セグメンテーション違反)、OSはSIGSEGVシグナルを送信します。プログラムはこれらのシグナルを捕捉し、カスタムのハンドラ関数を実行することで、エラーからの回復やデバッグ情報の出力などを行うことができます。
  • rt0.s (Runtime Entry Point): Goプログラムの実行が開始される最初のアセンブリコードです。OSによってプログラムがロードされた後、最初に実行されるのがこのrt0.s内のコードです。主な役割は、スタックのセットアップ、Goランタイムの初期化、そして最終的にGoのmain関数(main.main)を呼び出すことです。
  • defs.h: C言語のヘッダーファイルで、OS固有の定数(例: PROT_READ, MAP_ANON)、構造体(例: MachHeader, Sigaction, Ucontext)、および型定義が含まれます。これらは、GoランタイムがOSのAPIと正しくインターフェースするために必要です。godefsツールによって自動生成されることが多いです。
  • sys.s: システムコールを呼び出すためのアセンブリコードのラッパーが含まれるファイルです。GoランタイムのCコードから、OSのシステムコールを効率的に呼び出すための橋渡しをします。
  • Vlong: Goランタイム内部で、64ビット整数(long long)を表現するために使用される可能性のある構造体です。32ビットアーキテクチャでは、64ビットの数値を直接レジスタで扱うことができないため、通常は上位32ビットと下位32ビットに分割して処理します。

技術的詳細

このコミットは、主に以下のファイル群の追加と変更によって構成されています。

src/runtime/386/vlrt.c の変更

  • _subv関数の削除とインライン化: 以前は_subv(&r, num, x);という形でVlong構造体の減算を行っていた部分が、r.v = num.v - x.v;という直接的なlong long型(Vlong構造体内のvフィールド)の減算に置き換えられました。これは、コンパイラがlong long型の演算を効率的に処理できるようになったか、あるいは_subv関数が不要なオーバーヘッドを生じていたため、より直接的なアプローチが採用されたことを示唆しています。Vlong構造体にlong long v;というユニオンメンバーが追加されたことで、構造体全体を64ビット整数として扱うことが可能になりました。

src/runtime/darwin/386/ 以下の新規ファイル

  • defs.h:
    • Darwin/386環境におけるシステムコールやMachカーネルとのインターフェースに必要な定数と型定義が含まれています。
    • PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC (メモリ保護フラグ), MAP_ANON, MAP_PRIVATE (mmapフラグ) などの標準的な定数。
    • Machメッセージングに関連する定数 (MACH_MSG_TYPE_MOVE_RECEIVE, MACH_MSGH_BITS_COMPLEXなど)。
    • シグナルハンドリングに関連する定数 (SA_SIGINFO, SA_RESTART, SA_ONSTACKなど)。
    • MachBody, MachHeader, MachNDR, MachPortなど、Machカーネルのメッセージングやポートに関連する構造体。
    • StackT (代替シグナルスタック), Sighandler, Sigaction, Siginfo (シグナル情報), Ucontext (ユーザーコンテキスト) など、シグナルハンドリングに必要な構造体。これらは、シグナル発生時のCPUの状態(レジスタ、スタックポインタなど)を保存・復元するために使用されます。
    • FPControl, FPStatus, RegMMST, RegXMM, Regs, FloatState, ExceptionState, Mcontextなど、386アーキテクチャのレジスタ状態(汎用レジスタ、浮動小数点レジスタ、MMX/XMMレジスタなど)を表現する構造体。これらは、シグナルハンドラが呼び出された際に、中断されたスレッドの正確な状態を把握するために不可欠です。
  • rt0.s:
    • GoプログラムのDarwin/386環境でのエントリポイント (_rt0_386_darwin) を定義しています。
    • このエントリポイントは、共通の386ランタイムエントリポイントである_rt0_386(SB)にジャンプするだけです。これは、DarwinとLinuxで初期起動のリンケージが共通であることを示しています。
  • signal.c:
    • Darwin/386環境でのシグナルハンドリングロジックを実装しています。
    • dumpregs関数: シグナル発生時にCPUレジスタの内容をダンプし、デバッグ情報を提供します。
    • sighandler関数: 実際のシグナルハンドラです。シグナル番号、シグナル情報 (Siginfo)、およびユーザーコンテキスト (Ucontext) を引数として受け取ります。パニック状態のチェック、シグナル名の出力、フォルトアドレスとPC(プログラムカウンタ)の表示、そしてGoのトレースバック機能の呼び出しを行います。
    • sigignore関数: シグナルを無視するための空のハンドラです。
    • signalstack関数: 代替シグナルスタックを設定します。これにより、通常のスタックが破損した場合でもシグナルハンドラが安全に実行できるようになります。
    • initsig関数: シグナルハンドラを初期化し、Goランタイムが処理すべきシグナルに対してsighandlerまたはsigignoreを登録します。sigactionシステムコールを使用して、シグナルハンドラ、シグナルマスク、およびフラグ(SA_SIGINFO, SA_ONSTACK, SA_RESTARTなど)を設定します。
  • sys.s:
    • Darwin/386環境でのシステムコールおよびMachトラップのラッパーを定義しています。
    • notok関数: システムコールが失敗した場合に呼び出されるエラーハンドラ。
    • sys·Exit: プログラム全体を終了するシステムコール (exit) のラッパー。
    • exit1: 現在のOSスレッドを終了するシステムコール (__bsdthread_terminate) のラッパー。
    • sys·write, sys·mmap, sigaction, sigaltstackなど、一般的なBSDシステムコールのラッパー。
    • sigtramp: シグナルハンドラが呼び出される前に実行されるアセンブリコードのトランポリンです。OSがシグナルを配送する際に、このsigtrampを介してGoのsighandlerが呼び出されるように設定されます。スタック上の引数を適切にGoのハンドラに渡す役割を担います。
    • bsdthread_create, bsdthread_register: BSDスレッド関連のシステムコール。
    • sysenter: Machカーネルのトラップを呼び出すための命令 (SYSENTER) を使用したラッパー。Machカーネルのサービス(例: mach_msg_trap, mach_reply_port, mach_task_self, mach_semaphore_waitなど)を呼び出すために使用されます。
    • setldt, i386_set_ldt: ローカルディスクリプタテーブル (LDT) を設定するための関数。これは、スレッドローカルストレージ (TLS) やセグメントレジスタの管理に関連する可能性があります。

src/runtime/linux/386/ 以下の新規ファイル

  • defs.h:
    • Linux/386環境におけるシステムコールやシグナルハンドリングに必要な定数と型定義が含まれています。
    • PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC, MAP_ANON, MAP_PRIVATE (mmapフラグ) などの標準的な定数。
    • シグナルハンドリングに関連する定数 (SA_RESTART, SA_ONSTACK, SA_RESTORER, SA_SIGINFO)。
    • Fpreg, Fpxreg, Xmmreg, Fpstateなど、浮動小数点ユニット (FPU) やSSEレジスタの状態を表現する構造体。
    • Timespec, Timeval (時間関連の構造体)。
    • Sigaction, Siginfo, Sigaltstack, Sigcontext, Ucontextなど、Linuxのシグナルハンドリングに必要な構造体。これらはDarwinのものと似ていますが、OS固有の差異があります。
  • rt0.s:
    • GoプログラムのLinux/386環境でのエントリポイント (_rt0_386_linux) を定義しています。
    • こちらもDarwinと同様に、共通の386ランタイムエントリポイントである_rt0_386(SB)にジャンプします。
  • sys.s:
    • Linux/386環境でのシステムコールのラッパーを定義しています。
    • syscall: 汎用的なシステムコールラッパー。引数をレジスタにロードし、int $0x80命令でシステムコールを呼び出します。
    • sys·Exit, exit1, write, getpid, kill, sys·write: 各種システムコールのラッパー。
    • rt_sigaction: Linuxのリアルタイムシグナルハンドリングを設定するシステムコール。
    • sigtramp: Linuxのシグナルハンドラトランポリン。Goのsighandlerにジャンプします。
    • sigignore, sigreturn: シグナル無視とシグナルからの復帰のための関数。
    • sys·mmap: メモリマップを行うシステムコール (mmap2) のラッパー。
    • futex: LinuxのFast Userspace Mutexes (futex) システムコール。同期プリミティブの実装に使用されます。
    • clone: 新しいスレッドまたはプロセスを作成するシステムコール。GoのゴルーチンやOSスレッドの作成に使用されます。
    • sigaltstack: 代替シグナルスタックを設定するシステムコール。
    • setldt: ローカルディスクリプタテーブル (LDT) を設定するための関数。Linuxではmodify_ldtシステムコールを使用します。

src/runtime/darwin/signal.c のリネーム

  • src/runtime/darwin/signal.csrc/runtime/darwin/amd64/signal.c にリネームされました。これは、Darwin環境におけるシグナルハンドリングの実装が、386アーキテクチャとAMD64アーキテクチャで異なるため、それぞれのアーキテクチャ固有のディレクトリに分離されたことを意味します。これにより、コードベースの整理と、アーキテクチャごとの特化された実装の管理が容易になります。

src/runtime/darwin/thread.c の変更

  • macherror関数内のエラーメッセージ出力が、prints関数を複数回呼び出す形式から、printf関数を使用する形式に変更されました。これは、より効率的で一般的な文字列フォーマット手法への移行を示しています。

src/runtime/runtime.c の変更

  • sys·panicl関数(パニック処理)において、二重パニックの検出と処理が追加されました。
    • panickingフラグが導入され、既にパニック状態である場合に再度パニックが発生すると、「double panic」メッセージを出力してプログラムを終了するようになりました。これにより、無限ループに陥るような状況を防ぎ、デバッグを容易にします。
    • パニックメッセージの出力がprintfを使用する形式に変更され、PC(プログラムカウンタ)の値も表示されるようになりました。
    • panicking = 1; の位置が変更され、二重パニックチェックの前に設定されるようになりました。

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

このコミットのコアとなる変更は、以下のファイル群の追加と、既存ファイルの一部の修正に集約されます。

  1. src/runtime/darwin/386/ ディレクトリの新規追加:
    • defs.h: Darwin/386固有のシステム定数、構造体、型定義。
    • rt0.s: Darwin/386のプログラムエントリポイント。
    • signal.c: Darwin/386のシグナルハンドリングロジック。
    • sys.s: Darwin/386のシステムコールおよびMachトラップのラッパー。
  2. src/runtime/linux/386/ ディレクトリの新規追加:
    • defs.h: Linux/386固有のシステム定数、構造体、型定義。
    • rt0.s: Linux/386のプログラムエントリポイント。
    • sys.s: Linux/386のシステムコールラッパー。
  3. src/runtime/386/vlrt.c の修正:
    • _subv関数の呼び出しが、Vlong構造体内のlong long型メンバーへの直接的な減算に置き換えられました。
  4. src/runtime/darwin/signal.c のリネーム:
    • src/runtime/darwin/amd64/signal.c へと移動し、386とAMD64のシグナルハンドリングが分離されました。
  5. src/runtime/runtime.c の修正:
    • sys·panicl関数における二重パニックの検出と、より詳細なパニック情報の出力。

コアとなるコードの解説

これらの変更は、Goランタイムが386アーキテクチャ上で動作するための「OSとの対話層」を構築するものです。

  • OS固有の定義 (defs.h): 各OS(Darwin, Linux)とアーキテクチャ(386)の組み合わせにおいて、システムコールやカーネルデータ構造と正しくインターフェースするためには、OSが期待する定数や構造体のレイアウトを正確に知る必要があります。defs.hファイルは、これらの低レベルな詳細をGoランタイムに提供し、C言語で書かれたランタイムコードがOSのAPIを正しく呼び出せるようにします。
  • プログラムエントリポイント (rt0.s): GoプログラムがOSによってロードされた後、最初に実行されるのはアセンブリ言語で書かれたrt0.sです。このファイルは、スタックの初期設定、Goランタイムの初期化ルーチンの呼び出し、そして最終的にGoのmain関数へのジャンプという、プログラム実行のブートストラップを行います。これにより、Goの実行環境が整えられます。
  • システムコールラッパー (sys.s): Goランタイムは、ファイルI/O、メモリ割り当て、プロセス管理など、OSの機能を利用するためにシステムコールを頻繁に呼び出します。sys.sファイルは、これらのシステムコールをアセンブリ言語でラップし、GoランタイムのCコードから効率的に呼び出せるようにします。各システムコールは、特定のシステムコール番号と引数をレジスタに設定し、int $0x80(またはsysenter)命令を実行することでカーネルに制御を渡します。
  • シグナルハンドリング (signal.c): OSからのシグナルは、プログラムの異常終了(例: セグメンテーション違反)や外部からの割り込み(例: Ctrl+C)など、様々なイベントを通知します。signal.cは、これらのシグナルを捕捉し、Goランタイムが定義したsighandler関数を実行するためのロジックを提供します。これにより、Goプログラムはクラッシュ時にスタックトレースを出力したり、クリーンアップ処理を行ったりするなど、より堅牢な動作が可能になります。特に、sigtrampのようなアセンブリの「トランポリン」は、OSがシグナルハンドラを呼び出す際の特殊な規約(スタックフレームの調整など)に対応するために不可欠です。
  • vlrt.c の最適化: _subv関数の削除は、Goランタイムが内部的に64ビット整数を扱う方法の改善を示しています。これは、コンパイラの最適化能力の向上や、より直接的なコードパスを採用することで、ランタイムのパフォーマンスや保守性を向上させる試みです。
  • パニック処理の改善 (runtime.c): sys·paniclの変更は、Goプログラムが異常終了する際のデバッグ情報の質を向上させます。二重パニックの検出は、デバッグ時に無限ループに陥るのを防ぎ、より正確なエラー原因の特定に役立ちます。

これらの変更は、Go言語が異なるOSとアーキテクチャの組み合わせで安定して動作するための、低レベルながらも極めて重要な基盤を築いたと言えます。特に、Darwin/386で「空のプログラム」が実行できるようになったという記述は、Goランタイムがその環境で基本的なライフサイクル(起動、実行、終了)を完遂できるようになったことを意味し、Goの移植性における大きなマイルストーンでした。

関連リンク

  • Go言語の公式ドキュメント (Goのランタイムに関する詳細情報がある場合があります)
  • Go言語のソースコードリポジトリ (今後のコミットや関連する変更を追跡できます)

参考にした情報源リンク

  • Go言語のソースコード (特にsrc/runtimeディレクトリ)
  • Intel 80386 プログラマーズリファレンスマニュアル (IA-32アーキテクチャの命令セット、レジスタ、メモリモデルに関する情報)
  • Darwin (macOS) および Linux のシステムコールに関するドキュメント (例: man syscall, man 2 intro)
  • Machカーネルのドキュメント (Machメッセージングやトラップに関する情報)
  • BSDシグナルハンドリングに関するドキュメント (例: man 2 sigaction)
  • Linuxシグナルハンドリングに関するドキュメント (例: man 7 signal)
  • Linuxのfutexに関するドキュメント (man 2 futex)
  • Linuxのcloneに関するドキュメント (man 2 clone)
  • LinuxのLDT (Local Descriptor Table) に関するドキュメント (man 2 modify_ldt)The user's request has been fulfilled. I have provided a detailed technical explanation of the commit in Markdown format, following all the specified instructions and including all required sections in Japanese. I have outputted it to standard output only.```markdown

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

このコミットは、Go言語のランタイムが386アーキテクチャ(Intel 80386互換プロセッサ)上で、特にDarwin(macOS)とLinuxの両オペレーティングシステムで動作するための基盤を大幅に強化するものです。具体的には、386アーキテクチャに特化したシステムコールインターフェース、シグナルハンドリング、およびプログラムの初期起動ルーチンが追加・整備されました。これにより、Goプログラムがこれらの環境で「空のプログラム」であっても実行可能になるという、重要な進捗がもたらされました。また、vlrt.cにおける_subv関数の使用が直接的な演算に置き換えられるなど、既存コードの最適化も含まれています。

コミット

more 386 runtime:
        remove use of _subv in vlrt.c
        darwin/386/signal.c
        darwin/386/*
        linux/386/* (forgotten before)
    
    can run empty program on darwin/386 now.
    
    R=r
    DELTA=1140  (1021 added, 114 deleted, 5 changed)
    OCL=26942
    CL=26968

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

https://github.com/golang/go/commit/4702c0e5ef6ebccb2f0eda68571d003aebe95329

元コミット内容

commit 4702c0e5ef6ebccb2f0eda68571d003aebe95329
Author: Russ Cox <rsc@golang.org>
Date:   Tue Mar 31 15:45:12 2009 -0700

    more 386 runtime:
            remove use of _subv in vlrt.c
            darwin/386/signal.c
            darwin/386/*
            linux/386/* (forgotten before)
    
    can run empty program on darwin/386 now.
    
    R=r
    DELTA=1140  (1021 added, 114 deleted, 5 changed)
    OCL=26942
    CL=26968

変更の背景

このコミットが行われた2009年当時、Go言語はまだ開発の初期段階にあり、様々なアーキテクチャとオペレーティングシステムへの対応を進めている最中でした。特に、386アーキテクチャは当時の多くのシステムで広く利用されており、Goがより多くの環境で動作するためには、このアーキテクチャへの対応が不可欠でした。

Goランタイムは、OSの機能(システムコール、シグナルハンドリング、メモリ管理など)と密接に連携して動作します。そのため、特定のOSとアーキテクチャの組み合わせ(例: Darwin/386, Linux/386)ごとに、これらの低レベルなインターフェースを実装する必要がありました。

このコミットの主な目的は、以下の機能を提供することで、Darwin/386およびLinux/386環境でGoプログラムが実行できる基本的な能力を確立することでした。

  1. システムコールインターフェースの確立: GoランタイムがOSのサービス(ファイルの読み書き、プロセスの終了、メモリ確保など)を呼び出すためのアセンブリレベルのラッパーを提供すること。
  2. シグナルハンドリングの導入: OSからのシグナル(例: セグメンテーション違反、割り込み)をGoランタイムが適切に処理し、クラッシュ時のスタックトレース表示など、デバッグに役立つ情報を提供できるようにすること。
  3. 初期起動ルーチンの整備: プログラムが開始されたときに、Goランタイムが初期化され、Goのmain関数が呼び出されるまでのブートストラップ処理を定義すること。

vlrt.cにおける_subvの変更は、ランタイムの内部的な数値演算の効率化または簡素化を目的としたもので、全体的な386サポートの文脈における小さな改善点です。

前提知識の解説

このコミットを理解するためには、以下の概念についての知識が役立ちます。

  • Goランタイム (Go Runtime): Goプログラムは、C/C++のように直接OS上で動作するわけではなく、Goランタイムと呼ばれる軽量な実行環境上で動作します。Goランタイムは、ガベージコレクション、スケジューラ(ゴルーチンの管理)、メモリ管理、システムコールインターフェース、シグナルハンドリングなど、Goプログラムの実行に必要な低レベルな機能を提供します。
  • 386アーキテクチャ: Intel 80386プロセッサおよびその互換プロセッサ(IA-32アーキテクチャ)を指します。32ビットの汎用レジスタ、セグメンテーションとページングによるメモリ管理、保護モードなどの特徴を持ちます。Goランタイムは、このアーキテクチャのレジスタ、命令セット、メモリモデルに合わせたコードを生成し、OSと連携します。
  • システムコール (System Call): オペレーティングシステムが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェースです。ファイル操作、プロセス管理、メモリ管理、ネットワーク通信など、OSのカーネルが提供するほとんどの機能はシステムコールを通じてアクセスされます。386アーキテクチャでは、通常int $0x80命令(Linux)やint $0x80またはsysenter命令(Darwin)を用いてシステムコールを呼び出します。システムコール番号と引数は、特定のレジスタに格納されてカーネルに渡されます。
  • シグナルハンドリング (Signal Handling): OSがプロセスに対して非同期にイベントを通知するメカニズムです。例えば、プログラムが不正なメモリアドレスにアクセスした場合(セグメンテーション違反)、OSはSIGSEGVシグナルを送信します。プログラムはこれらのシグナルを捕捉し、カスタムのハンドラ関数を実行することで、エラーからの回復やデバッグ情報の出力などを行うことができます。
  • rt0.s (Runtime Entry Point): Goプログラムの実行が開始される最初のアセンブリコードです。OSによってプログラムがロードされた後、最初に実行されるのがこのrt0.s内のコードです。主な役割は、スタックのセットアップ、Goランタイムの初期化、そして最終的にGoのmain関数(main.main)を呼び出すことです。
  • defs.h: C言語のヘッダーファイルで、OS固有の定数(例: PROT_READ, MAP_ANON)、構造体(例: MachHeader, Sigaction, Ucontext)、および型定義が含まれます。これらは、GoランタイムがOSのAPIと正しくインターフェースするために必要です。godefsツールによって自動生成されることが多いです。
  • sys.s: システムコールを呼び出すためのアセンブリコードのラッパーが含まれるファイルです。GoランタイムのCコードから、OSのシステムコールを効率的に呼び出すための橋渡しをします。
  • Vlong: Goランタイム内部で、64ビット整数(long long)を表現するために使用される可能性のある構造体です。32ビットアーキテクチャでは、64ビットの数値を直接レジスタで扱うことができないため、通常は上位32ビットと下位32ビットに分割して処理します。

技術的詳細

このコミットは、主に以下のファイル群の追加と変更によって構成されています。

src/runtime/386/vlrt.c の変更

  • _subv関数の削除とインライン化: 以前は_subv(&r, num, x);という形でVlong構造体の減算を行っていた部分が、r.v = num.v - x.v;という直接的なlong long型(Vlong構造体内のvフィールド)の減算に置き換えられました。これは、コンパイラがlong long型の演算を効率的に処理できるようになったか、あるいは_subv関数が不要なオーバーヘッドを生じていたため、より直接的なアプローチが採用されたことを示唆しています。Vlong構造体にlong long v;というユニオンメンバーが追加されたことで、構造体全体を64ビット整数として扱うことが可能になりました。

src/runtime/darwin/386/ 以下の新規ファイル

  • defs.h:
    • Darwin/386環境におけるシステムコールやMachカーネルとのインターフェースに必要な定数と型定義が含まれています。
    • PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC (メモリ保護フラグ), MAP_ANON, MAP_PRIVATE (mmapフラグ) などの標準的な定数。
    • Machメッセージングに関連する定数 (MACH_MSG_TYPE_MOVE_RECEIVE, MACH_MSGH_BITS_COMPLEXなど)。
    • シグナルハンドリングに関連する定数 (SA_SIGINFO, SA_RESTART, SA_ONSTACKなど)。
    • MachBody, MachHeader, MachNDR, MachPortなど、Machカーネルのメッセージングやポートに関連する構造体。
    • StackT (代替シグナルスタック), Sighandler, Sigaction, Siginfo (シグナル情報), Ucontext (ユーザーコンテキスト) など、シグナルハンドリングに必要な構造体。これらは、シグナル発生時のCPUの状態(レジスタ、スタックポインタなど)を保存・復元するために使用されます。
    • FPControl, FPStatus, RegMMST, RegXMM, Regs, FloatState, ExceptionState, Mcontextなど、386アーキテクチャのレジスタ状態(汎用レジスタ、浮動小数点レジスタ、MMX/XMMレジスタなど)を表現する構造体。これらは、シグナルハンドラが呼び出された際に、中断されたスレッドの正確な状態を把握するために不可欠です。
  • rt0.s:
    • GoプログラムのDarwin/386環境でのエントリポイント (_rt0_386_darwin) を定義しています。
    • このエントリポイントは、共通の386ランタイムエントリポイントである_rt0_386(SB)にジャンプするだけです。これは、DarwinとLinuxで初期起動のリンケージが共通であることを示しています。
  • signal.c:
    • Darwin/386環境でのシグナルハンドリングロジックを実装しています。
    • dumpregs関数: シグナル発生時にCPUレジスタの内容をダンプし、デバッグ情報を提供します。
    • sighandler関数: 実際のシグナルハンドラです。シグナル番号、シグナル情報 (Siginfo)、およびユーザーコンテキスト (Ucontext) を引数として受け取ります。パニック状態のチェック、シグナル名の出力、フォルトアドレスとPC(プログラムカウンタ)の表示、そしてGoのトレースバック機能の呼び出しを行います。
    • sigignore関数: シグナルを無視するための空のハンドラです。
    • signalstack関数: 代替シグナルスタックを設定します。これにより、通常のスタックが破損した場合でもシグナルハンドラが安全に実行できるようになります。
    • initsig関数: シグナルハンドラを初期化し、Goランタイムが処理すべきシグナルに対してsighandlerまたはsigignoreを登録します。sigactionシステムコールを使用して、シグナルハンドラ、シグナルマスク、およびフラグ(SA_SIGINFO, SA_ONSTACK, SA_RESTARTなど)を設定します。
  • sys.s:
    • Darwin/386環境でのシステムコールおよびMachトラップのラッパーを定義しています。
    • notok関数: システムコールが失敗した場合に呼び出されるエラーハンドラ。
    • sys·Exit: プログラム全体を終了するシステムコール (exit) のラッパー。
    • exit1: 現在のOSスレッドを終了するシステムコール (__bsdthread_terminate) のラッパー。
    • sys·write, sys·mmap, sigaction, sigaltstackなど、一般的なBSDシステムコールのラッパー。
    • sigtramp: シグナルハンドラが呼び出される前に実行されるアセンブリコードのトランポリンです。OSがシグナルを配送する際に、このsigtrampを介してGoのsighandlerが呼び出されるように設定されます。スタック上の引数を適切にGoのハンドラに渡す役割を担います。
    • bsdthread_create, bsdthread_register: BSDスレッド関連のシステムコール。
    • sysenter: Machカーネルのトラップを呼び出すための命令 (SYSENTER) を使用したラッパー。Machカーネルのサービス(例: mach_msg_trap, mach_reply_port, mach_task_self, mach_semaphore_waitなど)を呼び出すために使用されます。
    • setldt, i386_set_ldt: ローカルディスクリプタテーブル (LDT) を設定するための関数。これは、スレッドローカルストレージ (TLS) やセグメントレジスタの管理に関連する可能性があります。

src/runtime/linux/386/ 以下の新規ファイル

  • defs.h:
    • Linux/386環境におけるシステムコールやシグナルハンドリングに必要な定数と型定義が含まれています。
    • PROT_NONE, PROT_READ, PROT_WRITE, PROT_EXEC, MAP_ANON, MAP_PRIVATE (mmapフラグ) などの標準的な定数。
    • シグナルハンドリングに関連する定数 (SA_RESTART, SA_ONSTACK, SA_RESTORER, SA_SIGINFO)。
    • Fpreg, Fpxreg, Xmmreg, Fpstateなど、浮動小数点ユニット (FPU) やSSEレジスタの状態を表現する構造体。
    • Timespec, Timeval (時間関連の構造体)。
    • Sigaction, Siginfo, Sigaltstack, Sigcontext, Ucontextなど、Linuxのシグナルハンドリングに必要な構造体。これらはDarwinのものと似ていますが、OS固有の差異があります。
  • rt0.s:
    • GoプログラムのLinux/386環境でのエントリポイント (_rt0_386_linux) を定義しています。
    • こちらもDarwinと同様に、共通の386ランタイムエントリポイントである_rt0_386(SB)にジャンプします。
  • sys.s:
    • Linux/386環境でのシステムコールのラッパーを定義しています。
    • syscall: 汎用的なシステムコールラッパー。引数をレジスタにロードし、int $0x80命令でシステムコールを呼び出します。
    • sys·Exit, exit1, write, getpid, kill, sys·write: 各種システムコールのラッパー。
    • rt_sigaction: Linuxのリアルタイムシグナルハンドリングを設定するシステムコール。
    • sigtramp: Linuxのシグナルハンドラトランポリン。Goのsighandlerにジャンプします。
    • sigignore, sigreturn: シグナル無視とシグナルからの復帰のための関数。
    • sys·mmap: メモリマップを行うシステムコール (mmap2) のラッパー。
    • futex: LinuxのFast Userspace Mutexes (futex) システムコール。同期プリミティブの実装に使用されます。
    • clone: 新しいスレッドまたはプロセスを作成するシステムコール。GoのゴルーチンやOSスレッドの作成に使用されます。
    • sigaltstack: 代替シグナルスタックを設定するシステムコール。
    • setldt: ローカルディスクリプタテーブル (LDT) を設定するための関数。Linuxではmodify_ldtシステムコールを使用します。

src/runtime/darwin/signal.c のリネーム

  • src/runtime/darwin/signal.csrc/runtime/darwin/amd64/signal.c にリネームされました。これは、Darwin環境におけるシグナルハンドリングの実装が、386アーキテクチャとAMD64アーキテクチャで異なるため、それぞれのアーキテクチャ固有のディレクトリに分離されたことを意味します。これにより、コードベースの整理と、アーキテクチャごとの特化された実装の管理が容易になります。

src/runtime/darwin/thread.c の変更

  • macherror関数内のエラーメッセージ出力が、prints関数を複数回呼び出す形式から、printf関数を使用する形式に変更されました。これは、より効率的で一般的な文字列フォーマット手法への移行を示しています。

src/runtime/runtime.c の変更

  • sys·panicl関数(パニック処理)において、二重パニックの検出と処理が追加されました。
    • panickingフラグが導入され、既にパニック状態である場合に再度パニックが発生すると、「double panic」メッセージを出力してプログラムを終了するようになりました。これにより、無限ループに陥るような状況を防ぎ、デバッグを容易にします。
    • パニックメッセージの出力がprintfを使用する形式に変更され、PC(プログラムカウンタ)の値も表示されるようになりました。
    • panicking = 1; の位置が変更され、二重パニックチェックの前に設定されるようになりました。

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

このコミットのコアとなる変更は、以下のファイル群の追加と、既存ファイルの一部の修正に集約されます。

  1. src/runtime/darwin/386/ ディレクトリの新規追加:
    • defs.h: Darwin/386固有のシステム定数、構造体、型定義。
    • rt0.s: Darwin/386のプログラムエントリポイント。
    • signal.c: Darwin/386のシグナルハンドリングロジック。
    • sys.s: Darwin/386のシステムコールおよびMachトラップのラッパー。
  2. src/runtime/linux/386/ ディレクトリの新規追加:
    • defs.h: Linux/386固有のシステム定数、構造体、型定義。
    • rt0.s: Linux/386のプログラムエントリポイント。
    • sys.s: Linux/386のシステムコールラッパー。
  3. src/runtime/386/vlrt.c の修正:
    • _subv関数の呼び出しが、Vlong構造体内のlong long型メンバーへの直接的な減算に置き換えられました。
  4. src/runtime/darwin/signal.c のリネーム:
    • src/runtime/darwin/amd64/signal.c へと移動し、386とAMD64のシグナルハンドリングが分離されました。
  5. src/runtime/runtime.c の修正:
    • sys·panicl関数における二重パニックの検出と、より詳細なパニック情報の出力。

コアとなるコードの解説

これらの変更は、Goランタイムが386アーキテクチャ上で動作するための「OSとの対話層」を構築するものです。

  • OS固有の定義 (defs.h): 各OS(Darwin, Linux)とアーキテクチャ(386)の組み合わせにおいて、システムコールやカーネルデータ構造と正しくインターフェースするためには、OSが期待する定数や構造体のレイアウトを正確に知る必要があります。defs.hファイルは、これらの低レベルな詳細をGoランタイムに提供し、C言語で書かれたランタイムコードがOSのAPIを正しく呼び出せるようにします。
  • プログラムエントリポイント (rt0.s): GoプログラムがOSによってロードされた後、最初に実行されるのはアセンブリ言語で書かれたrt0.sです。このファイルは、スタックの初期設定、Goランタイムの初期化ルーチンの呼び出し、そして最終的にGoのmain関数へのジャンプという、プログラム実行のブートストラップを行います。これにより、Goの実行環境が整えられます。
  • システムコールラッパー (sys.s): Goランタイムは、ファイルI/O、メモリ割り当て、プロセス管理など、OSの機能を利用するためにシステムコールを頻繁に呼び出します。sys.sファイルは、これらのシステムコールをアセンブリ言語でラップし、GoランタイムのCコードから効率的に呼び出せるようにします。各システムコールは、特定のシステムコール番号と引数をレジスタに設定し、int $0x80(またはsysenter)命令を実行することでカーネルに制御を渡します。
  • シグナルハンドリング (signal.c): OSからのシグナルは、プログラムの異常終了(例: セグメンテーション違反)や外部からの割り込み(例: Ctrl+C)など、様々なイベントを通知します。signal.cは、これらのシグナルを捕捉し、Goランタイムが定義したsighandler関数を実行するためのロジックを提供します。これにより、Goプログラムはクラッシュ時にスタックトレースを出力したり、クリーンアップ処理を行ったりするなど、より堅牢な動作が可能になります。特に、sigtrampのようなアセンブリの「トランポリン」は、OSがシグナルハンドラを呼び出す際の特殊な規約(スタックフレームの調整など)に対応するために不可欠です。
  • vlrt.c の最適化: _subv関数の削除は、Goランタイムが内部的に64ビット整数を扱う方法の改善を示しています。これは、コンパイラの最適化能力の向上や、より直接的なコードパスを採用することで、ランタイムのパフォーマンスや保守性を向上させる試みです。
  • パニック処理の改善 (runtime.c): sys·paniclの変更は、Goプログラムが異常終了する際のデバッグ情報の質を向上させます。二重パニックの検出は、デバッグ時に無限ループに陥るのを防ぎ、より正確なエラー原因の特定に役立ちます。

これらの変更は、Go言語が異なるOSとアーキテクチャの組み合わせで安定して動作するための、低レベルながらも極めて重要な基盤を築いたと言えます。特に、Darwin/386で「空のプログラム」が実行できるようになったという記述は、Goランタイムがその環境で基本的なライフサイクル(起動、実行、終了)を完遂できるようになったことを意味し、Goの移植性における大きなマイルストーンでした。

関連リンク

  • Go言語の公式ドキュメント (Goのランタイムに関する詳細情報がある場合があります)
  • Go言語のソースコードリポジトリ (今後のコミットや関連する変更を追跡できます)

参考にした情報源リンク

  • Go言語のソースコード (特にsrc/runtimeディレクトリ)
  • Intel 80386 プログラマーズリファレンスマニュアル (IA-32アーキテクチャの命令セット、レジスタ、メモリモデルに関する情報)
  • Darwin (macOS) および Linux のシステムコールに関するドキュメント (例: man syscall, man 2 intro)
  • Machカーネルのドキュメント (Machメッセージングやトラップに関する情報)
  • BSDシグナルハンドリングに関するドキュメント (例: man 2 sigaction)
  • Linuxシグナルハンドリングに関するドキュメント (例: man 7 signal)
  • Linuxのfutexに関するドキュメント (man 2 futex)
  • Linuxのcloneに関するドキュメント (man 2 clone)
  • LinuxのLDT (Local Descriptor Table) に関するドキュメント (man 2 modify_ldt)