[インデックス 13902] ファイルの概要
このコミットは、Go言語のcgo
ツールにおけるWindows環境での列挙型(enums)のデバッグデータ利用に関する修正です。具体的には、Windowsの実行可能ファイル形式であるPE(Portable Executable)ファイルからDWARFデバッグ情報を読み込む際に、列挙型のデバッグデータを正しく取得できるように変更されています。
コミット
commit 49aa74ef7f3a3c3c261bd9cdf3b8eb26631dcd17
Author: Joel Sing <jsing@google.com>
Date: Sat Sep 22 17:57:54 2012 +1000
cmd/cgo: use debug data for enums on windows
Use the debug data for enums on windows.
Fixes #4120.
R=alex.brainman
CC=golang-dev
https://golang.org/cl/6545047
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/49aa74ef7f3a3c3c261bd9cdf3b8eb26631dcd17
元コミット内容
cmd/cgo: use debug data for enums on windows
Windows環境において、cgo
が生成するコードで列挙型(enums)のデバッグデータを適切に利用するように修正します。
変更の背景
この変更の背景には、Go言語のcgo
ツールがWindows環境でC言語のコードを扱う際に、列挙型のデバッグ情報が正しく扱われていなかった問題があります。デバッグ情報が不完全であると、デバッガでプログラムの実行を追跡する際に、列挙型の値がシンボリックに表示されず、数値としてしか認識されないなどの不便が生じます。
特に、Windowsの実行可能ファイル形式であるPEファイルは、デバッグ情報を格納する方法が他のOS(例えばLinuxのELFやmacOSのMach-O)とは異なります。このコミットは、PEファイル内のDWARFデバッグデータから、__cgodebug_data
という特定のセクションに格納されている列挙型関連のデバッグ情報を正しく抽出できるようにすることで、この問題を解決しようとしています。
コミットメッセージにあるFixes #4120
は、Goプロジェクトの内部的な課題追跡システムにおける問題番号を示していると考えられます。この問題が、Windows環境でのcgo
利用におけるデバッグ体験の改善を目的としていたことが伺えます。
前提知識の解説
- cgo: Go言語のプログラムからC言語のコードを呼び出すためのツールです。GoとCの間の相互運用を可能にし、既存のCライブラリをGoプロジェクトで利用する際に不可欠です。
cgo
は、GoとCの型変換や関数呼び出しの橋渡しとなるコードを生成します。 - DWARF (Debugging With Arbitrary Record Formats): プログラムのデバッグ情報を格納するための標準的なフォーマットです。ソースコードの行番号、変数名、型情報、関数名など、デバッガがプログラムの実行状態を理解し、開発者がデバッグを行うために必要な情報を提供します。
- PE (Portable Executable) Format: Microsoft Windowsオペレーティングシステムで使用される実行可能ファイル、オブジェクトコード、DLL(Dynamic Link Library)などのファイル形式です。PEファイルは、プログラムのコード、データ、リソース、そしてデバッグ情報などを構造化して格納します。
debug/pe
パッケージ: Go言語の標準ライブラリに含まれるパッケージで、WindowsのPEファイルを解析するための機能を提供します。PEファイルのヘッダ、セクション、シンボルテーブル、デバッグ情報などにアクセスできます。debug/dwarf
パッケージ: Go言語の標準ライブラリに含まれるパッケージで、DWARFデバッグ情報を解析するための機能を提供します。このパッケージを使用することで、DWARF形式で格納された型情報や変数情報などをプログラム的に読み取ることができます。- 列挙型 (Enums): プログラミング言語において、名前付きの定数セットを定義するためのデータ型です。例えば、曜日を表す
MONDAY
,TUESDAY
などの定数をまとめて定義する際に使用されます。デバッグ時には、これらの名前が数値の代わりに表示されると、コードの理解が容易になります。
技術的詳細
このコミットは、src/cmd/cgo/gcc.go
ファイル内のgccDebug
関数に焦点を当てています。この関数は、cgo
がGCCコンパイラから生成されたデバッグ情報を処理する部分です。
以前のコードでは、WindowsのPEファイルからDWARFデバッグ情報を読み込んだ後、// Can skip debug data block in PE for now.
というコメントがあり、特定のデバッグデータブロックをスキップしていました。これは、列挙型に関する重要な情報が含まれる可能性のある__cgodebug_data
セクションの処理を怠っていたことを示唆しています。
修正後のコードでは、PEファイル(pe.Open(gccTmp())
で開かれる)からDWARF情報を取得するだけでなく、PEファイルのシンボルテーブルを走査し、"__cgodebug_data"
という名前のシンボルを探します。このシンボルが見つかった場合、そのシンボルが指すセクションのデータ(sect.Data()
)を読み込み、シンボルの値(s.Value
)から始まる部分をdata
変数に格納します。このdata
変数は、gccDebug
関数の戻り値としてDWARFデータと共に返され、後続の処理で列挙型のデバッグ情報として利用されます。
この変更により、cgo
はWindows環境で生成されるPEファイル内の__cgodebug_data
セクションに格納された列挙型に関するデバッグ情報を正しく認識し、デバッガがより詳細な情報を提供できるようになります。これは、Windows上でのGoとCの混在コードのデバッグ体験を向上させる上で重要な修正です。
コアとなるコードの変更箇所
--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -830,15 +830,25 @@ func (p *Package) gccDebug(stdin []byte) (*dwarf.Data, binary.ByteOrder, []byte)\n return d, f.ByteOrder, data\n }\n \n- // Can skip debug data block in PE for now.\n- // The DWARF information is complete.\n-\n if f, err := pe.Open(gccTmp()); err == nil {\n d, err := f.DWARF()\n if err != nil {\n fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)\n }\n- return d, binary.LittleEndian, nil\n+ var data []byte\n+ for _, s := range f.Symbols {\n+ if s.Name == "_" + "__cgodebug_data" {\n+ if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {\n+ sect := f.Sections[i]\n+ if s.Value < sect.Size {\n+ if sdat, err := sect.Data(); err == nil {\n+ data = sdat[s.Value:]\n+ }\n+ }\n+ }\n+ }\n+ }\n+ return d, binary.LittleEndian, data\n }\n \n fatalf("cannot parse gcc output %s as ELF, Mach-O, PE object", gccTmp())\n```
## コアとなるコードの解説
変更の中心は、`src/cmd/cgo/gcc.go`ファイルの`gccDebug`関数内、PEファイル処理ブロックです。
1. **変更前**:
```go
if f, err := pe.Open(gccTmp()); err == nil {
d, err := f.DWARF()
if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
}
return d, binary.LittleEndian, nil // ここで `data` を `nil` で返していた
}
```
以前のコードでは、PEファイルからDWARF情報を読み込んだ後、`data`というデバッグデータブロックを`nil`として返していました。これは、PEファイル内の特定のデバッグデータ(特に`__cgodebug_data`)を無視していたことを意味します。
2. **変更後**:
```go
if f, err := pe.Open(gccTmp()); err == nil {
d, err := f.DWARF()
if err != nil {
fatalf("cannot load DWARF output from %s: %v", gccTmp(), err)
}
var data []byte // data変数を初期化
for _, s := range f.Symbols { // PEファイルのシンボルを走査
if s.Name == "_" + "__cgodebug_data" { // "__cgodebug_data"シンボルを探す
if i := int(s.SectionNumber) - 1; 0 <= i && i < len(f.Sections) {
sect := f.Sections[i] // シンボルが属するセクションを取得
if s.Value < sect.Size { // シンボルの値がセクションサイズ内か確認
if sdat, err := sect.Data(); err == nil {
data = sdat[s.Value:] // セクションデータからシンボルが指す部分を抽出
}
}
}
}
}
return d, binary.LittleEndian, data // 抽出した `data` を返す
}
```
変更後では、`pe.Open`でPEファイルを開き、`f.DWARF()`でDWARF情報を取得するまでは同じです。しかし、その後、`f.Symbols`(PEファイルのシンボルテーブル)をループで走査します。
* `s.Name == "_" + "__cgodebug_data"`: ここで、特定のシンボル名`__cgodebug_data`を探しています。このシンボルは、`cgo`が生成するコード内で列挙型などのデバッグ情報を格納するために使用される特別なデータブロックを指していると考えられます。Goの内部的な規約で、Cのシンボル名に`_`がプレフィックスとして付与されることがあるため、`"_" + "__cgodebug_data"`という形式になっています。
* シンボルが見つかった場合、そのシンボルが属するセクション(`f.Sections[i]`)を取得し、セクションのデータ(`sect.Data()`)を読み込みます。
* `data = sdat[s.Value:]`: シンボルの`Value`フィールドは、そのシンボルが指すデータのオフセットを示します。ここでは、セクションデータ`sdat`から、このオフセット以降のバイト列を`data`変数に格納しています。
* 最終的に、`d`(DWARFデータ)、`binary.LittleEndian`(バイトオーダー)、そして抽出された`data`が返されます。この`data`には、Windows環境での列挙型に関するデバッグ情報が含まれていると推測されます。
この修正により、`cgo`はWindowsのPEファイルから必要なデバッグデータを抽出し、デバッガが列挙型をより適切に表示できるようになります。
## 関連リンク
* Go言語の公式リポジトリ: [https://github.com/golang/go](https://github.com/golang/go)
* Go言語のコードレビューシステム (Gerrit): [https://golang.org/cl/6545047](https://golang.org/cl/6545047) (コミットメッセージに記載されている変更リストへのリンク)
## 参考にした情報源リンク
* DWARF Debugging Standard: [https://dwarfstd.org/](https://dwarfstd.org/)
* Portable Executable (PE) Format: Microsoftの公式ドキュメントや、PEファイルフォーマットに関する技術記事
* Go言語の`debug/pe`パッケージのドキュメント: [https://pkg.go.dev/debug/pe](https://pkg.go.dev/debug/pe)
* Go言語の`debug/dwarf`パッケージのドキュメント: [https://pkg.go.dev/debug/dwarf](https://pkg.go.dev/debug/dwarf)
* Go言語の`cmd/cgo`に関するドキュメントやソースコード