[インデックス 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) およびシンボル命名規則に関する知識