[インデックス 13486] ファイルの概要
このコミットは、Go言語のCgoツールにおいて、gccgo
コンパイラとの連携を強化するための変更を導入しています。具体的には、gccgo
の-fgo-pkgpath
オプションに対応する-gccgopkgpath
オプションをCgoに追加し、gccgo
が生成するシンボル名との整合性を向上させています。これにより、Cgoで生成されたコードがgccgo
でコンパイルされる際に、より正確なシンボル解決が可能になります。
コミット
commit b575a98121a60f2c74782130d639fa636d41e98a
Author: Ian Lance Taylor <iant@golang.org>
Date: Fri Jul 20 16:58:08 2012 -0700
cgo: add -gccgopkgpath option to match gccgo -fgo-pkgpath
R=golang-dev, r, iant
CC=golang-dev
https://golang.org/cl/6416056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b575a98121a60f2c74782130d639fa636d41e98a
元コミット内容
cgo: add -gccgopkgpath option to match gccgo -fgo-pkgpath
変更の背景
Go言語には、公式コンパイラであるgc
(Go Compiler)と、GCCのフロントエンドとしてGoをコンパイルするgccgo
の二つの主要なコンパイラ実装が存在します。Cgoは、GoプログラムからC言語のコードを呼び出したり、C言語のコードからGoの関数を呼び出したりするためのツールです。
gccgo
は、Goのパッケージパスに基づいて、エクスポートされたGoの関数や変数に特定のシンボル名を付与します。これは、CgoがGoとCの間のインターフェースを生成する際に、gccgo
が期待するシンボル命名規則と一致させる必要があることを意味します。
以前のCgoは、gccgo
のシンボル命名規則を完全にカバーしていませんでした。特に、gccgo
が提供する-fgo-pkgpath
オプション(Goのパッケージパスをシンボル名に含めるためのオプション)に対応するCgo側のメカニズムが不足していました。この不一致により、Cgoで生成されたコードをgccgo
でコンパイルする際に、シンボル解決の問題が発生する可能性がありました。
このコミットは、Cgoがgccgo
の-fgo-pkgpath
オプションによって生成されるシンボル名と正確に一致するように、Cgoに-gccgopkgpath
オプションを追加し、内部のシンボル生成ロジックを調整することで、この問題を解決することを目的としています。これにより、gccgo
を使用する開発者がCgoをよりスムーズに利用できるようになります。
前提知識の解説
Cgo
Cgoは、GoプログラムとC/C++プログラムの間で相互運用を可能にするGoのツールです。Goのコード内でCの関数を呼び出したり、CのコードからGoの関数を呼び出したりするために使用されます。Cgoは、GoとCの間のブリッジとなるコード(スタブ関数など)を生成します。
Gccgo
gccgo
は、GCC(GNU Compiler Collection)の一部として実装されたGo言語のコンパイラです。Goの公式コンパイラであるgc
とは異なる実装であり、GCCの最適化やバックエンドを利用できるという特徴があります。gccgo
は、Goのソースコードをコンパイルして実行可能なバイナリを生成する際に、独自のシンボル命名規則を使用します。
シンボルとシンボル命名規則
プログラムにおいて、関数、変数、型などの識別子は「シンボル」として扱われます。コンパイラはこれらのシンボルを、リンカが参照できるように、特定の命名規則に従ってバイナリファイル内に記録します。異なるコンパイラや言語は、それぞれ独自のシンボル命名規則を持つことがあり、これらが異なると、異なる言語で書かれたモジュールをリンクする際に「未解決のシンボル」エラーなどの問題が発生することがあります。
gccgo
のコンパイルオプション
-fgo-pkgpath
:gccgo
のコンパイルオプションの一つで、Goのパッケージパスをエクスポートされるシンボル名に含めるように指定します。これにより、異なるパッケージに同じ名前の関数や変数があっても、シンボル名が衝突するのを防ぎ、一意性を保証します。例えば、fmt
パッケージのPrintln
関数は、内部的にはgo.fmt.Println
のようなシンボル名になることがあります。-fgo-prefix
:gccgo
のコンパイルオプションの一つで、生成されるGoのシンボルに共通のプレフィックスを追加します。デフォルトではgo
が使用されることが多いですが、このオプションで変更できます。
技術的詳細
このコミットの核心は、Cgoがgccgo
のシンボル命名規則、特に-fgo-pkgpath
オプションによって生成されるシンボル名に正確に合わせるためのロジック変更です。
gccgo
は、Goの関数や変数をCから呼び出せるようにエクスポートする際、そのシンボル名にパッケージパスを含めることがあります。例えば、my/package/name.MyFunction
のような形式です。Cgoは、Goの関数をCから呼び出すためのCのスタブ関数を生成しますが、このスタブ関数が参照するGo側のシンボル名は、gccgo
が実際に生成するシンボル名と一致している必要があります。
このコミット以前は、Cgoはgccgo
のシンボル名を生成する際に、主に-gccgoprefix
オプションとGoのパッケージ名に基づいていました。しかし、これはgccgo
の-fgo-pkgpath
オプションが提供する、より詳細なパッケージパス情報を含んだシンボル名とは一致しませんでした。
新しい-gccgopkgpath
オプションがCgoに追加されたことで、Cgoはgccgo
の-fgo-pkgpath
オプションで指定された正確なパッケージパス情報を取得できるようになります。Cgoは、この情報を使用して、gccgo
が期待する形式のシンボル名を生成します。
具体的には、CgoはエクスポートされるGoの関数シンボル名を生成する際に、以下の優先順位でプレフィックスを決定します。
-gccgopkgpath
オプションが指定されていれば、その値がシンボルプレフィックスとして使用されます。これは、gccgo
の-fgo-pkgpath
と直接対応します。-gccgopkgpath
が指定されていない場合、従来の-gccgoprefix
オプションが考慮されます。- もし現在のGoパッケージが
main
パッケージであり、かつ-gccgoprefix
が指定されていない(または空文字列)場合、シンボルプレフィックスはmain
となります。 - それ以外の場合、
-gccgoprefix
の値(デフォルトはgo
)とGoのパッケージ名を結合したものがプレフィックスとなります(例:go.mypackage
)。
- もし現在のGoパッケージが
この変更により、Cgoが生成するCのスタブ関数が、gccgo
によってコンパイルされたGoの関数を正確なシンボル名で参照できるようになり、リンケージエラーを防ぎます。
また、シンボル名のフォーマットも変更されています。以前は%s.%s.%s
(gccgoSymbolPrefix.PackageName.FuncName
)のような形式でしたが、新しいロジックではgccgoSymbolPrefix
自体がパッケージパスを含むため、%s.%s
(gccgoSymbolPrefix.FuncName
)という形式に変更され、冗長なPackageName
が削除されています。
コアとなるコードの変更箇所
src/cmd/cgo/main.go
--- a/src/cmd/cgo/main.go
+++ b/src/cmd/cgo/main.go
@@ -144,7 +144,8 @@ var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C
var objDir = flag.String("objdir", "", "object directory")
var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
-var gccgoprefix = flag.String("gccgoprefix", "go", "prefix of symbols generated by gccgo")
+var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
+var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
var goarch, goos string
gccgoprefix
フラグのデフォルト値が"go"
から""
に変更され、説明もgccgo
の-fgo-prefix
オプションに対応することが明記されました。- 新しく
gccgopkgpath
フラグが追加されました。これはgccgo
の-fgo-pkgpath
オプションに対応します。
src/cmd/cgo/out.go
--- a/src/cmd/cgo/out.go
+++ b/src/cmd/cgo/out.go
@@ -669,7 +669,21 @@ func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) {
}
return '_'
}
- gccgoSymbolPrefix := strings.Map(clean, *gccgoprefix)
+
+ var gccgoSymbolPrefix string
+ if *gccgopkgpath != "" {
+ gccgoSymbolPrefix = strings.Map(clean, *gccgopkgpath)
+ } else {
+ if *gccgoprefix == "" && p.PackageName == "main" {
+ gccgoSymbolPrefix = "main"
+ } else {
+ prefix := strings.Map(clean, *gccgoprefix)
+ if prefix == "" {
+ prefix = "go"
+ }
+ gccgoSymbolPrefix = prefix + "." + p.PackageName
+ }
+ }
for _, exp := range p.ExpFunc {
// TODO: support functions with receivers.
@@ -707,7 +721,7 @@ func (p *Package) writeGccgoExports(fgo2, fc, fm *os.File) {
// The function name.
fmt.Fprintf(cdeclBuf, " "+exp.ExpName)
- gccgoSymbol := fmt.Sprintf("%s.%s.%s", gccgoSymbolPrefix, p.PackageName, exp.Func.Name)
+ gccgoSymbol := fmt.Sprintf("%s.%s", gccgoSymbolPrefix, exp.Func.Name)
fmt.Fprintf(cdeclBuf, " (")
// Function parameters.
forFieldList(fntype.Params,
writeGccgoExports
関数内で、gccgoSymbolPrefix
の計算ロジックが大幅に変更されました。*gccgopkgpath
が空でなければ、その値が直接gccgoSymbolPrefix
として使用されます。*gccgopkgpath
が空の場合、従来の*gccgoprefix
とp.PackageName
に基づいてプレフィックスが生成されます。特に、main
パッケージの場合はmain
が、それ以外の場合はprefix.PackageName
(prefix
のデフォルトはgo
)が使用されます。
gccgoSymbol
のフォーマット文字列が"%s.%s.%s"
から"%s.%s"
に変更されました。これにより、p.PackageName
がシンボル名に二重に含まれることがなくなりました。
コアとなるコードの解説
src/cmd/cgo/main.go
の変更
main.go
でのフラグの追加と変更は、Cgoツールがコマンドライン引数として新しい情報を受け取れるようにするためのものです。
var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
この行は、Cgoの実行時に-gccgopkgpath
という新しいコマンドラインオプションを定義しています。このオプションは文字列を受け取り、そのデフォルト値は空文字列です。この値は、gccgo
コンパイラの-fgo-pkgpath
オプションで指定されるパッケージパスと一致させることを意図しています。Cgoは、この情報を使って、gccgo
が期待する正確なシンボル名を生成します。var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
gccgoprefix
フラグのデフォルト値が"go"
から""
に変更されました。これは、-gccgopkgpath
が優先されるようになったため、gccgoprefix
が指定されない場合の挙動をより柔軟にするためと考えられます。説明もgccgo
の-fgo-prefix
オプションに対応することが明確にされました。
src/cmd/cgo/out.go
の変更
out.go
のwriteGccgoExports
関数は、Goの関数をCから呼び出せるようにするためのエクスポートコード(Cの宣言やGoのスタブ関数など)を生成する役割を担っています。この関数内のgccgoSymbolPrefix
の計算ロジックが、今回の変更の最も重要な部分です。
var gccgoSymbolPrefix string
if *gccgopkgpath != "" {
gccgoSymbolPrefix = strings.Map(clean, *gccgopkgpath)
} else {
if *gccgoprefix == "" && p.PackageName == "main" {
gccgoSymbolPrefix = "main"
} else {
prefix := strings.Map(clean, *gccgoprefix)
if prefix == "" {
prefix = "go"
}
gccgoSymbolPrefix = prefix + "." + p.PackageName
}
}
このコードブロックは、gccgo
が生成するGoのシンボル名のプレフィックスを決定します。
*gccgopkgpath
の優先: 最も優先されるのは、新しく追加された-gccgopkgpath
オプションの値です。もしこのオプションが指定されていれば(空文字列でなければ)、その値がそのままシンボルプレフィックスとして使用されます。これは、gccgo
の-fgo-pkgpath
オプションが提供する、より正確なパッケージパス情報にCgoが直接対応できるようにするためです。strings.Map(clean, ...)
は、シンボル名として有効な文字に変換するための処理です。*gccgoprefix
とmain
パッケージの考慮:-gccgopkgpath
が指定されていない場合、Cgoは従来の-gccgoprefix
オプションと現在のGoパッケージ名(p.PackageName
)に基づいてプレフィックスを生成します。- もし
-gccgoprefix
が空文字列であり、かつ現在のパッケージがmain
パッケージである場合、シンボルプレフィックスは単純に"main"
となります。これは、main
パッケージの実行可能ファイルにおけるシンボル命名の慣習に合わせたものです。 - それ以外の場合、
*gccgoprefix
の値(もし空であればデフォルトの"go"
が使用される)と現在のパッケージ名を.
で結合したものがプレフィックスとなります(例:go.mypackage
)。
- もし
fmt.Fprintf(cdeclBuf, " "+exp.ExpName)
gccgoSymbol := fmt.Sprintf("%s.%s", gccgoSymbolPrefix, exp.Func.Name)
fmt.Fprintf(cdeclBuf, " (")
この部分では、実際にCの宣言内で使用されるGoのシンボル名gccgoSymbol
が生成されます。以前は%s.%s.%s
というフォーマットでgccgoSymbolPrefix
, p.PackageName
, exp.Func.Name
の3つの要素を結合していましたが、新しいロジックではgccgoSymbolPrefix
自体が既にパッケージパス情報を含んでいる可能性があるため、p.PackageName
が冗長になります。そのため、フォーマットが"%s.%s"
に変更され、gccgoSymbolPrefix
とexp.Func.Name
の2つの要素のみを結合するようになりました。これにより、生成されるシンボル名がgccgo
の期待する形式と正確に一致するようになります。
関連リンク
- Go Change-Id:
6416056
- https://golang.org/cl/6416056
参考にした情報源リンク
- GCCgo Documentation (General information about gccgo): https://gcc.gnu.org/onlinedocs/gccgo/
- Go Command
cgo
Documentation: https://pkg.go.dev/cmd/cgo - Understanding Go and Cgo: https://go.dev/blog/cgo
- GCCgo
-fgo-pkgpath
and-fgo-prefix
options (specific documentation might be within GCCgo source or man pages, general understanding from Go community discussions): https://gcc.gnu.org/onlinedocs/gccgo/Go-Options.html (This link provides general Go options for GCC, but specific details on-fgo-pkgpath
might require deeper search or source code review.)