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

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

このコミットは、Go言語のsyscallパッケージにおけるLinux AMD64アーキテクチャ向けのビルド問題を修正するものです。具体的には、src/pkg/syscall/syscall_linux_amd64.goファイル内のTime関数におけるエラーハンドリングと型変換の不整合を解消しています。

コミット

commit 4cf79eb80c37dcc6aa2a22ebd242a9db0d28d2c3
Author: Rob Pike <r@golang.org>
Date:   Fri May 16 09:30:28 2014 -0700

    syscall: fix linux amd64 build
    TBR=rsc
    
    TBR=rsc
    R=rsc
    CC=golang-codereviews
    https://golang.org/cl/100500047

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

https://github.com/golang/go/commit/4cf79eb80c37dcc6aa2a22ebd242a9db0d28d2c3

元コミット内容

diff --git a/src/pkg/syscall/syscall_linux_amd64.go b/src/pkg/syscall/syscall_linux_amd64.go
index 1e330dd99c..8915ed83b8 100644
--- a/src/pkg/syscall/syscall_linux_amd64.go
+++ b/src/pkg/syscall/syscall_linux_amd64.go
@@ -73,10 +73,10 @@ func Time(t *Time_t) (tt Time_t, err error) {\n 	var tv Timeval\n 	errno := gettimeofday(&tv)\n 	if errno != 0 {\n-\t\treturn errno\n+\t\treturn 0, errno\n \t}\n \tif t != nil {\n-\t\t*t = tv.Sec\n+\t\t*t = Time_t(tv.Sec)\n \t}\n \treturn Time_t(tv.Sec), nil

変更の背景

このコミットは、Go言語のsyscallパッケージがLinux AMD64環境でビルドエラーを起こす問題を解決するために導入されました。syscallパッケージは、オペレーティングシステムの低レベルなシステムコールにアクセスするためのGo言語のインターフェースを提供します。特定のアーキテクチャ(この場合はLinux AMD64)において、gettimeofdayシステムコールのラッパー関数Timeの戻り値の扱いと型変換に誤りがあり、これがビルドの失敗につながっていました。

Go言語では、関数が複数の戻り値を返す場合、エラーが発生した際にもすべての戻り値を適切に返す必要があります。また、厳密な型付けが求められるため、異なる型間での代入には明示的な型変換が必要となる場合があります。このコミットは、これらのGo言語の規約に沿うようにコードを修正し、ビルドが成功するようにしました。

興味深いことに、この修正はLinux ARM環境で新たな問題(syscall.Seekのシンボル重複エラー)を引き起こした可能性が指摘されており、その問題は別のコミット(CL 99320044)で対処されたようです。これは、低レベルなシステムコールラッパーの変更が、異なるアーキテクチャ間で予期せぬ副作用をもたらす可能性があることを示しています。

前提知識の解説

  • Go言語のsyscallパッケージ: Go言語の標準ライブラリの一部で、オペレーティングシステムのシステムコールに直接アクセスするための機能を提供します。これにより、ファイル操作、プロセス管理、ネットワーク通信など、OSレベルの低レベルな操作を行うことができます。このパッケージはOSやアーキテクチャに依存する部分が多く、syscall_linux_amd64.goのように特定の環境に特化した実装が含まれます。
  • gettimeofdayシステムコール: Unix系OSで時間を取得するためのシステムコールです。通常、エポック(1970年1月1日00:00:00 UTC)からの秒数とマイクロ秒数をstruct timeval構造体に格納して返します。成功時には0を、エラー時には-1を返し、エラーの詳細はerrno変数に設定されます。
  • errno: C言語の標準ライブラリで定義されているグローバル変数で、システムコールやライブラリ関数の実行中に発生したエラーコードを格納します。Go言語のsyscallパッケージでは、Cのシステムコールをラップする際に、このerrnoの値をGoのエラー型に変換して返すことがよくあります。
  • Go言語の多値戻り値: Go言語の関数は複数の値を返すことができます。例えば、(result, error)のように、操作の結果とエラー情報を同時に返すパターンが一般的です。エラーが発生した場合でも、すべての戻り値(この場合は結果のゼロ値とエラー値)を返す必要があります。
  • 型変換(Type Conversion): Go言語は静的型付け言語であり、異なる型間での代入や演算には明示的な型変換が必要となる場合があります。特に、基になる型が同じでも、異なる型エイリアスが定義されている場合には、型安全性を保つために変換が求められます。
  • Time_tTimeval: syscallパッケージ内で定義されている型で、C言語のtime_tstruct timevalに対応します。Timevalは通常、秒とマイクロ秒を保持する構造体であり、Time_tは秒数を表す整数型です。

技術的詳細

このコミットの技術的な核心は、Go言語のsyscallパッケージがC言語のシステムコールをどのようにラップし、その戻り値と型をGoのセマンティクスに適合させるかという点にあります。

func Time(t *Time_t) (tt Time_t, err error)という関数シグネチャは、Time_t型の値とerror型の値を返すことを期待しています。

  1. エラーハンドリングの修正: 変更前: if errno != 0 { return errno } 変更後: if errno != 0 { return 0, errno }

    gettimeofday(&tv)は、Cのシステムコールと同様に、成功時には0を、エラー時には非ゼロのerrno値を返します。GoのTime関数は(Time_t, error)という2つの戻り値を期待しているため、エラーが発生した場合でも両方の値を返す必要があります。 元のコードでは、エラー時にerrno(整数型)のみを返しており、これはTime_terrorの2つの戻り値の期待に合致していませんでした。Goコンパイラは、このような型と戻り値の数の不一致をビルドエラーとして検出します。 修正後は、Time_tのゼロ値(この場合は0)とerrnoを返すことで、関数のシグネチャに適合させ、ビルドエラーを解消しています。Goでは、syscall.Errno型がerrorインターフェースを実装しているため、errnoを直接errorとして返すことができます。

  2. 明示的な型変換の追加: 変更前: *t = tv.Sec 変更後: *t = Time_t(tv.Sec)

    tv.SecTimeval構造体のフィールドであり、通常はint64のような整数型です。一方、t*Time_t型であり、Time_tint64の型エイリアスである可能性がありますが、Goでは型エイリアスであっても異なる型として扱われるため、明示的な型変換が必要となる場合があります。 この修正は、tv.Secの値をTime_t型に明示的にキャストすることで、型安全性を確保し、コンパイラが型不一致を検出するのを防ぎます。これにより、Time_t型のポインタtが指すメモリ位置に、正しい型の値が代入されることが保証されます。

これらの変更は、Go言語の厳格な型システムと多値戻り値のセマンティクスにコードを適合させるためのものであり、特定のアーキテクチャ(Linux AMD64)でのビルド問題を解決するために不可欠でした。

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

変更はsrc/pkg/syscall/syscall_linux_amd64.goファイル内のTime関数に集中しています。

 func Time(t *Time_t) (tt Time_t, err error) {
 	var tv Timeval
 	errno := gettimeofday(&tv)
 	if errno != 0 {
-		return errno // 変更前: 戻り値の数が不一致
+		return 0, errno // 変更後: Time_tのゼロ値とerrnoを返す
 	}
 	if t != nil {
-		*t = tv.Sec // 変更前: 明示的な型変換がない
+		*t = Time_t(tv.Sec) // 変更後: Time_t型への明示的なキャスト
 	}
 	return Time_t(tv.Sec), nil
 }

コアとなるコードの解説

Time関数は、gettimeofdayシステムコールを呼び出して現在の時刻を取得し、それをTime_t型で返します。また、引数tnilでなければ、そのポインタが指す場所にも時刻を格納します。

  1. errno := gettimeofday(&tv): gettimeofdayは、Timeval構造体へのポインタを受け取り、システムコールを実行します。この関数は、Cのシステムコールと同様に、成功時には0を、エラー時にはerrno値を返します。このerrnoはGoのsyscall.Errno型に変換され、エラー情報として扱われます。

  2. if errno != 0 { return 0, errno }: gettimeofdayがエラーを返した場合(errno0でない場合)、Time関数はエラーを呼び出し元に伝播させる必要があります。Goの関数シグネチャ(tt Time_t, err error)に従い、ttにはTime_tのゼロ値である0を、errにはerrnoを返します。これにより、関数が期待されるすべての戻り値を返し、型システムに適合します。

  3. *t = Time_t(tv.Sec): tv.SecTimeval構造体から取得した秒数です。これをTime_t型のポインタtが指すメモリ位置に代入する際、Time_t(tv.Sec)と明示的に型変換を行うことで、tv.Secの値をTime_t型として扱います。これにより、Goの型チェックがパスし、コンパイルエラーが回避されます。

  4. return Time_t(tv.Sec), nil: gettimeofdayが成功した場合、Time関数はTime_t(tv.Sec)を結果として返し、エラーがないことを示すnilを返します。ここでもtv.SecTime_t型に明示的に変換されています。

これらの変更により、Time関数はGo言語の型システムとエラーハンドリングの規約に完全に準拠し、Linux AMD64環境でのビルドが可能になりました。

関連リンク

参考にした情報源リンク