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

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

このコミットは、Go言語のsyscallパッケージにnacl/armアーキテクチャのサポートを導入するものです。具体的には、Native Client (NaCl) 環境でARMプロセッサ上で動作するGoプログラムが、システムコールを適切に実行できるようにするためのアセンブリコード、Goの型定義、および自動生成されたシステムコールラッパーを追加しています。

コミット

commit 837bc4e502e324abe062999fea1a8a07d0f0bdb8
Author: Shenghou Ma <minux@golang.org>
Date:   Thu Jul 10 15:15:06 2014 -0400

    syscall: nacl/arm support.

    LGTM=dave, rsc
    R=rsc, iant, dave
    CC=golang-codereviews
    https://golang.org/cl/101620043

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

https://github.com/golang/go/commit/837bc4e502e324abe062999fea1a8a07d0f0bdb8

元コミット内容

このコミットでは、以下の4つの新しいファイルが追加されています。

  • src/pkg/syscall/asm_nacl_arm.s: このアセンブリファイルは、nacl/armアーキテクチャにおける低レベルなシステムコールエントリポイントを提供します。Goランタイムがシステムコールを行う際に呼び出すsyscall·Syscall関数がここで定義されており、Native Clientサンドボックスへの引数渡しと戻り値の受け取り、およびエラー処理を扱います。

  • src/pkg/syscall/syscall_nacl_arm.go: このGoファイルは、POSIX系システムで時間表現に一般的に使用されるTimespecおよびTimeval構造体を定義しています。また、これらの構造体とナノ秒単位の時間を相互変換するためのヘルパー関数(TimespecToNsec, NsecToTimespec, TimevalToNsec, NsecToTimeval)も含まれており、時間関連のシステムコールを扱う上で不可欠です。

  • src/pkg/syscall/time_nacl_arm.s: このアセンブリファイルは、Goランタイムの内部タイマー管理に関連するstartTimerおよびstopTimer関数のスタブを提供し、それぞれtime·startTimerおよびtime·stopTimerにリダイレクトします。

  • src/pkg/syscall/zsyscall_nacl_arm.go: このファイルは、mksyscall.plスクリプトによって自動生成されたGoコードです。naclClose, Exit, naclFstat, naclRead, naclSeekといった特定のNative Clientシステムコールに対するGoラッパー関数が含まれています。これらのラッパーは、asm_nacl_arm.sで定義された汎用Syscall関数を適切なシステムコール番号と引数で呼び出します。

変更の背景

このコミットは、Go言語がGoogle Native Client (NaCl) 環境でARMアーキテクチャをサポートするために導入されました。NaClは、ウェブブラウザ内でC/C++コードを安全かつ高性能に実行するためのサンドボックス技術です。当初、NaClはx86アーキテクチャのみをサポートしていましたが、2013年1月にARMサポートが追加されました。

Go 1.3では、NaCl環境でのGoプログラムの実行がサポートされ、特にARMv7Aアーキテクチャ(32ビット)が対象となりました。このコミットは、GoのsyscallパッケージがNaClのARM環境と適切に連携できるようにするための基盤を提供します。具体的には、GoプログラムがNaClサンドボックス内でファイルI/O、プロセス管理、時間操作などの低レベルなシステム操作を実行できるように、必要なシステムコールインターフェースを実装しています。

これにより、Go開発者はARMベースのデバイス上で動作するNaClアプリケーションを開発できるようになり、Go言語の適用範囲が拡大しました。

前提知識の解説

  • Google Native Client (NaCl): Googleが開発したサンドボックス技術で、ウェブブラウザ(主にGoogle Chrome)内でC/C++などのネイティブコードを安全に実行することを可能にします。NaClは、ソフトウェアベースのフォルト分離(Software-based Fault Isolation: SFI)などの技術を用いて、悪意のあるコードがシステムに損害を与えるのを防ぎながら、ネイティブに近いパフォーマンスを提供します。NaClは、WebAssemblyの登場により現在は非推奨となっていますが、当時はウェブアプリケーションの性能向上に貢献しました。

  • ARMアーキテクチャ: Advanced RISC Machinesの略で、モバイルデバイスや組み込みシステムで広く使用されているRISC(Reduced Instruction Set Computer)ベースのプロセッサアーキテクチャです。低消費電力と高性能を両立できる点が特徴です。

  • Go言語のsyscallパッケージ: Go言語の標準ライブラリの一部で、オペレーティングシステムの低レベルなプリミティブ(システムコール)にアクセスするためのインターフェースを提供します。ファイル操作、プロセス管理、ネットワーク通信など、OSカーネルが提供するサービスをGoプログラムから呼び出すために使用されます。Go 1.4以降、このパッケージは「ロックダウン」され、新しいコードではgolang.org/x/sysリポジトリの使用が推奨されていますが、Goランタイム自体は内部的にsyscallパッケージを広範に利用しています。

  • システムコール (Syscall): オペレーティングシステムが提供するサービスを、ユーザー空間のプログラムがカーネル空間に要求するためのメカニズムです。プログラムが直接ハードウェアにアクセスしたり、他のプロセスと通信したりする際には、システムコールを介してカーネルに処理を依頼します。

  • アセンブリ言語 (Assembly Language): 特定のプロセッサアーキテクチャに特化した低レベルのプログラミング言語です。機械語と1対1に対応しており、ハードウェアを直接制御したり、パフォーマンスが重要な部分を最適化したりする際に使用されます。Goランタイムでは、システムコールの呼び出しや、特定のアーキテクチャに依存する処理のためにアセンブリが用いられることがあります。

技術的詳細

このコミットの核となるのは、GoランタイムがNaClのARM環境でシステムコールをどのように実行するかを定義する点です。

  • asm_nacl_arm.s: このファイルは、Goのsyscallパッケージが提供する汎用システムコール関数Syscallの実装を含んでいます。ARMアーキテクチャでは、システムコールは通常、特定のレジスタにシステムコール番号と引数を設定し、特別な命令(例えばSVC命令)を実行することでカーネルにトラップします。しかし、NaCl環境では、直接カーネルを呼び出すのではなく、NaClランタイムが提供するサンドボックス化されたインターフェースを介してシステムコールが実行されます。

    • NACL_SYSCALLおよびNACL_SYSJMPマクロは、NaClのシステムコール呼び出し規約を抽象化しています。MOVW $(0x10000 + ((code)<<5)), R8; BL (R8)のような命令は、NaClがシステムコールを処理するために使用する特定のメモリ領域(0x10000をベースアドレスとし、システムコールコードに基づいてオフセットを計算)へのジャンプを生成します。
    • TEXT syscall·Syscall(SB),NOSPLIT,$0-28は、GoのSyscall関数のアセンブリ実装です。この関数は、Goのruntime·entersyscallを呼び出してシステムコールに入る準備をし、引数をARMレジスタ(R0, R1, R2など)にロードし、NaClのシステムコールインターフェースを呼び出します。システムコールからの戻り値はR0とR1に格納され、エラーはR0が負の値の場合に処理されます。最後にruntime·exitsyscallを呼び出してシステムコールから抜けます。
  • syscall_nacl_arm.go: このファイルは、TimespecTimevalという2つの重要な構造体を定義しています。これらは、Unix系システムで時間情報を表現するために広く使われるデータ型です。Goのtimeパッケージや他の時間関連のシステムコールがこれらの構造体と連携できるように、NsecToTimespecTimespecToNsecのような変換ヘルパー関数も提供されています。これは、Goの内部的な時間表現(ナノ秒単位のint64)と、OSが期待する時間構造体との間のブリッジとなります。

  • zsyscall_nacl_arm.go: このファイルは、mksyscall.plスクリプトによって自動生成されるGoコードです。Syscall関数を直接呼び出す代わりに、naclClosenaclReadのような特定のシステムコールに対するGoラッパー関数を提供します。これにより、Goプログラムはより型安全で使いやすいインターフェースを通じてシステムコールを呼び出すことができます。例えば、naclRead関数は、ファイルディスクリプタ、バイトスライス、および読み取るバイト数を引数として受け取り、内部でSyscallを呼び出して実際の読み取り操作を実行します。

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

src/pkg/syscall/asm_nacl_arm.s

#include "../../cmd/ld/textflag.h"
#include "../runtime/syscall_nacl.h"

//
// System call support for ARM, Native Client
//


#define NACL_SYSCALL(code) \
	MOVW $(0x10000 + ((code)<<5)), R8; BL (R8)

#define NACL_SYSJMP(code) \
	MOVW $(0x10000 + ((code)<<5)), R8; B (R8)

TEXT syscall·Syscall(SB),NOSPLIT,$0-28
	BL	runtime·entersyscall(SB)
	MOVW	trap+0(FP), R8
	MOVW	a1+4(FP), R0
	MOVW	a2+8(FP), R1
	MOVW	a3+12(FP), R2
	// more args would use R3, and then stack.
	MOVW	$0x10000, R7
	ADD	R8<<5, R7
	BL	(R7)
	CMP	$0, R0
	BGE	ok
	MOVW	$-1, R1
	MOVW	R1, r1+16(FP)
	MOVW	R1, r2+20(FP)
	RSB	$0, R0
	MOVW	R0, err+24(FP)
	BL	runtime·exitsyscall(SB)
	RET
ok:
	MOVW	R0, r1+16(FP)
	MOVW	R1, r2+20(FP)
	MOVW	$0, R2
	MOVW	R2, err+24(FP)
	BL	runtime·exitsyscall(SB)
	RET

src/pkg/syscall/syscall_nacl_arm.go

// Copyright 2014 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

type Timespec struct {
	Sec  int64
	Nsec int32
}

type Timeval struct {
	Sec  int64
	Usec int32
}

func TimespecToNsec(ts Timespec) int64 { return int64(ts.Sec)*1e9 + int64(ts.Nsec) }

func NsecToTimespec(nsec int64) (ts Timespec) {
	ts.Sec = int64(nsec / 1e9)
	ts.Nsec = int32(nsec % 1e9)
	return
}

func TimevalToNsec(tv Timeval) int64 { return int64(tv.Sec)*1e9 + int64(tv.Usec)*1e3 }

func NsecToTimeval(nsec int64) (tv Timeval) {
	nsec += 999 // round up to microsecond
	tv.Usec = int32(nsec % 1e9 / 1e3)
	tv.Sec = int64(nsec / 1e9)
	return
}

src/pkg/syscall/zsyscall_nacl_arm.go

// mksyscall.pl -l32 -nacl -arm syscall_nacl.go syscall_nacl_arm.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 naclClose(fd int) (err error) {
	_, _, e1 := Syscall(sys_close, uintptr(fd), 0, 0)
	if e1 != 0 {
		err = e1
	}
	return
}

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

func Exit(code int) (err error) {
	_, _, e1 := Syscall(sys_exit, uintptr(code), 0, 0)
	if e1 != 0 {
		err = e1
	}
	return
}

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

func naclFstat(fd int, stat *Stat_t) (err error) {
	_, _, e1 := Syscall(sys_fstat, uintptr(fd), uintptr(unsafe.Pointer(stat)), 0)
	if e1 != 0 {
		err = e1
	}
	return
}

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

func naclRead(fd int, b []byte) (n int, err error) {
	var _p0 unsafe.Pointer
	if len(b) > 0 {
		_p0 = unsafe.Pointer(&b[0])
	} else {
		_p0 = unsafe.Pointer(&_zero)
	}
	r0, _, e1 := Syscall(sys_read, uintptr(fd), uintptr(_p0), uintptr(len(b)))
	n = int(r0)
	if e1 != 0 {
		err = e1
	}
	return
}

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

func naclSeek(fd int, off *int64, whence int) (err error) {
	_, _, e1 := Syscall(sys_lseek, uintptr(fd), uintptr(unsafe.Pointer(off)), uintptr(whence))
	if e1 != 0 {
		err = e1
	}
	return
}

コアとなるコードの解説

  • src/pkg/syscall/asm_nacl_arm.s:

    • NACL_SYSCALLマクロは、NaCl環境でのシステムコール呼び出しのパターンを定義しています。0x10000はNaClのシステムコールエントリポイントのベースアドレスであり、((code)<<5)はシステムコール番号に基づいてオフセットを計算します。BL (R8)は、計算されたアドレスに分岐し、システムコールを実行します。
    • syscall·Syscall関数は、Goのユーザーコードからシステムコールを呼び出す際の主要なエントリポイントです。
      • BL runtime·entersyscall(SB): システムコールに入る前にGoランタイムの状態を調整します。これにより、ガベージコレクションなどのランタイム操作がシステムコール中に中断されないようにします。
      • MOVW trap+0(FP), R8, MOVW a1+4(FP), R0, MOVW a2+8(FP), R1, MOVW a3+12(FP), R2: Go関数呼び出しのフレームポインタ(FP)からのオフセットを使って、システムコール番号(trap)と引数(a1, a2, a3)をARMレジスタにロードします。ARMでは、最初の4つの引数はR0からR3レジスタに渡されるのが一般的です。
      • MOVW $0x10000, R7; ADD R8<<5, R7; BL (R7): これはNACL_SYSCALLマクロの展開に相当し、NaClのシステムコールエントリポイントを呼び出します。
      • CMP $0, R0; BGE ok: システムコールからの戻り値(R0に格納される)をチェックします。NaClのシステムコールでは、エラーが発生した場合、R0に負の値が返されます。
      • エラー処理 (MOVW $-1, R1; ... RSB $0, R0; MOVW R0, err+24(FP)): R0が負の場合、エラーコードをGoのエラー形式に変換し、戻り値として設定します。RSB $0, R0はR0の符号を反転させ、正のエラーコードにします。
      • 成功時の処理 (MOVW R0, r1+16(FP); MOVW R1, r2+20(FP); MOVW $0, R2; MOVW R2, err+24(FP)): 成功した場合、R0とR1の戻り値をGoの戻り値(r1, r2)に格納し、エラーをnilに設定します。
      • BL runtime·exitsyscall(SB): システムコールから抜けた後にGoランタイムの状態を復元します。
      • RET: 関数から戻ります。
  • src/pkg/syscall/syscall_nacl_arm.go:

    • TimespecTimeval構造体は、秒とナノ秒(またはマイクロ秒)で時間を表現するための標準的な方法を提供します。これらは、stat構造体やselectシステムコールなど、多くの時間関連のシステムコールで使用されます。
    • TimespecToNsecNsecToTimespecなどのヘルパー関数は、Goの内部的な時間表現(ナノ秒単位のint64)と、OSが期待するこれらの構造体との間のシームレスな変換を可能にします。これにより、Goプログラムは時間関連のシステムコールをより簡単に利用できます。
  • src/pkg/syscall/zsyscall_nacl_arm.go:

    • naclReadのような関数は、Goのsyscallパッケージのユーザーフレンドリーなラッパーです。これらの関数は、Goの型システムを活用し、Syscall関数への低レベルな呼び出しを抽象化します。
    • Syscall(sys_read, uintptr(fd), uintptr(_p0), uintptr(len(b)))の部分では、sys_readというシステムコール番号、ファイルディスクリプタ、読み取りバッファのポインタ、およびバッファの長さをSyscall関数に渡しています。unsafe.Pointerは、Goの型安全性を一時的にバイパスして、バイトスライスの基盤となるメモリへのポインタをシステムコールに渡すために使用されます。
    • 戻り値のr0は読み取られたバイト数、e1はエラーコードを表します。Goの慣例に従い、エラーはerrorインターフェースとして返されます。

関連リンク

参考にした情報源リンク