[インデックス 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-char
はcgo
ではなく、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
型の符号付き/符号なしの不一致に起因する潜在的なバグが回避されます。
関連リンク
- Go言語の
cgo
に関する公式ドキュメント: https://pkg.go.dev/cmd/cgo - GCCのコンパイラオプションに関するドキュメント(
-fsigned-char
について): https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html - Goの
syscall
パッケージ: https://pkg.go.dev/syscall
参考にした情報源リンク
- Stack Overflow: "Why is char signed by default on x86 and unsigned on ARM?" (https://stackoverflow.com/questions/10023845/why-is-char-signed-by-default-on-x86-and-unsigned-on-arm)
- ARM Developer: "The char type" (https://developer.arm.com/documentation/dui0472/m/arm-and-thumb-instructions/the-char-type)
- Go issue tracker (関連する可能性のあるissue): https://github.com/golang/go/issues?q=is%3Aissue+cgo+freebsd+arm+char
- Go Wiki: FreeBSD ARM (https://go.dev/wiki/FreeBSDARM)
- Go Blog: Cgo (https://go.dev/blog/cgo)
- Go source code on GitHub (syscall package): https://github.com/golang/go/tree/master/src/syscall
- Go source code on GitHub (mkall.sh): https://github.com/golang/go/blob/master/src/syscall/mkall.sh