[インデックス 17802] ファイルの概要
このコミットは、Go言語のcgo
ツールにおける、生成されるCコードのインクルード順序に関する問題を修正するものです。具体的には、builtinProlog
と各ファイルのPreamble
の出力順序を変更することで、sys/types.h
の二重インクルードによる問題を回避し、既存のcgo
コードの互換性を保ちつつ、特定のLinuxシステムでのgo tool cgo -godefs
の利用を修正します。
コミット
commit 5feb15508e9cefa06f7d109da8233c91e69937fa
Author: Russ Cox <rsc@golang.org>
Date: Tue Oct 15 15:00:48 2013 -0400
cmd/cgo: print the builtin prolog after the per-file preamble
The preamble may want to #define some special symbols
and then #include <sys/types.h> itself. The builtin prolog
also #includes <sys/types.h>, which would break such a
preamble (because the second #include will be a no-op).
The use of sys/types.h in the builtin prolog is new since Go 1.1,
so this should preserve the semantics of more existing cgo
code than we would otherwise.
It also fixes src/pkg/syscall/mkall.sh's use of go tool cgo -godefs
on some Linux systems.
Thanks to fullung@ for identifying the problem.
Fixes #6558.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/14684044
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5feb15508e9cefa06f7d109da8233c91e69937fa
元コミット内容
cmd/cgo: print the builtin prolog after the per-file preamble
このコミットは、cgo
コマンドが生成するCコードにおいて、組み込みのプロローグ(builtinProlog
)を、各Goソースファイルに記述されたCコードのプリアンブル(Preamble
)の後に配置するように変更します。これにより、プリアンブル内で#define
されたシンボルや、#include <sys/types.h>
が適切に機能するようになります。
変更の背景
この変更の背景には、cgo
がGoコードとCコードを連携させる際に生成するCソースファイルの構造と、Cプリプロセッサの動作が関係しています。
-
sys/types.h
の二重インクルード問題:- Go 1.1以降、
cgo
のbuiltinProlog
(cgo
が自動的に挿入するCコードの冒頭部分)が#include <sys/types.h>
を含むようになりました。 - 一方で、ユーザーがGoソースファイル内で
import "C"
ブロックに記述するCコード(これがPreamble
として扱われる)も、独自に#include <sys/types.h>
を含む場合があります。 - Cプリプロセッサの一般的な動作として、同じヘッダーファイルが複数回インクルードされた場合、2回目以降のインクルードは通常無視されます(これはヘッダーガードによって実現されます)。しかし、この「無視」が問題を引き起こすことがあります。
- もし
Preamble
が#define
を使って特定のシンボルを定義し、その後に#include <sys/types.h>
を呼び出す場合、そしてbuiltinProlog
が先にインクルードされると、Preamble
内の#include <sys/types.h>
は無効になり、Preamble
が意図する#define
がsys/types.h
の定義に影響を与えられなくなります。これは、sys/types.h
が#define
されたシンボルに依存するような場合に特に問題となります。
- Go 1.1以降、
-
既存の
cgo
コードのセマンティクス維持:- Go 1.1で
builtinProlog
にsys/types.h
が追加されたことで、それ以前に書かれたcgo
コードが、この新しい動作によって予期せぬ影響を受ける可能性がありました。プリアンブルがsys/types.h
のインクルード順序に依存している場合、その動作が壊れる恐れがありました。
- Go 1.1で
-
src/pkg/syscall/mkall.sh
の問題:- 特定のLinuxシステムにおいて、
src/pkg/syscall/mkall.sh
スクリプトがgo tool cgo -godefs
を使用する際に問題が発生していました。これは、godefs
がCの定義をGoの定義に変換するツールであり、上記のようなインクルード順序の問題が、Cの定義の解釈に影響を与えていたためと考えられます。
- 特定のLinuxシステムにおいて、
これらの問題を解決するため、builtinProlog
をPreamble
の後に配置することで、Preamble
が先に処理され、その中で行われる#define
や#include
が意図通りに機能するように変更されました。
前提知識の解説
このコミットを理解するためには、以下の概念を理解しておく必要があります。
cgo
: Go言語とC言語のコードを相互に呼び出すためのGoツールチェーンの一部です。GoプログラムからC関数を呼び出したり、CプログラムからGo関数を呼び出したりすることを可能にします。cgo
は、Goソースファイル内の特別なimport "C"
ブロックに記述されたCコードを抽出し、GoコードとCコードをリンクするための橋渡しとなるCソースファイルを生成します。import "C"
ブロック(Cプリアンブル): Goソースファイル内でCコードを記述するための特別なブロックです。このブロックに記述されたCコードは、cgo
によって生成されるCソースファイルの冒頭部分(プリアンブル)に挿入されます。ユーザーはここに#include
ディレクティブや#define
マクロ、C関数の定義などを記述できます。コミットメッセージではこれをper-file preamble
または単にPreamble
と呼んでいます。builtinProlog
:cgo
ツールが自動的に生成するCコードの冒頭部分です。これは、ユーザーが記述したCプリアンブルとは別に、cgo
がGoとCの連携に必要な基本的な定義やインクルード(例:sys/types.h
)を挿入するために使用されます。#include <sys/types.h>
: C言語の標準ヘッダーファイルの一つで、様々なデータ型(例:size_t
,ssize_t
,off_t
など)の定義が含まれています。システムコールやファイル操作など、多くの低レベルなCプログラミングで必要とされます。- Cプリプロセッサ: Cコンパイルプロセスの最初の段階で実行されるプログラムです。
#include
、#define
、#ifdef
などのプリプロセッサディレクティブを処理し、最終的なCソースコードを生成します。ヘッダーガード(#ifndef
/#define
/#endif
)は、ヘッダーファイルが複数回インクルードされるのを防ぐための一般的な手法です。 go tool cgo -godefs
:cgo
ツールの一機能で、Cの構造体や定数をGoの構造体や定数に変換するために使用されます。特に、Goのsyscall
パッケージのように、OSのシステムコールをGoから呼び出す際に、Cの定義をGoの型にマッピングするために利用されます。bytes.Buffer
: Go言語の標準ライブラリbytes
パッケージにある型で、可変長のバイトシーケンスを効率的に構築するためのバッファです。文字列の連結や、データを一時的に保持する際によく使用されます。gccDefines
:cgo
内部の関数で、CコードをGCCに渡し、その出力から#define
されたシンボルなどの情報を抽出するために使用されます。
技術的詳細
このコミットの技術的な核心は、src/cmd/cgo/gcc.go
ファイル内のbytes.Buffer
への書き込み順序の変更にあります。cgo
ツールは、Goのソースコードを解析し、Cコードを生成する際に、いくつかの段階でCのコンパイラ(GCCなど)を利用してCの定義を解析したり、最終的なCソースファイルを生成したりします。
変更前は、builtinProlog
がPreamble
よりも先にbytes.Buffer
に書き込まれていました。これは、生成されるCコードにおいてbuiltinProlog
がPreamble
よりも前に配置されることを意味します。
// 変更前
b.WriteString(builtinProlog)
b.WriteString(f.Preamble)
この順序だと、もしPreamble
が#define
で特定のシンボルを定義し、その後に#include <sys/types.h>
を呼び出す場合、builtinProlog
が先に#include <sys/types.h>
を処理してしまうため、Preamble
内の#include <sys/types.h>
はヘッダーガードによって無視されます。結果として、Preamble
で定義された#define
がsys/types.h
の定義に影響を与えることができず、意図しない動作を引き起こす可能性がありました。
変更後は、Preamble
がbuiltinProlog
よりも先にbytes.Buffer
に書き込まれるようになりました。
// 変更後
b.WriteString(f.Preamble)
b.WriteString(builtinProlog)
この変更により、生成されるCコードではPreamble
がbuiltinProlog
よりも前に配置されます。これにより、Preamble
内で#define
されたシンボルが、その後に続くbuiltinProlog
や、Preamble
自身がインクルードするsys/types.h
の定義に影響を与えることが可能になります。もしPreamble
が#include <sys/types.h>
を含んでいても、それが先に処理されるため、builtinProlog
内の#include <sys/types.h>
はヘッダーガードによって無視されますが、これは問題ありません。なぜなら、Preamble
が意図するsys/types.h
のインクルードは既に完了しているからです。
この修正は、cgo
がCコンパイラに渡すCコードの構造を根本的に変更するものであり、特にloadDefines
、guessKinds
、loadDWARF
といった、Cの定義を解析するためにCコンパイラを利用する関数に影響を与えます。これらの関数は、Cコンパイラに渡すCコードを構築する際に、この新しい順序を適用します。
コアとなるコードの変更箇所
変更はsrc/cmd/cgo/gcc.go
ファイルに集中しています。具体的には、以下の3つの箇所でbytes.Buffer
への書き込み順序が入れ替えられています。
-
func (p *Package) loadDefines(f *File)
内: この関数は、Cの定義をロードするために使用されます。--- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -188,8 +188,8 @@ func (p *Package) Translate(f *File) { // in the file f and saves relevant renamings in f.Name[name].Define. func (p *Package) loadDefines(f *File) { var b bytes.Buffer - b.WriteString(builtinProlog) b.WriteString(f.Preamble) + b.WriteString(builtinProlog) stdout := p.gccDefines(b.Bytes()) for _, line := range strings.Split(stdout, "\\n") {
-
func (p *Package) guessKinds(f *File) []*Name
内: この関数は、Cの型の種類を推測するために使用されます。--- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -301,8 +301,8 @@ func (p *Package) guessKinds(f *File) []*Name { } var b bytes.Buffer - b.WriteString(builtinProlog) b.WriteString(f.Preamble) + b.WriteString(builtinProlog) b.WriteString("void __cgo__f__(void) {\\n") // For a #defined expression, clang silences the warning about "unused expression".
-
func (p *Package) loadDWARF(f *File, names []*Name)
内: この関数は、DWARFデバッグ情報からCの型情報をロードするために使用されます。--- a/src/cmd/cgo/gcc.go +++ b/src/cmd/cgo/gcc.go @@ -417,8 +417,8 @@ func (p *Package) loadDWARF(f *File, names []*Name) { // for each entry in names and then dereference the type we // learn for __cgo__i. var b bytes.Buffer - b.WriteString(builtinProlog) b.WriteString(f.Preamble) + b.WriteString(builtinProlog) for i, n := range names { fmt.Fprintf(&b, "typeof(%s) *__cgo__%d;\\n", n.C, i) if n.Kind == "const" {
コアとなるコードの解説
上記の3つの変更箇所は、いずれもcgo
がCコンパイラに渡すCコードスニペットを構築する部分です。これらのスニペットは、Cの定義を解析したり、Cの型情報を取得したりするために一時的に生成されます。
変更前は、これらのスニペットが常にbuiltinProlog
で始まり、その後にユーザーが記述したPreamble
が続く形でした。
// 変更前の一般的な構造
// b.WriteString(builtinProlog)
// b.WriteString(f.Preamble)
// ... その他のCコード ...
この順序では、もしf.Preamble
が#define
マクロと#include <sys/types.h>
を含んでいた場合、builtinProlog
が先にsys/types.h
をインクルードしてしまうため、f.Preamble
内の#include
は効果がなくなります。その結果、f.Preamble
で定義された#define
がsys/types.h
内の型定義に影響を与えることができず、コンパイルエラーや予期せぬ動作につながる可能性がありました。
変更後は、f.Preamble
がbuiltinProlog
よりも先に書き込まれるようになりました。
// 変更後の一般的な構造
// b.WriteString(f.Preamble)
// b.WriteString(builtinProlog)
// ... その他のCコード ...
この新しい順序により、f.Preamble
内の#define
や#include
がbuiltinProlog
よりも先に処理されます。これにより、f.Preamble
がsys/types.h
をインクルードする場合でも、それが先に完了し、f.Preamble
で定義された#define
がsys/types.h
の定義に適切に影響を与えることができるようになります。builtinProlog
がその後でsys/types.h
をインクルードしようとしても、ヘッダーガードによって無視されるため、問題は発生しません。
この修正は、cgo
が生成するCコードのセマンティクスをより堅牢にし、特にsys/types.h
のような共通のヘッダーファイルとユーザー定義のプリアンブルとの間の相互作用における潜在的な問題を解決します。これにより、既存のcgo
コードの互換性が向上し、特定の環境でのビルド問題が解消されました。
関連リンク
- Go Issue #6558: cmd/cgo: sys/types.h included too early - このコミットが修正した具体的な問題のトラッキングイシューです。問題の詳細な議論や再現手順が記載されています。
- Go CL 14684044: https://golang.org/cl/14684044 - このコミットに対応するGerritの変更リストです。レビューコメントやパッチセットの履歴を確認できます。
参考にした情報源リンク
- Go Issue #6558: https://github.com/golang/go/issues/6558
- Go CL 14684044: https://golang.org/cl/14684044
- Cgo documentation: https://pkg.go.dev/cmd/cgo
sys/types.h
man page (Linux): https://man7.org/linux/man-pages/man0/sys_types.h.0p.html- C Preprocessor: https://gcc.gnu.org/onlinedocs/cpp/
bytes.Buffer
documentation: https://pkg.go.dev/bytes#Buffer