Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 13541] ファイルの概要

本コミットは、Go言語のCgoツールにおけるデバッグ情報生成に関する修正です。具体的には、__cgodebug_dataセクションの最終エントリに0ではなく1を使用するように変更することで、LLVMベースのGCCが生成するオブジェクトファイルがGoのデバッグツール(特にdebug/machoパッケージ)で正しく処理されるようにします。

コミット

commit dd62bb4753147ff832eb769af892054fb1562c7c
Author: Shenghou Ma <minux.ma@gmail.com>
Date:   Mon Jul 30 18:44:48 2012 -0400

    cmd/cgo: use 1 as last entry for __cgodebug_data
            LLVM-based gcc will place all-zero data in a zero-filled
            section, but our debug/macho can't handle that.
            Fixes #3821.
    
    R=golang-dev, rsc
    CC=golang-dev
    https://golang.org/cl/6444049

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/dd62bb4753147ff832eb769af892054fb1562c7c

元コミット内容

cmd/cgo: use 1 as last entry for __cgodebug_data LLVM-based gcc will place all-zero data in a zero-filled section, but our debug/macho can't handle that. Fixes #3821.

変更の背景

この変更は、GoのCgoツールが生成するデバッグ情報が、LLVMベースのGCCコンパイラでコンパイルされた際に問題を引き起こすというバグ(Issue #3821)を修正するために行われました。

従来のCgoは、__cgodebug_dataというデバッグ情報セクションの最終エントリに0を書き込んでいました。しかし、LLVMベースのGCC(例えばClang)は、全てがゼロで初期化されたデータ(all-zero data)を、最適化の一環として「ゼロフィルセクション(zero-filled section)」、特にMach-O形式では__DATA.__commonのようなセクションに配置する傾向があります。

Goのdebug/machoパッケージは、Mach-O形式の実行ファイルやライブラリからデバッグ情報を読み取るためのものです。このパッケージが、ゼロフィルセクションに配置された__cgodebug_dataセクションを正しく処理できないという問題がありました。具体的には、ゼロフィルセクションはファイルサイズを削減するために、実際にゼロが書き込まれているわけではなく、実行時にOSがゼロで埋めることを期待する領域としてマークされます。debug/machoがこの特殊なセクションの扱いを考慮していなかったため、デバッグ情報の読み取りに失敗していました。

この問題を解決するため、__cgodebug_dataの最終エントリを0ではなく1にすることで、データが全てゼロであるという条件を満たさなくなり、LLVMベースのGCCがこのセクションをゼロフィルセクションに配置するのを防ぎます。これにより、debug/machoが期待する形式でデバッグ情報が生成され、正しく読み取れるようになります。

前提知識の解説

  • Cgo: Go言語の機能の一つで、C言語のコードをGoプログラムから呼び出すことを可能にします。Cgoは、GoとCの間のインターフェースコードを生成し、コンパイルプロセスを管理します。
  • デバッグ情報: プログラムの実行中にデバッガがソースコードレベルで情報を表示したり、変数の値を検査したりするために必要な情報です。これには、ソースファイル名、行番号、変数名、型情報などが含まれます。
  • Mach-O (Mach Object file format): macOS、iOS、watchOS、tvOSなどのAppleのオペレーティングシステムで使用される実行可能ファイル、オブジェクトコード、共有ライブラリ、ダイナミックロード可能バンドル、およびコアダンプのファイル形式です。
  • セクション (Section): 実行可能ファイルやオブジェクトファイルは、コード、データ、デバッグ情報などを格納するために複数のセクションに分割されます。例えば、.textセクションは実行可能なコードを、.dataセクションは初期化されたデータを、.bssセクションは初期化されていないデータを格納します。
  • ゼロフィルセクション (Zero-filled section): プログラムの実行開始時にOSによってゼロで埋められることが保証されているセクションです。これにより、ファイルサイズを削減できます。Mach-Oでは、__DATA.__common__BSSなどがこれに該当します。
  • LLVM (Low Level Virtual Machine): コンパイラ技術のオープンソースプロジェクトです。LLVMは、コンパイラのバックエンド、最適化、コード生成など、コンパイラツールチェーンの様々な側面を提供します。
  • GCC (GNU Compiler Collection): GNUプロジェクトによって開発されたコンパイラ群です。C、C++、Objective-C、Fortran、Ada、Goなどの多くのプログラミング言語をサポートしています。
  • LLVMベースのGCC: GCC自体はLLVMとは異なるコンパイラですが、この文脈では、LLVMの技術(例えばClangフロントエンド)を利用してGCCの機能を提供するようなコンパイラ、あるいはLLVMツールチェーンの一部としてGCCの機能が提供される環境を指している可能性があります。特にmacOSでは、Xcodeに付属するgccコマンドが実質的にClang(LLVMベースのC/C++/Objective-Cコンパイラ)のラッパーである場合が多いです。Clangは、ゼロ初期化されたデータを効率的に扱うために、ゼロフィルセクションに配置する最適化を行います。
  • Issue #3821: Goプロジェクトのバグトラッカーで報告された問題の識別子です。このコミットがこの特定のバグを修正したことを示します。

技術的詳細

GoのCgoツールは、CコードとGoコードの連携を可能にするために、いくつかの内部的な処理を行います。その一つが、デバッグ情報の生成です。Cgoは、GoのランタイムがCgo呼び出しに関するデバッグ情報を取得できるように、__cgodebug_dataという名前のカスタムセクションを生成します。このセクションには、Cgo呼び出しに関連するメタデータが含まれます。

問題は、この__cgodebug_dataセクションの構造にありました。Cgoは、このセクションの終端を示すマーカーとして、全ての要素がゼロであるエントリを配置していました。

// 簡略化した概念的な表現
struct CgoDebugEntry {
    // ... デバッグ情報 ...
};

struct CgoDebugEntry __cgodebug_data[] = {
    { /* エントリ1 */ },
    { /* エントリ2 */ },
    // ...
    { 0, 0, 0, ... } // 終端マーカー (全てゼロ)
};

LLVMベースのコンパイラ(特にClang)は、コンパイル時に最適化を行います。その最適化の一つに、全てがゼロで初期化された静的データを、実行可能ファイル内の物理的な領域を占有しない「ゼロフィルセクション」に配置するというものがあります。これにより、実行可能ファイルのサイズを削減できます。Mach-O形式では、__DATA.__commonセクションがこのような目的でよく使用されます。

Goのdebug/machoパッケージは、Mach-Oファイルを解析してデバッグ情報を読み取るためのライブラリです。このライブラリは、通常のデータセクションから情報を読み取ることを想定していましたが、ゼロフィルセクションに配置されたデータ(特に__DATA.__common)の特殊な扱いを考慮していませんでした。ゼロフィルセクションは、ファイル内では単にその領域が存在することを示すだけで、実際のゼロバイトは含まれていません。実行時にローダーがその領域をゼロで埋めます。

したがって、__cgodebug_dataセクションが全てゼロの終端マーカーを持つためにゼロフィルセクションに配置されると、debug/machoがそのセクションを読み取ろうとした際に、期待するデータ(ゼロバイト)が見つからず、デバッグ情報の解析に失敗していました。

このコミットでは、この問題を回避するために、__cgodebug_dataセクションの最終エントリを0ではなく1に設定するように変更しました。

// 変更後の概念的な表現
struct CgoDebugEntry __cgodebug_data[] = {
    { /* エントリ1 */ },
    { /* エントリ2 */ },
    // ...
    { 1, 0, 0, ... } // 終端マーカー (最初の要素が1)
};

これにより、最終エントリが「全てゼロ」ではなくなるため、LLVMベースのGCCはこれをゼロフィルセクションに配置する最適化を行わなくなります。結果として、__cgodebug_dataセクションは通常のデータセクションに配置され、debug/machoが正しく読み取れるようになります。この変更は、デバッグ情報の意味論的な内容には影響を与えず、単にコンパイラの最適化挙動を回避するためのテクニックです。

コアとなるコードの変更箇所

変更は src/cmd/cgo/gcc.go ファイルの loadDWARF 関数内で行われています。

--- a/src/cmd/cgo/gcc.go
+++ b/src/cmd/cgo/gcc.go
@@ -508,7 +508,12 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
 			fmt.Fprintf(&b, "\t0,\n")
 		}
 	}
-	fmt.Fprintf(&b, "\t0\n")
+	// for the last entry, we can not use 0, otherwise
+	// in case all __cgodebug_data is zero initialized,
+	// LLVM-based gcc will place the it in the __DATA.__common
+	// zero-filled section (our debug/macho doesn't support
+	// this)
+	fmt.Fprintf(&b, "\t1\n")
 	fmt.Fprintf(&b, "};\\n")
 
 	d, bo, debugData := p.gccDebug(b.Bytes())

コアとなるコードの解説

src/cmd/cgo/gcc.go は、CgoがCコンパイラ(この場合はGCC)を呼び出してCコードをコンパイルし、GoとCの間のリンケージを処理する部分を担っています。

loadDWARF 関数は、Cgoが生成するデバッグ情報の一部であるDWARF(Debugging With Attributed Record Formats)情報を処理する際に、__cgodebug_dataというC言語の配列を生成するためのコードを書き出しています。この配列は、GoのランタイムがCgo呼び出しに関するデバッグ情報を取得するために使用されます。

変更前のコードでは、fmt.Fprintf(&b, "\t0\n") という行で、__cgodebug_data配列の最終エントリとして数値の 0 を書き込んでいました。これは、配列の終端を示すマーカーとして機能していました。

変更後のコードでは、この行が fmt.Fprintf(&b, "\t1\n") に変更されています。これにより、最終エントリとして 1 が書き込まれるようになります。

追加されたコメントが変更の理由を明確に説明しています。 // for the last entry, we can not use 0, otherwise // in case all __cgodebug_data is zero initialized, // LLVM-based gcc will place the it in the __DATA.__common // zero-filled section (our debug/macho doesn't support // this)

このコメントは、最終エントリに0を使用できない理由を述べています。もし__cgodebug_data全体がゼロで初期化される場合(つまり、他のエントリも全てゼロで、最終エントリもゼロの場合)、LLVMベースのGCCはこれを__DATA.__commonというゼロフィルセクションに配置します。しかし、Goのdebug/machoパッケージはこのゼロフィルセクションの扱いをサポートしていないため、問題が発生するという背景が説明されています。最終エントリを1にすることで、この条件を回避し、セクションが通常のデータセクションに配置されるようにしています。

関連リンク

参考にした情報源リンク