[インデックス 17857] ファイルの概要
このコミットは、Go言語のcmd/cgo
ツールにおけるデバッグ情報シンボル__cgodebug_data
の検索ロジックを改善するものです。特に、異なるオペレーティングシステム(OS)やオブジェクトファイル形式におけるシンボル名のプレフィックスの差異を吸収し、Windowsでのビルド問題を解決することを目的としています。
コミット
commit 797d1bac0d5f22f8d2ce105ecb22d3b64b7ebd37
Author: Russ Cox <rsc@golang.org>
Date: Wed Oct 30 10:24:42 2013 -0400
cmd/cgo: accept extra leading _ on __cgodebug_data for all object formats
The current Windows build breakage appears to be because
the Windows code should be looking for __cgodebug_data
not ___cgodebug_data. Dodge the question everywhere by
accepting both.
R=golang-dev, iant
CC=golang-dev
https://golang.org/cl/19780043
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/797d1bac0d5f22f8d2ce105ecb22d3b64b7ebd37
元コミット内容
cmd/cgo: accept extra leading _ on __cgodebug_data for all object formats
現在のWindowsビルドの破損は、Windowsコードが___cgodebug_data
ではなく__cgodebug_data
を探すべきであることに起因しているようです。この問題を回避するため、両方の形式を受け入れるようにしました。
変更の背景
Go言語のcmd/cgo
ツールは、C言語のコードをGoプログラムから呼び出す(またはその逆)際に使用されます。このツールは、Cコンパイラが生成したオブジェクトファイルからデバッグ情報(DWARFデータ)を抽出する必要があります。このデバッグ情報には、__cgodebug_data
という特定のシンボルが含まれており、cgo
はこのシンボルを探して関連するデータを読み込みます。
しかし、異なるOSやコンパイラ、リンカは、オブジェクトファイル内のシンボル名に独自の命名規則やプレフィックスを適用することがあります。特に、C言語のグローバルシンボルには、アセンブリレベルで単一のアンダースコア(_
)がプレフィックスとして付与されることが一般的です(例: _main
)。さらに、Windowsのような特定の環境では、異なる数のアンダースコアが付与されたり、全く付与されなかったりする場合があります。
このコミットが作成された時点では、Windows環境でのGoのビルドが失敗している問題が発生していました。その原因は、cmd/cgo
が__cgodebug_data
シンボルを探す際に、Windowsが生成する実際のシンボル名とcgo
が期待するシンボル名との間に不一致があったためと考えられます。具体的には、cgo
が___cgodebug_data
(アンダースコア3つ)を探していたのに対し、Windowsは__cgodebug_data
(アンダースコア2つ)を生成していた可能性が指摘されています。
この問題を解決するために、特定のOSやオブジェクトファイル形式に依存せず、より柔軟に__cgodebug_data
シンボルを認識できるように、cgo
のシンボル検索ロジックを修正する必要がありました。
前提知識の解説
- cgo: Go言語のForeign Function Interface (FFI) です。GoプログラムからC言語の関数を呼び出したり、C言語からGoの関数を呼び出したりすることを可能にします。
cgo
は、GoとCの間の橋渡しをするためのコードを生成し、コンパイルプロセスを管理します。 - DWARF (Debugging With Attributed Record Formats): プログラムのデバッグ情報を格納するための標準的な形式です。コンパイラによって生成され、デバッガがソースコードレベルでのデバッグ(変数名、型情報、行番号など)を可能にするために使用されます。
cmd/cgo
は、Cコンパイラが生成したオブジェクトファイル内のDWARFデータから、__cgodebug_data
という特定のシンボルに関連する情報を抽出します。 - シンボル (Symbol): プログラムのオブジェクトファイルや実行可能ファイル内で、関数やグローバル変数などのメモリ上の位置を識別するために使用される名前です。
- シンボル命名規則 (Symbol Naming Conventions): オペレーティングシステム、コンパイラ、リンカによって、オブジェクトファイル内のシンボル名に特定のプレフィックスやサフィックスが付与されることがあります。
- Unix系システム (Mach-O, ELF): 多くのUnix系システム(特にmacOSのMach-O形式)では、C言語のグローバルシンボルに単一のアンダースコア(
_
)がプレフィックスとして付与されることが一般的です。例えば、Cの関数my_func
はオブジェクトファイル内で_my_func
として表現されます。これは、アセンブリレベルでの名前の衝突を避けるためや、リンカがシンボルを解決する際の慣習によるものです。 - Windows (PE形式): WindowsのPortable Executable (PE) 形式では、シンボル命名規則がより複雑で、呼び出し規約(例:
__stdcall
,__cdecl
)によって異なるプレフィックスやサフィックスが付与されることがあります。また、コンパイラの設定によっても異なります。
- Unix系システム (Mach-O, ELF): 多くのUnix系システム(特にmacOSのMach-O形式)では、C言語のグローバルシンボルに単一のアンダースコア(
- オブジェクトファイル形式:
- Mach-O: macOSやiOSで使用される実行可能ファイルおよびオブジェクトファイルの形式です。
- ELF (Executable and Linkable Format): Linux、BSD、Solarisなど、多くのUnix系システムで使用される標準的な実行可能ファイルおよびオブジェクトファイルの形式です。
- PE (Portable Executable): Windowsで使用される実行可能ファイル、DLL、オブジェクトファイルの形式です。
技術的詳細
このコミットの核心は、cmd/cgo
が__cgodebug_data
シンボルを検索する際に、異なるプラットフォームでのシンボル命名規則の差異を吸収することにあります。
以前のコードでは、オブジェクトファイル形式に応じて、以下のようにシンボルを検索していました。
- Mach-O (macOS):
_
+__cgodebug_data
、つまり___cgodebug_data
というシンボル名を明示的に探していました。これは、Mach-OがC言語のグローバルシンボルに単一のアンダースコアを付与する慣習に従ったものです。 - ELF (Linuxなど):
__cgodebug_data
というシンボル名を直接探していました。 - PE (Windows): コミットメッセージから、Windowsでは
__cgodebug_data
が生成されるべきなのに、cgo
が___cgodebug_data
を探していたためにビルドが失敗していたことが示唆されています。
このコミットでは、このプラットフォームごとの差異を個別にハンドリングするのではなく、より汎用的なアプローチを採用しました。具体的には、__cgodebug_data
と___cgodebug_data
の両方を有効なシンボル名として受け入れるように変更しました。これにより、cgo
は、Cコンパイラがどちらの形式でシンボルを生成しても、正しくデバッグ情報を抽出できるようになります。
この変更は、特にWindows環境でのビルドの安定性を向上させるとともに、将来的に他のプラットフォームで同様のシンボル命名規則の差異が発生した場合にも、より堅牢に対応できる設計となっています。
コアとなるコードの変更箇所
変更はsrc/cmd/cgo/gcc.go
ファイルに集中しています。
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -780,6 +780,11 @@ func (p *Package) gccCmd() []string {
func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte) {
runGcc(stdin, p.gccCmd())
+ isDebugData := func(s string) bool {
+ // Some systems use leading _ to denote non-assembly symbols.
+ return s == "__cgodebug_data" || s == "___cgodebug_data"
+ }
+
if f, err := macho.Open(gccTmp()); err == nil {
defer f.Close()
td, err := f.DWARF()
@@ -790,8 +795,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
if f.Symtab != nil {
for i := range f.Symtab.Syms {
s := &f.Symtab.Syms[i]
- // Mach-O still uses a leading _ to denote non-assembly symbols.
- if s.Name == "_" + "__cgodebug_data" {
+ if isDebugData(s.Name) {
// Found it. Now find data section.
if i := int(s.Sect) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
@@ -818,7 +822,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
if err == nil {
for i := range symtab {
s := &symtab[i]
- if s.Name == "__cgodebug_data" {
+ if isDebugData(s.Name) {
// Found it. Now find data section.
if i := int(s.Section); 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
@@ -842,7 +846,7 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)
}
var data []byte
for _, s := range f.Symbols {
- if s.Name == "_" + "__cgodebug_data" {
+ if isDebugData(s.Name) {
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i]
if s.Value < sect.Size {
コアとなるコードの解説
-
isDebugData
関数の導入:+ isDebugData := func(s string) bool { + // Some systems use leading _ to denote non-assembly symbols. + return s == "__cgodebug_data" || s == "___cgodebug_data" + }
このコミットの最も重要な変更点です。
gccDebug
関数内に、isDebugData
という匿名関数が新しく定義されました。この関数は、引数として与えられたシンボル名s
が__cgodebug_data
または___cgodebug_data
のいずれかである場合にtrue
を返します。 コメントにあるように、「一部のシステムでは、非アセンブリシンボルを示すために先頭に_
を使用する」という背景を考慮し、両方の形式を許容するようにしています。これにより、プラットフォーム固有のシンボル命名規則の差異を抽象化し、より柔軟な検索を可能にしています。 -
Mach-O (macOS) シンボル検索の変更:
- if s.Name == "_" + "__cgodebug_data" { + if isDebugData(s.Name) {
Mach-O形式のオブジェクトファイルを処理する部分で、以前は
_
と__cgodebug_data
を結合した___cgodebug_data
という文字列とシンボル名を直接比較していました。これは、Mach-OがC言語のグローバルシンボルに単一のアンダースコアを付与する慣習に基づいています。 この変更により、直接文字列比較する代わりに、新しく定義されたisDebugData
関数を呼び出すようになりました。これにより、Mach-O環境で生成されるシンボルが___cgodebug_data
であっても、あるいは何らかの理由で__cgodebug_data
として生成された場合でも、両方に対応できるようになります。 -
その他のオブジェクト形式 (ELFなど) シンボル検索の変更:
- if s.Name == "__cgodebug_data" { + if isDebugData(s.Name) {
Mach-O以外のオブジェクト形式(主にELF形式)を処理する部分でも同様の変更が行われました。以前は
__cgodebug_data
という文字列とシンボル名を直接比較していました。 ここでもisDebugData
関数を使用することで、ELF環境で生成されるシンボルが__cgodebug_data
である場合に加えて、もし___cgodebug_data
として生成された場合でも対応できるようになります。これにより、Windowsでのビルド問題の根本原因であった、cgo
が期待するシンボル名と実際のシンボル名の不一致を解消しています。 -
Mach-O (Symbolsセクション) シンボル検索の変更:
- if s.Name == "_" + "__cgodebug_data" { + if isDebugData(s.Name) {
Mach-Oファイルの
Symbols
セクションを処理する別の箇所でも、同様にisDebugData
関数が適用されています。これにより、Mach-Oオブジェクトファイル内のシンボル検索ロジック全体で一貫性が保たれ、__cgodebug_data
シンボルの検出がより堅牢になりました。
これらの変更により、cmd/cgo
は、異なるOSやコンパイラが生成するオブジェクトファイル内の__cgodebug_data
シンボルの命名規則の差異を透過的に処理できるようになり、特にWindows環境でのビルドの安定性が向上しました。
関連リンク
- Go言語の
cgo
に関する公式ドキュメント: https://pkg.go.dev/cmd/cgo - DWARF Debugging Standard: https://dwarfstd.org/
- Go言語のコードレビューシステム (Gerrit): https://go-review.googlesource.com/ (コミットメッセージにあるCLリンクはGerritへのリンクです)
参考にした情報源リンク
- コミットメッセージと差分 (
src/cmd/cgo/gcc.go
の変更内容) - Go言語のソースコード (
src/cmd/cgo/gcc.go
) - 一般的なオブジェクトファイル形式 (Mach-O, ELF, PE) およびシンボル命名規則に関する知識