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

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

コミット

このコミットは、Go言語のsyscallパッケージにおいて、FreeBSD/ARMアーキテクチャ向けにz-files(GoのシステムコールAPIを生成するためのファイル)を生成する際に、C言語のchar型が符号付きとして扱われるように明示的な変更を加えるものです。これにより、cgoがFreeBSD/ARM上で正しく機能するための準備を進めています。

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

https://github.com/golang/go/commit/546081fd01aad2446b9cd50444662c6438e7fb2e

元コミット内容

syscall: make use of signed char explicit in generating z-files on freebsd/arm

This CL is in preparation to make cgo work on freebsd/arm.

The signedness of C char might be a problem when we make bare syscall
APIs, Go structures, using built-in bootstrap scripts with cgo because
they do translate C stuff to Go stuff internally. For now almost all
the C compilers assume that the type of char will be unsigned on arm
by default but it makes a different view from amd64, 386.

This CL just passes -fsigned-char, let the type of char be signed,
option which is supported on both gcc and clang to the underlying C
compilers through cgo for avoiding such inconsistency on syscall API.

LGTM=iant
R=iant
CC=golang-codereviews
https://golang.org/cl/59740051

変更の背景

この変更の主な背景は、Go言語のcgo(C言語のコードをGoプログラムから呼び出すためのメカニズム)をFreeBSD/ARMアーキテクチャで動作させるための準備です。

C言語のchar型は、その符号付き/符号なしの扱いがコンパイラやアーキテクチャによって異なる場合があります。特にARMアーキテクチャでは、多くのCコンパイラがデフォルトでchar型を符号なし(unsigned char)として扱います。これに対し、x86(amd64, 386)アーキテクチャでは通常、符号付き(signed char)として扱われます。

Goのsyscallパッケージは、OSのシステムコールをGoから呼び出すためのAPIを提供しており、その実装にはC言語の構造体やAPIが内部的に利用されています。cgoがC言語の型をGo言語の型に変換する際、このchar型の符号付き/符号なしの不一致が問題を引き起こす可能性がありました。具体的には、Goのint8型は符号付きの8ビット整数ですが、Cのcharが符号なしとして扱われると、予期せぬ値の解釈やバグにつながる恐れがあります。

この不整合を解消し、FreeBSD/ARM上でのsyscall APIの動作を他のプラットフォーム(特にx86系)と一貫させるために、Cコンパイラに対してchar型を明示的に符号付きとして扱うよう指示する必要がありました。

前提知識の解説

1. char型の符号付き/符号なし

C言語のchar型は、通常1バイト(8ビット)の整数型であり、文字を表現するために使われます。しかし、その値が符号付き(-128から127)として解釈されるか、符号なし(0から255)として解釈されるかは、C標準では実装定義(implementation-defined)とされています。

  • signed char: 符号付きの文字型。通常、-128から127の範囲の値を表現します。
  • unsigned char: 符号なしの文字型。通常、0から255の範囲の値を表現します。
  • char: signed charまたはunsigned charのどちらかと同じですが、どちらになるかはコンパイラやアーキテクチャに依存します。

多くのシステムでは、charはデフォルトでsigned charですが、ARMアーキテクチャのGCCなど、一部のコンパイラではデフォルトでunsigned charとして扱われることがあります。この違いが、異なるアーキテクチャ間での移植性や、CとGoのような異なる言語間での相互運用性において問題となることがあります。

2. cgo

cgoは、GoプログラムからC言語のコードを呼び出すためのGoのツールです。Goのソースコード内にC言語のコードを直接記述し、cgoがそのCコードをコンパイルし、Goの関数として呼び出せるようにするためのバインディングを生成します。

cgoは、C言語の型とGo言語の型の間で自動的な変換を行います。例えば、CのintはGoのintに、Cのchar*はGoの*C.char(またはGoのstring[]byte)に変換されます。この変換プロセスにおいて、Cのchar型が符号付きか符号なしかによって、Goのint8(符号付き)またはuint8(符号なし)のどちらにマッピングされるかが変わるため、型の不整合が生じる可能性があります。

3. go tool cgo -godefs

go tool cgo -godefsは、C言語のヘッダファイルからGo言語の構造体定義を自動生成するためのコマンドです。これは、Goのsyscallパッケージのように、OSのAPI(C言語で定義されていることが多い)をGoから利用するために非常に重要なツールです。

このコマンドは、Cの構造体や定数をGoの対応する型に変換し、Goのソースコードとして出力します。この変換プロセス中に、Cのchar型の符号付き/符号なしの解釈が、生成されるGoのコードに直接影響を与えます。

4. z-files

Goのsyscallパッケージでは、各OSやアーキテクチャ固有のシステムコールAPIや構造体定義を自動生成するために、z-filesと呼ばれるファイル群が利用されます。これらのファイルは、go tool cgo -godefsなどのツールによって生成され、Goのソースコードとしてコンパイル時に組み込まれます。

例えば、zsyscall_freebsd_arm.goのようなファイルは、FreeBSD/ARM向けのシステムコール定義を含んでいます。これらのファイルが正しく生成されないと、GoプログラムがFreeBSD/ARM上でシステムコールを正しく実行できなくなります。

5. -fsigned-charコンパイラオプション

-fsigned-charは、GCCやClangといったCコンパイラに渡すオプションの一つです。このオプションを指定すると、コンパイラはデフォルトのchar型を明示的にsigned charとして扱います。これにより、char型の符号付き/符号なしのデフォルトの挙動が異なる環境間での互換性の問題を解決できます。

技術的詳細

このコミットは、Goのビルドシステムの一部であるsrc/pkg/syscall/mkall.shスクリプトを変更することで、FreeBSD/ARM向けのz-files生成プロセスに-fsigned-charオプションを導入しています。

具体的には、mkall.shスクリプト内で、FreeBSD/ARMアーキテクチャ向けのmktypes変数の定義を変更しています。mktypes変数は、go tool cgo -godefsコマンドとその引数を定義しており、このコマンドがC言語のヘッダファイルからGoの型定義を生成する際に使用されます。

変更前は、mktypesは単にGOARCH=$GOARCH go tool cgo -godefsと定義されていました。これでは、cgoが内部的にCコンパイラを呼び出す際に、char型のデフォルトの符号付き/符号なしの挙動がコンパイラやアーキテクチャに依存してしまいます。

変更後は、mktypes-- -fsigned-charという引数が追加されました。この--は、cgoコマンドのオプションと、cgoが内部的に呼び出すCコンパイラに渡すオプションを区切るためのものです。したがって、-fsigned-charcgoではなく、cgoが呼び出すCコンパイラ(GCCやClang)に直接渡されます。

これにより、FreeBSD/ARM上でz-filesを生成する際に、Cコンパイラはchar型を明示的に符号付きとして扱うようになります。結果として、GoのsyscallパッケージがC言語のAPIをGoの型に変換する際に、char型が常にint8(符号付き)として正しくマッピングされるようになり、異なるプラットフォーム間でのsyscall APIの一貫性が保たれます。

この変更は、特にベアメタルなシステムコールAPIや、Goの構造体がCの構造体と相互作用する際に重要です。cgoがCのものをGoのものに内部的に変換する際に、charの符号付き/符号なしの不一致が原因で発生する可能性のあるバグや予期せぬ動作を防ぐことができます。

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

変更はsrc/pkg/syscall/mkall.shファイルの一箇所のみです。

--- a/src/pkg/syscall/mkall.sh
+++ b/src/pkg/syscall/mkall.sh
@@ -148,7 +148,9 @@ freebsd_arm)
 	mkerrors="$mkerrors"
 	mksyscall="./mksyscall.pl -l32 -arm"
 	mksysnum="curl -s 'http://svn.freebsd.org/base/head/sys/kern/syscalls.master' | ./mksysnum_freebsd.pl"
-	mktypes="GOARCH=$GOARCH go tool cgo -godefs"
+	# Let the type of C char be singed for making the bare syscall
+	# API consistent across over platforms.
+	mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char"
 	;;
 linux_386)
 	mkerrors="$mkerrors -m32"

コアとなるコードの解説

この変更は、mkall.shスクリプト内のfreebsd_arm)ケースにあります。

  • mktypes="GOARCH=$GOARCH go tool cgo -godefs": 変更前の行です。これは、FreeBSD/ARMアーキテクチャ向けのGoの型定義を生成するためにgo tool cgo -godefsコマンドを実行することを指定しています。しかし、このままではCコンパイラにchar型の符号付き/符号なしの扱いを明示的に指示できません。
  • # Let the type of C char be singed for making the bare syscall: 新しく追加されたコメント行です。この変更の目的を説明しています。「Cのchar型を符号付きにすることで、ベアシステムコールAPIを他のプラットフォームと一貫させる」という意図が示されています。
  • # API consistent across over platforms.: 上記コメントの続きです。
  • mktypes="GOARCH=$GOARCH go tool cgo -godefs -- -fsigned-char": 変更後の行です。ここに-- -fsigned-charが追加されています。
    • --: これは、go tool cgoコマンドに対するオプションの終わりを示し、それ以降の引数がcgoが内部的に呼び出すCコンパイラに渡されることを意味します。
    • -fsigned-char: このオプションは、Cコンパイラに対してchar型を符号付きとして扱うように指示します。これにより、FreeBSD/ARM環境で生成されるGoのシステムコール関連の型定義において、char型が常に符号付きとして解釈され、Goのint8型と正しくマッピングされるようになります。

この修正により、FreeBSD/ARM上でのcgoの動作がより堅牢になり、char型の符号付き/符号なしの不一致に起因する潜在的なバグが回避されます。

関連リンク

参考にした情報源リンク