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

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

このコミットは、Go言語のデバッグ情報パーサーであるdebug/dwarfパッケージに、DWARF 4形式の新しい定数を追加し、それらのパースに対応するための変更を加えています。これにより、Clangコンパイラが生成するDWARF 4形式のデバッグ情報を正しく処理できるようになります。

コミット

  • コミットハッシュ: 7dbbb53f3743aa1a654d75dd43ed4affc3ddc23d
  • Author: Russ Cox rsc@golang.org
  • Date: Tue Oct 29 10:36:51 2013 -0400

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

https://github.com/golang/go/commit/7dbbb53f3743aa1a654d75dd43ed4affc3ddc23d

元コミット内容

debug/dwarf: add DWARF 4 form constants

Some versions of clang generate DWARF 4-format attributes
even when using -gdwarf-2. We don't care much about the
values, but we do need to be able to parse past them.

This fixes a bug in Go 1.2 rc2 reported via private mail using
a near-tip version of clang.

R=golang-dev, iant, dvyukov
CC=golang-dev
https://golang.org/cl/18460043

変更の背景

この変更の背景には、Go 1.2リリース候補2(rc2)におけるデバッグ情報のパースに関するバグがありました。具体的には、一部のバージョンのClangコンパイラが、-gdwarf-2オプション(DWARFバージョン2形式のデバッグ情報を生成するよう指示するオプション)を使用しているにもかかわらず、内部的にDWARF 4形式の属性を生成してしまうという問題がありました。

Goのdebug/dwarfパッケージは、実行可能ファイルに埋め込まれたDWARFデバッグ情報を解析するために使用されます。このパッケージがDWARF 4形式の新しい属性を認識できない場合、パースエラーが発生し、デバッグ情報が正しく読み取れないという問題が生じます。

このコミットは、Clangが生成するこれらのDWARF 4形式の属性をGoのデバッガが「パースしてスキップする」ことができるようにすることを目的としています。つまり、これらの属性の具体的な値に深い関心があるわけではなく、それらを正しく読み飛ばして、残りのデバッグ情報のパースを続行できるようにすることが重要でした。この修正は、プライベートメールで報告された、Clangの最新に近いバージョンを使用したバグ報告に対応するものです。

前提知識の解説

DWARFとは

DWARF (Debugging With Arbitrary Record Formats) は、プログラムのソースコードとコンパイルされたバイナリコードとの間のマッピングを記述するための標準的なデバッグ情報形式です。コンパイラによって生成され、実行可能ファイルや共有ライブラリに埋め込まれます。デバッガはDWARF情報を使用して、ソースコードレベルでのデバッグ(変数名の解決、行番号へのマッピング、スタックトレースの表示など)を可能にします。

DWARFは、以下のような情報を含みます。

  • コンパイル単位 (Compilation Unit): ソースファイルごとの情報。
  • 型情報 (Type Information): 変数や関数のデータ型。
  • 変数情報 (Variable Information): 変数の名前、型、メモリ上の位置。
  • 関数情報 (Function Information): 関数の名前、引数、戻り値、コードの範囲。
  • 行番号情報 (Line Number Information): バイナリコードのアドレスとソースコードの行番号のマッピング。
  • ロケーション情報 (Location Information): 変数がレジスタやメモリのどこに存在するかを示す式。

DWARFのバージョン

DWARFには複数のバージョンがあり、それぞれ新しい機能や改善が導入されています。このコミットで言及されているのは、DWARF 2とDWARF 4です。

  • DWARF 2: 広く使用されているバージョンで、多くのシステムでサポートされています。
  • DWARF 4: DWARF 3の後継として登場し、いくつかの新しい属性、フォーム、オペコードが導入されました。特に、型シグネチャ(Type Signature)やセクションオフセット(Section Offset)の扱いが改善されています。

DWARFの「フォーム (Form)」

DWARFでは、デバッグ情報の属性(例えば、変数の値、型、ロケーションなど)がどのようにエンコードされるかを「フォーム」で定義します。フォームは、属性のデータの種類(整数、文字列、参照、オフセットなど)とサイズを示します。

このコミットで追加された主要なDWARF 4のフォーム定数は以下の通りです。

  • DW_FORM_sec_offset:
    • DWARF 4で導入されました。
    • ELFセクションや実行可能ファイルまたはライブラリの他のサブコンポーネントへのオフセットを表すために使用されます。
    • 32ビットDWARFでは4バイト、64ビットDWARFでは8バイトのサイズを持ちます。
    • DWARF 2および3では、この目的のためにDW_FORM_data4またはDW_FORM_data8が使用されていました。
    • 主に、DW_AT_stmt_list (行番号テーブルへのオフセット)、DW_AT_loclist (ロケーションリストへのオフセット)、DW_AT_macinfo (マクロ情報へのオフセット)、DW_AT_rangelist (アドレス範囲リストへのオフセット) などの属性で使用されます。
  • DW_FORM_exprloc:
    • DWARF 4で導入されました。
    • DWARF式(DWARF Expression)を示すために使用されます。DWARF式は、値に評価されるか、またはロケーションを記述します。
    • このフォームのデータは、符号なしLEB128形式の長さ(式のバイト数)に続き、その指定された長さのバイトシーケンス(式自体)で構成されます。
  • DW_FORM_ref_sig8:
    • DWARF 4で導入されました。
    • 型シグネチャ(Type Signature)を参照するために使用される参照型です。
    • 特に、分割DWARFオブジェクト(Split DWARF)のようなシナリオで重要になります。これは、参照される型の定義が別のオブジェクトファイルに存在する可能性がある場合に利用されます。

技術的詳細

このコミットは、Goのdebug/dwarfパッケージがDWARF 4形式の新しいフォーム定数を認識し、それらを適切にパースできるようにするためのものです。

debug/dwarfパッケージは、DWARF情報を読み取る際に、各属性のフォームに基づいてデータを解釈します。新しいフォームが導入された場合、そのフォームに対応する読み取りロジックを追加する必要があります。

具体的には、以下の点が技術的な詳細として挙げられます。

  1. 新しいフォーム定数の定義: src/pkg/debug/dwarf/const.goに、formSecOffsetformExprlocformFlagPresentformRefSig8といったDWARF 4の新しいフォーム定数が追加されました。これらは、DWARF仕様で定義されている対応するDW_FORM_定数の内部表現です。
  2. entry.goでのパースロジックの拡張: src/pkg/debug/dwarf/entry.go内のbuf.entryメソッドは、DWARFエントリの属性を読み取る主要なロジックを含んでいます。このメソッド内のswitch文が拡張され、新しいフォーム定数に対応するケースが追加されました。
    • formSecOffsetの処理: このフォームはセクションオフセットを表すため、DWARFのビット幅(32ビットか64ビットか)に応じて、uint32()またはuint64()を呼び出してオフセット値を読み取ります。dwarf64()メソッドで現在のDWARFのビット幅を確認し、それに基づいて適切なサイズの整数を読み込むロジックが実装されています。
    • formExprlocの処理: このフォームはDWARF式を表すため、まずLEB128形式で式の長さを読み取り(b.uint())、その長さに基づいてバイト列を読み取ります(b.bytes(int(b.uint())))。
    • formRefSig8の処理: このフォームは64ビットの型シグネチャ参照を表すため、b.uint64()を呼び出して64ビットの符号なし整数を読み取ります。
  3. 既存のformFlagPresentのコメント追加: formFlagPresentはDWARF 4で導入されたフォームですが、このコミット以前から存在していました。今回のコミットで「New in DWARF 4.」というコメントが追加され、その由来が明確化されました。
  4. Clangとの互換性: コミットメッセージにあるように、Clangが-gdwarf-2を指定してもDWARF 4形式の属性を生成するケースがあるため、Goのデバッガがこれらの新しいフォームを認識し、パースできることが重要でした。これにより、GoのツールチェーンがClangでコンパイルされたバイナリのデバッグ情報をより堅牢に扱えるようになります。

この変更は、Goのデバッガが最新のコンパイラツールチェーン(特にClang)によって生成される多様なDWARF形式に対応し、デバッグ情報の互換性と堅牢性を向上させる上で不可欠なものです。

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

src/pkg/debug/dwarf/const.go

--- a/src/pkg/debug/dwarf/const.go
+++ b/src/pkg/debug/dwarf/const.go
@@ -207,7 +207,10 @@ const (
 	formRef8        format = 0x14
 	formRefUdata    format = 0x15
 	formIndirect    format = 0x16
+	formSecOffset   format = 0x17
+	formExprloc     format = 0x18
 	formFlagPresent format = 0x19
+	formRefSig8     format = 0x20
 )

 // A Tag is the classification (the type) of an Entry.

src/pkg/debug/dwarf/entry.go

--- a/src/pkg/debug/dwarf/entry.go
+++ b/src/pkg/debug/dwarf/entry.go
@@ -188,6 +188,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
 		// flag
 		case formFlag:
 			val = b.uint8() == 1
+		// New in DWARF 4.
 		case formFlagPresent:
 			// The attribute is implicitly indicated as present, and no value is
 			// encoded in the debugging information entry itself.
@@ -236,6 +237,30 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
 			b.err = b1.err
 			return nil
 		}
+
+		// lineptr, loclistptr, macptr, rangelistptr
+		// New in DWARF 4, but clang can generate them with -gdwarf-2.
+		// Section reference, replacing use of formData4 and formData8.
+		case formSecOffset:
+			is64, known := b.format.dwarf64()
+			if !known {
+				b.error("unknown size for DW_FORM_sec_offset")
+			} else if is64 {
+				val = int64(b.uint64())
+			} else {
+				val = int64(b.uint32())
+			}
+
+		// exprloc
+		// New in DWARF 4.
+		case formExprloc:
+			val = b.bytes(int(b.uint()))
+
+		// reference
+		// New in DWARF 4.
+		case formRefSig8:
+			// 64-bit type signature.
+			val = b.uint64()
 		}
 		e.Field[i].Val = val
 	}

コアとなるコードの解説

src/pkg/debug/dwarf/const.goの変更

このファイルでは、DWARFのフォーム定数がGoのformat型として定義されています。追加された行は、DWARF 4で導入された新しいフォームに対応する定数を定義しています。

  • formSecOffset = 0x17: DW_FORM_sec_offsetに対応。
  • formExprloc = 0x18: DW_FORM_exprlocに対応。
  • formRefSig8 = 0x20: DW_FORM_ref_sig8に対応。

これらの定数は、debug/dwarfパッケージがDWARF情報をパースする際に、どのフォームのデータを読み取るべきかを識別するために使用されます。

src/pkg/debug/dwarf/entry.goの変更

このファイルは、DWARFエントリ(デバッグ情報の論理的な塊)をパースするロジックを含んでいます。func (b *buf) entry(...)メソッドは、DWARFエントリ内の各属性を読み取る際に、その属性のフォームに基づいて適切な読み取り関数を呼び出します。

変更点では、switch文に新しいcaseが追加され、それぞれの新しいDWARF 4フォームに対応するデータ読み取りロジックが実装されています。

  • case formFlagPresent:: 既存のformFlagPresentのケースに「New in DWARF 4.」というコメントが追加されました。これは、このフォームがDWARF 4で導入されたものであることを明示しています。
  • case formSecOffset::
    • このケースは、DW_FORM_sec_offsetを処理します。
    • b.format.dwarf64()を呼び出して、現在のDWARFのフォーマットが64ビットであるかどうかを判断します。これは、セクションオフセットのサイズがDWARFのビット幅に依存するためです。
    • もしdwarf64()trueを返せば64ビットオフセットとしてb.uint64()を読み込み、そうでなければ32ビットオフセットとしてb.uint32()を読み込みます。
    • !knownの場合(DWARFのビット幅が不明な場合)はエラーを報告します。
    • コメントで「New in DWARF 4, but clang can generate them with -gdwarf-2.」とあり、Clangの挙動に対応していることが強調されています。
  • case formExprloc::
    • このケースは、DW_FORM_exprlocを処理します。
    • b.uint()を呼び出して、DWARF式の長さをLEB128形式で読み取ります。
    • その長さをintにキャストし、b.bytes()を呼び出して、その長さ分のバイト列(DWARF式自体)を読み取ります。
    • 「New in DWARF 4.」とコメントされています。
  • case formRefSig8::
    • このケースは、DW_FORM_ref_sig8を処理します。
    • b.uint64()を呼び出して、64ビットの型シグネチャを読み取ります。
    • 「New in DWARF 4.」とコメントされています。

これらの変更により、Goのdebug/dwarfパッケージは、DWARF 4形式のデバッグ情報に含まれるこれらの新しいフォームを正しく解釈し、パースエラーを回避できるようになりました。これにより、Goのデバッグツールがより広範なコンパイラとデバッグ情報形式に対応できるようになり、Go開発者のデバッグ体験が向上します。

関連リンク

参考にした情報源リンク