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

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

このコミットは、Go言語のランタイムとシステムコールライブラリに、Plan 9オペレーティングシステムの64ビット版(AMD64アーキテクチャ)のサポートを追加するものです。これにより、GoプログラムがPlan 9のAMD64環境でネイティブに動作できるようになります。

コミット

commit a72bebf6e1a5eafd8347fb84e60155e2b9cdb6d7
Author: Akshat Kumar <seed@mail.nanosouffle.net>
Date:   Fri Aug 31 13:21:13 2012 -0400

    src: Add support for 64-bit version of Plan 9
    
    This set of changes extends the Plan 9 support
    to include the AMD64 architecture and should
    work on all versions of Plan 9.
    
    R=golang-dev, rminnich, noah.evans, rsc, minux.ma, npe
    CC=akskuma, golang-dev, jfflore, noah.evans
    https://golang.org/cl/6479052

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

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

元コミット内容

「src: Add support for 64-bit version of Plan 9」 この変更は、Plan 9のサポートをAMD64アーキテクチャに拡張し、すべてのバージョンのPlan 9で動作するはずです。

変更の背景

Go言語は、その設計当初からクロスプラットフォーム対応を重視しており、様々なオペレーティングシステムやCPUアーキテクチャで動作することを目標としています。Plan 9は、ベル研究所で開発された分散オペレーティングシステムであり、Go言語の開発者の一部がその設計思想に影響を受けていることでも知られています。

このコミットが行われた2012年当時、Goは既にPlan 9の32ビット版(386アーキテクチャ)をサポートしていましたが、64ビット版(AMD64アーキテクチャ)のサポートは不足していました。64ビット環境でのGoプログラムの実行を可能にすることは、より広範なハードウェアでのGoの利用を促進し、パフォーマンスの向上やより大きなメモリ空間の利用といったメリットをもたらします。

このコミットの目的は、GoランタイムがPlan 9のAMD64環境で適切に動作するために必要な、低レベルのアセンブリコード、システムコールインターフェース、および関連する定義を実装することにあります。これにより、Go開発者はPlan 9の64ビット環境でもGoアプリケーションをビルドし、実行できるようになります。

前提知識の解説

  • Plan 9 (プラン・ナイン): ベル研究所で開発された分散オペレーティングシステム。Unixの設計思想をさらに推し進め、すべてのリソースをファイルとして表現し、ネットワーク透過性を重視しています。Go言語の開発者の一部(Rob Pike, Ken Thompsonなど)がPlan 9の開発にも携わっていたため、Go言語の設計にもその影響が見られます。
  • AMD64アーキテクチャ: x86-64とも呼ばれる64ビットの命令セットアーキテクチャ。現在のほとんどのデスクトップPCやサーバーで使用されています。32ビットアーキテクチャと比較して、より大きなメモリ空間を扱え、より多くの汎用レジスタを持つため、パフォーマンスの向上が期待できます。
  • Goランタイム (Go Runtime): Goプログラムの実行を管理する部分。ガベージコレクション、スケジューリング、システムコールインターフェース、メモリ管理など、Goプログラムが動作するために必要な低レベルの機能を提供します。OSやアーキテクチャごとに異なる実装が必要です。
  • システムコール (System Call): オペレーティングシステムが提供するサービスを、ユーザー空間のプログラムが利用するためのインターフェース。ファイルI/O、メモリ管理、プロセス管理など、OSのカーネルが提供する機能にアクセスするために使用されます。各OS、各アーキテクチャでシステムコールの呼び出し規約や番号が異なります。
  • mksyscall.pl: Go言語のsyscallパッケージで、OS固有のシステムコールラッパーコードを自動生成するためのPerlスクリプト。OSのシステムコール定義ファイル(C言語のヘッダファイルなど)を解析し、Goのsyscallパッケージが利用できるGoコードとアセンブリコードを生成します。これにより、手動でシステムコールラッパーを記述する手間を省き、エラーを減らします。
  • godefs: Go言語のツールで、C言語の構造体や定数定義をGoの構造体や定数に変換するために使用されます。OS固有のデータ構造をGoプログラムで扱う際に利用されます。
  • SYSCALL命令: x86-64アーキテクチャにおけるシステムコールを呼び出すためのCPU命令。この命令を実行すると、CPUは特権レベルをカーネルモードに切り替え、OSのシステムコールハンドラに制御を移します。
  • TLS (Thread Local Storage): スレッドごとに独立したデータを保持するためのメカニズム。Goランタイムでは、ゴルーチン(Goの軽量スレッド)のコンテキスト情報などをTLSに格納することがあります。OSによってはTLSのセットアップ方法が異なります。

技術的詳細

このコミットは、Go言語がPlan 9のAMD64環境で動作するために、以下の主要な技術的側面に対処しています。

  1. アーキテクチャ固有の型定義:

    • include/plan9/amd64/u.h に、Plan 9のAMD64環境における基本的な整数型(int8, uint8, int16, uint16, int32, uint32, int64, uint64)のtypedefが追加されています。これらは、GoのランタイムやシステムコールがPlan 9のC言語の型と正しく連携するために必要です。
    • src/pkg/runtime/defs_plan9_amd64.h では、Plan 9 AMD64固有の定数(例: PAGESIZE)が定義されています。
  2. リンカの変更 (src/cmd/6l/pass.c):

    • Goのリンカ(6lはAMD64アーキテクチャ用のリンカ)が、Plan 9 AMD64バイナリを生成する際に、特定のレジスタ(FSレジスタ)の使用を認識するように変更されています。LinuxやFreeBSDなどのELFベースのシステムと同様に、Plan 9 AMD64でもFSレジスタがTLS(Thread Local Storage)のベースアドレスを指すために使用されることがあります。
  3. ランタイムの変更 (src/pkg/runtime/):

    • TLSセットアップのスキップ: src/pkg/runtime/asm_amd64.s では、Plan 9環境ではTLSのセットアップをスキップするロジックが追加されています。これは、Plan 9が他のUnix系OSとは異なるTLSの扱い方をするためです。
    • メモリ管理 (mem_plan9.c): runtime·SysAlloc 関数が、PAGESIZE定数を使用してメモリのアラインメントを行うように変更されています。これにより、Plan 9のメモリページサイズに合わせた効率的なメモリ割り当てが可能になります。
    • システムコールラッパーのアセンブリコード: src/pkg/runtime/sys_plan9_amd64.s には、Plan 9のシステムコールを呼び出すためのアセンブリラッパーが多数追加されています。これらは、Goランタイムが直接Plan 9カーネルの機能(open, pread, pwrite, close, exits, brk_, sleep, semacquire, semrelease, rforkなど)を呼び出すために使用されます。Plan 9のシステムコールは、AXレジスタに0x8000を設定し、BPレジスタにシステムコール番号を設定してからSYSCALL命令を実行するという独特の規約を持っています。
    • エントリポイント (rt0_plan9_amd64.s): Goプログラムの初期化エントリポイントである_rt0_amd64_plan9が追加され、Plan 9環境でのGoランタイムの初期化フローを定義しています。
  4. システムコールパッケージの変更 (src/pkg/syscall/):

    • AMD64固有のシステムコール定義: src/pkg/syscall/zsysnum_plan9_amd64.go には、Plan 9 AMD64アーキテクチャで利用可能なシステムコール番号の定数が定義されています。これらはmksysnum_plan9.shスクリプトによって生成されます。
    • システムコールラッパーのGoコード: src/pkg/syscall/zsyscall_plan9_amd64.go には、mksyscall.plスクリプトによって生成された、Go言語からPlan 9のシステムコールを呼び出すためのGoラッパー関数が多数含まれています。これらの関数は、Goの引数をPlan 9のシステムコール規約に合わせて変換し、アセンブリラッパーを介してカーネルを呼び出します。
    • エラー定義と定数: src/pkg/syscall/zerrors_plan9_amd64.go には、Plan 9固有のエラー定数(例: EINVAL, ENOENT)や、ファイル操作に関する定数(例: O_CREAT, S_IFREG)が定義されています。
    • 型定義: src/pkg/syscall/ztypes_plan9_amd64.go には、godefsツールによって生成された、Plan 9のシステムコールが使用するC言語の構造体や定数をGoの型にマッピングした定義が含まれています。
    • Getpagesize()のアーキテクチャ固有化: syscall_plan9.go からGetpagesize()関数が削除され、syscall_plan9_386.gosyscall_plan9_amd64.go にそれぞれ32ビットと64ビットのページサイズを返す実装が移動されました。これにより、アーキテクチャごとに正しいページサイズが返されるようになります。

これらの変更は、Goのクロスコンパイルとマルチアーキテクチャサポートの仕組みに深く統合されており、特定のOS/アーキテクチャの組み合わせに対して、ランタイム、リンカ、システムコール層で必要な調整を行う典型的な例を示しています。

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

このコミットでは、主に以下のファイル群が変更されています。

  • include/plan9/amd64/u.h (新規): Plan 9 AMD64向けの基本型定義。
  • src/cmd/6l/pass.c (変更): Goリンカの変更。Plan 9 AMD64のバイナリ生成時にFSレジスタの扱いを調整。
  • src/pkg/runtime/asm_amd64.s (変更): AMD64アセンブリコード。Plan 9でのTLSセットアップをスキップ。
  • src/pkg/runtime/defs_plan9_386.h (変更): Plan 9 386向けの定義。PAGESIZEを追加。
  • src/pkg/runtime/defs_plan9_amd64.h (新規): Plan 9 AMD64向けの定義。tos_pidPAGESIZEを定義。
  • src/pkg/runtime/mem_plan9.c (変更): Plan 9のメモリ管理。SysAllocPAGESIZEを使用。
  • src/pkg/runtime/os_plan9.h (変更): Plan 9 OS固有のランタイム関数宣言。brk_の戻り値型をintptrに変更。
  • src/pkg/runtime/rt0_plan9_amd64.s (新規): Plan 9 AMD64向けのGoプログラムエントリポイント。
  • src/pkg/runtime/signal_plan9_amd64.c (新規): Plan 9 AMD64向けのシグナルハンドリング(未実装部分あり)。
  • src/pkg/runtime/sys_plan9_amd64.s (新規): Plan 9 AMD64向けの低レベルシステムコールアセンブリラッパー。
  • src/pkg/syscall/asm_plan9_amd64.s (新規): Plan 9 AMD64向けのsyscallパッケージのアセンブリラッパー。
  • src/pkg/syscall/syscall_plan9.go (変更): 汎用Plan 9システムコール定義からGetpagesizeを削除。
  • src/pkg/syscall/syscall_plan9_386.go (変更): Plan 9 386向けのGetpagesizeを追加。
  • src/pkg/syscall/syscall_plan9_amd64.go (新規): Plan 9 AMD64向けのGetpagesizeを追加。
  • src/pkg/syscall/zerrors_plan9_amd64.go (新規): Plan 9 AMD64向けのエラー定数とファイルモード定数。
  • src/pkg/syscall/zsyscall_plan9_amd64.go (新規): mksyscall.plによって生成されたPlan 9 AMD64システムコールラッパー。
  • src/pkg/syscall/zsysnum_plan9_amd64.go (新規): mksysnum_plan9.shによって生成されたPlan 9 AMD64システムコール番号。
  • src/pkg/syscall/ztypes_plan9_amd64.go (新規): godefsによって生成されたPlan 9 AMD64のC言語型定義のGoマッピング。

これらのファイルは、GoプログラムがPlan 9の64ビット環境で動作するために必要な、低レベルのOSインターフェース、システムコール、メモリ管理、および型定義を網羅しています。

コアとなるコードの解説

src/cmd/6l/pass.c の変更

--- a/src/cmd/6l/pass.c
+++ b/src/cmd/6l/pass.c
@@ -310,7 +310,8 @@ patch(void)
 			}
 		}
 		if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd
-		|| HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd) {
+		|| HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd
+		|| HEADTYPE == Hplan9x64) {
 			// ELF uses FS instead of GS.
 			if(p->from.type == D_INDIR+D_GS)
 			p->from.type = D_INDIR+D_FS;
@@ -444,7 +445,8 @@ dostkoff(void)
 			p = appendp(p);	// load g into CX
 			p->as = AMOVQ;
 			if(HEADTYPE == Hlinux || HEADTYPE == Hfreebsd
-			|| HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd)	// ELF uses FS
+			|| HEADTYPE == Hopenbsd || HEADTYPE == Hnetbsd
+			|| HEADTYPE == Hplan9x64)	// ELF uses FS
 			p->from.type = D_INDIR+D_FS;
 			else
 			p->from.type = D_INDIR+D_GS;

この変更は、Goのリンカ(6l)が、Plan 9の64ビット版(Hplan9x64)を他のUnix系OS(Linux, FreeBSDなど)と同様に扱うように拡張しています。具体的には、GSレジスタではなくFSレジスタをTLS(Thread Local Storage)のベースアドレスとして使用するよう指示しています。これは、Goランタイムがゴルーチン固有のデータにアクセスする際に、OSが提供するTLSメカニズムを正しく利用するために重要です。

src/pkg/runtime/asm_amd64.s の変更

--- a/src/pkg/runtime/asm_amd64.s
+++ b/src/pkg/runtime/asm_amd64.s
@@ -31,6 +31,10 @@ TEXT _rt0_amd64(SB),7,$-8
 	JEQ ok
 
 needtls:
+// skip TLS setup on Plan 9
+	CMPL	runtime·isplan9(SB), $1
+	JEQ ok
+
 	LEAQ	runtime·tls0(SB), DI
 	CALL	runtime·settls(SB)

_rt0_amd64はGoプログラムの初期エントリポイントの一つです。この変更は、Plan 9環境の場合(runtime·isplan9が1の場合)、TLS(Thread Local Storage)のセットアップをスキップするように指示しています。これは、Plan 9が他のOSとは異なるTLSの管理方法を持っているため、GoランタイムがPlan 9の規約に合わせるための調整です。

src/pkg/runtime/defs_plan9_amd64.h (新規)

// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// nothing to see here
#define tos_pid 64
#define PAGESIZE 0x200000ULL

このファイルは、Plan 9の64ビット環境に特化した定義を提供します。

  • tos_pid 64: tos(Thread of System)構造体内のプロセスIDのオフセットを示している可能性があります。
  • PAGESIZE 0x200000ULL: Plan 9 AMD64環境におけるメモリページサイズを定義しています。0x200000は2MB(2 * 1024 * 1024バイト)であり、これは一般的な64ビットシステムにおける大きなページサイズ(huge page)に相当します。Goランタイムのメモリ管理がこのページサイズを考慮して動作するようにします。

src/pkg/runtime/sys_plan9_amd64.s (新規)

// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include "defs_GOOS_GOARCH.h"
#include "zasm_GOOS_GOARCH.h"

// setldt(int entry, int address, int limit)
TEXT runtime·setldt(SB),7,$0
	RET

TEXT runtime·open(SB),7,$0
	MOVQ	$0x8000, AX
	MOVQ	$14, BP
	SYSCALL
	RET

// ... (他のシステムコールラッパー) ...

TEXT runtime·rfork(SB),7,$0
	MOVQ	$0x8000, AX
	MOVQ	$19, BP // rfork
	SYSCALL

	// In parent, return.
	CMPQ	AX, $0
	JEQ	2(PC)
	RET

	// In child on forked stack.
	MOVQ	mm+24(SP), BX	// m
	MOVQ	gg+32(SP), DX	// g
	MOVQ	fn+40(SP), SI	// fn

	// set SP to be on the new child stack
	MOVQ	stack+16(SP), CX
	MOVQ	CX, SP

	// Initialize m, g.
	get_tls(AX)
	MOVQ	DX, g(AX)
	MOVQ	BX, m(AX)

	// Initialize AX from _tos->pid
	MOVQ	_tos(SB), AX
	MOVQ	tos_pid(AX), AX
	MOVQ	AX, m_procid(BX)	// save pid as m->procid
	
	CALL	runtime·stackcheck(SB)	// smashes AX, CX
	
	MOVQ	0(DX), DX	// paranoia; check they are not nil
	MOVQ	0(BX), BX
	
	CALL	SI	// fn()
	CALL	runtime·exit(SB)
	RET

// This is needed by asm_amd64.s
TEXT runtime·settls(SB),7,$0
	RET

このファイルは、GoランタイムがPlan 9のシステムコールを直接呼び出すためのアセンブリラッパーを定義しています。

  • MOVQ $0x8000, AX: Plan 9のシステムコール規約では、AXレジスタに0x8000を設定する必要があります。
  • MOVQ $XX, BP: BPレジスタにはシステムコール番号を設定します。例えば、openシステムコールは番号14です。
  • SYSCALL: 実際のシステムコールを実行する命令です。
  • runtime·rfork: Plan 9のrforkシステムコールは、プロセスをフォークし、親と子で異なる処理を行うための複雑なロジックを含んでいます。このアセンブリコードは、rforkが成功した後に、子プロセス側で新しいスタックポインタを設定し、Goのm(マシン)とg(ゴルーチン)構造体を初期化し、指定された関数(fn)を呼び出すというGoランタイムの規約に沿った処理を行っています。

src/pkg/syscall/asm_plan9_amd64.s (新規)

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

//
// System call support for Plan 9
//

//func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err string)
//func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err string)
//func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
//func RawSyscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)

// Trap # in BP, args on stack above caller pc.
// NxM requires that Plan 9 system calls be
// marked with $0x8000 in AX.
TEXT	·Syscall(SB),7,$0
	CALL	runtime·entersyscall(SB)
	MOVQ	$0x8000, AX	// for NxM
	MOVQ	8(SP), BP	// syscall entry
	// slide args down on top of system call number
	LEAQ	16(SP), SI
	LEAQ	8(SP), DI
	CLD
	MOVSQ
	MOVSQ
	MOVSQ
	SYSCALL
	MOVQ	AX, r1+40(SP)
	MOVQ	$0, r2+48(SP)
	CMPQ	AX, $-1
	JNE	ok3

	SUBQ	$16, SP
	CALL	syscall·errstr(SB)
	MOVQ	SP, SI
	ADDQ	$16, SP
	JMP	copyresult3
	
ok3:
	LEAQ	runtime·emptystring(SB), SI	
	
copyresult3:
	LEAQ	err+56(SP), DI

	CLD
	MOVSQ
	MOVSQ

	CALL	runtime·exitsyscall(SB)
	RET

// ... (RawSyscall, Syscall6, RawSyscall6, seek, exit など) ...

このファイルは、Goのsyscallパッケージが提供する汎用システムコール呼び出し関数(Syscall, Syscall6, RawSyscall, RawSyscall6)のPlan 9 AMD64向けアセンブリ実装です。

  • CALL runtime·entersyscall(SB) / CALL runtime·exitsyscall(SB): Goランタイムのスケジューラに、システムコールに入る前と出た後に通知するための呼び出しです。これにより、スケジューラはゴルーチンの状態を適切に管理できます。
  • MOVQ $0x8000, AX: ここでもPlan 9のシステムコール規約に従い、AXレジスタに0x8000を設定しています。
  • MOVQ 8(SP), BP: スタックからシステムコール番号をBPレジスタにロードします。
  • 引数のスライド: スタック上の引数をシステムコール番号の上に移動させることで、Plan 9のシステムコール規約に合わせたスタックレイアウトを構築しています。
  • エラーハンドリング: システムコールがエラーを返した場合(AX-1の場合)、syscall·errstrを呼び出してエラー文字列を取得し、Goのerror型に変換するロジックが含まれています。

src/pkg/syscall/syscall_plan9_amd64.go (新規)

// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package syscall

func Getpagesize() int { return 0x200000 }

このファイルは、Plan 9の64ビット環境におけるGetpagesize()関数の実装を提供します。0x200000は2MBであり、defs_plan9_amd64.hで定義されたPAGESIZEと一致します。これにより、Goプログラムは実行時に正しいページサイズを取得できます。

src/pkg/syscall/zsyscall_plan9_amd64.go (新規)

// mksyscall.pl -l32 -plan9 syscall_plan9.go syscall_plan9_386.go
// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT

package syscall

import "unsafe"

// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT

func exits(msg *byte) {
	Syscall(SYS_EXITS, uintptr(unsafe.Pointer(msg)), 0, 0)
	return
}

// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT

func fd2path(fd int, buf []byte) (err error) {
	var _p0 unsafe.Pointer
	if len(buf) > 0 {
		_p0 = unsafe.Pointer(&buf[0])
	} else {
		_p0 = unsafe.Pointer(&_zero)
	}
	r0, _, e1 := Syscall(SYS_FD2PATH, uintptr(fd), uintptr(_p0), uintptr(len(buf)))
	if int(r0) == -1 {
		err = e1
	}
	return
}

// ... (他のシステムコールラッパー) ...

このファイルは、mksyscall.plスクリプトによって自動生成された、Go言語からPlan 9の個々のシステムコールを呼び出すためのGoラッパー関数群です。例えば、exits関数はSYS_EXITSシステムコールを、fd2path関数はSYS_FD2PATHシステムコールを呼び出します。これらの関数は、Goの型をuintptrに変換し、syscallパッケージのアセンブリラッパー(SyscallまたはSyscall6)を介して実際のシステムコールを実行します。

関連リンク

参考にした情報源リンク