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

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

このコミットは、Go言語のデバッグ情報処理ライブラリである debug/dwarf および debug/elf パッケージに、DWARF 4形式の型情報(Type Unit)の読み込みサポートを追加するものです。具体的には、DWARF 4で導入された .debug_types セクションの解析機能と、構造体フィールドのバイトオフセット表現に関するDWARF 3の変更に対応しています。

コミット

commit bd997b24f7a7cdb57bb9bf02fbfacef1f59f0f89
Author: Ian Lance Taylor <iant@golang.org>
Date:   Mon Jan 27 10:18:22 2014 -0800

    debug/dwarf, debug/elf: add support for reading DWARF 4 type info
    
    In DWARF 4 the debug info for large types is put into
    .debug_type sections, so that the linker can discard duplicate
    info.  This change adds support for reading type units.
    
    Another small change included here is that DWARF 3 supports
    storing the byte offset of a struct field as a formData rather
    than a formDwarfBlock.
    
    R=golang-codereviews, r
    CC=golang-codereviews
    https://golang.org/cl/56300043

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

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

元コミット内容

debug/dwarf および debug/elf パッケージに、DWARF 4の型情報読み込みサポートを追加。 DWARF 4では、大きな型のデバッグ情報が .debug_types セクションに配置され、リンカが重複情報を破棄できるようになっています。この変更は、型単位(type units)の読み込みサポートを追加します。 また、DWARF 3では、構造体フィールドのバイトオフセットを formData として格納できるようになり、formDwarfBlock ではなくなりました。この小さな変更も含まれています。

変更の背景

このコミットの主な背景は、デバッグ情報フォーマットであるDWARFの進化に対応することです。特に、DWARF 4で導入された新しいメカニズムである「型単位(Type Unit)」のサポートが重要です。

従来のDWARFバージョン(DWARF 3まで)では、すべてのデバッグ情報、特に型定義は .debug_info セクションにまとめて格納されていました。しかし、大規模なプログラムでは、同じ型が複数のコンパイル単位(ソースファイル)で定義されたり、インクルードされたりすることが頻繁にあります。これにより、.debug_info セクションに重複する型情報が大量に生成され、デバッグ情報のサイズが肥大化するという問題がありました。これは、コンパイル時間、リンク時間、そして最終的なバイナリサイズに悪影響を及ぼします。

DWARF 4は、この問題を解決するために .debug_types セクションを導入しました。 .debug_types セクションは、独立した型定義の単位である「型単位(Type Unit)」を格納するために使用されます。各型単位は、単一のプライマリ型とその8バイトのシグネチャを定義します。これにより、リンカは重複する型情報を容易に識別し、破棄できるようになります。結果として、デバッグ情報のサイズを削減し、リンカの効率を向上させることができます。

Go言語のツールチェインが生成するデバッグ情報がDWARF 4に準拠するためには、debug/dwarf パッケージがこの新しい形式を正しく解析できる必要があります。このコミットは、そのための基盤を提供します。

また、DWARF 3における構造体フィールドのバイトオフセットの表現方法の変更(formDwarfBlock から formData へ)も、互換性を保つために対応が必要でした。これは、DWARFの属性値のエンコーディングに関する細かな変更であり、デバッガが正確なオフセット情報を取得するために重要です。

前提知識の解説

DWARF (Debugging With Attributed Record Formats)

DWARFは、プログラムのデバッグ情報を表現するための標準的なフォーマットです。コンパイラによって生成され、実行可能ファイルに埋め込まれます。デバッガはDWARF情報を使用して、ソースコードレベルでのデバッグ(変数名の解決、行番号へのマッピング、スタックトレースの表示など)を可能にします。

DWARFは、主に以下のセクションで構成されます。

  • .debug_info: プログラムの構造(関数、変数、型など)に関する主要なデバッグ情報が格納されます。
  • .debug_abbrev: .debug_info セクションのエントリの構造を定義する省略形式(abbreviations)が格納されます。これにより、.debug_info のサイズを削減できます。
  • .debug_line: ソースコードの行番号と実行可能コードのアドレスのマッピングが格納されます。
  • .debug_str: 文字列定数(変数名、型名など)が格納されます。
  • .debug_aranges: アドレス範囲とコンパイル単位のマッピングが格納されます。
  • .debug_frame: スタックフレームのレイアウトに関する情報が格納されます。

DWARF 4

DWARF 4は、DWARFの主要な改訂版の一つで、いくつかの新機能と改善が導入されました。このコミットに関連する最も重要な変更点は、型単位(Type Unit)とそれらを格納する.debug_types セクションの導入です。

  • 型単位(Type Unit): DWARF 4で導入された新しい種類のコンパイル単位です。これは、単一の型定義とその型を識別するための8バイトのシグネチャを含みます。型単位は、.debug_types セクションに格納されます。
  • .debug_types セクション: 型単位を格納するための新しいセクションです。これにより、リンカは重複する型定義を識別し、破棄することが可能になり、デバッグ情報のサイズを削減できます。他のデバッグ情報セクション(例: .debug_info)は、formRefSig8 という新しい形式の参照を使用して、.debug_types セクション内の型単位を参照します。

DWARF Attributes and Forms

DWARFでは、デバッグ情報エントリ(DIE: Debugging Information Entry)が、プログラム要素(変数、関数、型など)の属性を記述します。各属性は、その値の形式(Form)を持ちます。

  • formDwarfBlock: 可変長のバイト列を格納するための形式です。DWARF式(DWARF Expression)など、バイトコードのシーケンスを表現するのに使われます。
  • formData: 固定長または可変長の数値データを格納するための形式です。このコミットでは、構造体フィールドのバイトオフセットが formData として格納されるようになったことが言及されています。これは、以前は formDwarfBlock で表現されていた可能性があり、より直接的な数値表現への変更を示唆しています。
  • formRefSig8: DWARF 4で導入された新しい形式で、8バイトのシグネチャを使用して .debug_types セクション内の型単位を参照します。

ELF (Executable and Linkable Format)

ELFは、Unix系システムで広く使用されている実行可能ファイル、オブジェクトファイル、共有ライブラリの標準フォーマットです。デバッグ情報は通常、ELFファイル内の特定のセクションに埋め込まれます。debug/elf パッケージは、GoプログラムがELFファイルを解析し、その中のデバッグ情報セクションにアクセスするための機能を提供します。

技術的詳細

このコミットは、主に src/pkg/debug/dwarfsrc/pkg/debug/elf パッケージに影響を与えます。

debug/dwarf パッケージの変更点

  1. const.go:

    • DWARF 4で導入された新しい format 定数(formSecOffset, formExprloc, formFlagPresent)が追加されています。
    • DWARF 3およびDWARF 4で導入された新しい Tag 定数(TagCondition, TagSharedType, TagTypeUnit, TagRvalueReferenceType, TagTemplateAlias)が追加されています。特に TagTypeUnit は、型単位を表すために重要です。
    • tagNames マップもこれらの新しいタグに対応するように更新されています。
  2. entry.go:

    • Reader 型に clone()offset() メソッドが追加されています。これらは、新しい typeReader インターフェースで使用され、型情報の読み込み時にリーダーの状態を複製したり、現在のオフセットを取得したりするために利用されます。
  3. open.go:

    • Data 構造体に typeSigs map[uint64]*typeUnit が追加されています。これは、型シグネチャ(8バイト)をキーとして、対応する typeUnit をキャッシュするために使用されます。
    • AddTypes(name string, types []byte) error メソッドが追加されています。このメソッドは、.debug_types セクションの生データを Data オブジェクトに追加し、解析を開始します。複数の .debug_types セクションが存在する場合に対応できるようになっています。
  4. type.go:

    • typeReader インターフェースが新しく定義されています。これは、.debug_info セクションと .debug_types セクションのどちらからでも型情報を読み取れるようにするための抽象化です。Reader と新しく導入される typeUnitReader がこのインターフェースを実装します。
    • Type(off Offset) メソッドが readType という新しい内部ヘルパー関数を呼び出すように変更されています。readType は、typeReader インターフェースと型キャッシュを受け取ることで、.debug_info.debug_types の両方から型を読み込むロジックを共通化しています。
    • typeOf クロージャ内で、AttrType 属性の値が Offset だけでなく uint64(型シグネチャ)である場合も処理できるようになっています。uint64 の場合は、d.sigToType(toff) を呼び出して、対応する型単位から型情報を取得します。これは、formRefSig8 形式の参照に対応するものです。
    • 構造体フィールドのオフセットを処理する部分で、AttrDataMemberLoc の値が []byteformDwarfBlock)だけでなく int64formData)である場合も処理できるようになっています。これは、DWARF 3で導入された変更に対応します。
  5. typeunit.go (新規ファイル):

    • typeUnit 構造体が定義されています。これは、.debug_types セクション内の個々の型単位を表します。
    • parseTypes(name string, types []byte) error メソッドが実装されています。このメソッドは、.debug_types セクションの生データを解析し、各型単位のヘッダ情報(長さ、バージョン、アブレビエーションオフセット、シグネチャ、型オフセットなど)を読み取ります。そして、解析された typeUnit オブジェクトを Data.typeSigs マップにシグネチャをキーとして格納します。
    • sigToType(sig uint64) (Type, error) メソッドが実装されています。このメソッドは、与えられた型シグネチャに対応する型単位を見つけ、その型単位から実際の型情報を読み込みます。型情報はキャッシュされます。
    • typeUnitReader 構造体と、それが typeReader インターフェースを実装するメソッド(Seek, Next, clone, offset)が定義されています。これにより、debug/dwarf の既存の型解析ロジックが、.debug_types セクション内の型単位に対しても透過的に動作するようになります。
  6. unit.go:

    • parseUnits 関数で、サポートするDWARFバージョンに 4 が追加されています。

debug/elf パッケージの変更点

  1. file.go:
    • DWARF() メソッドが変更されています。従来の .debug_info などのセクションを解析した後、ELFファイル内の .debug_types セクションを検索するロジックが追加されています。
    • .debug_types セクションが見つかった場合、そのデータを dwarf.Data.AddTypes() メソッドに渡して、DWARF 4の型単位の解析を開始します。
    • リロケーション(SHT_RELA または SHT_REL タイプで、.debug_types セクションを参照するもの)も適用されるようになっています。これは、.debug_types セクション内のオフセットが、リンカによって調整される可能性があるためです。

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

src/pkg/debug/dwarf/const.go

--- a/src/pkg/debug/dwarf/const.go
+++ b/src/pkg/debug/dwarf/const.go
@@ -207,6 +207,7 @@ const (
 	formRef8        format = 0x14
 	formRefUdata    format = 0x15
 	formIndirect    format = 0x16
+	// The following are new in DWARF 4.
 	formSecOffset   format = 0x17
 	formExprloc     format = 0x18
 	formFlagPresent format = 0x19
@@ -264,15 +265,22 @@ const (
 	TagVariantPart            Tag = 0x33
 	TagVariable               Tag = 0x34
 	TagVolatileType           Tag = 0x35
-	TagDwarfProcedure         Tag = 0x36
-	TagRestrictType           Tag = 0x37
-	TagInterfaceType          Tag = 0x38
-	TagNamespace              Tag = 0x39
-	TagImportedModule         Tag = 0x3A
-	TagUnspecifiedType        Tag = 0x3B
-	TagPartialUnit            Tag = 0x3C
-	TagImportedUnit           Tag = 0x3D
-	TagMutableType            Tag = 0x3E
+	// The following are new in DWARF 3.
+	TagDwarfProcedure  Tag = 0x36
+	TagRestrictType    Tag = 0x37
+	TagInterfaceType   Tag = 0x38
+	TagNamespace       Tag = 0x39
+	TagImportedModule  Tag = 0x3A
+	TagUnspecifiedType Tag = 0x3B
+	TagPartialUnit     Tag = 0x3C
+	TagImportedUnit    Tag = 0x3D
+	TagMutableType     Tag = 0x3E // Later removed from DWARF.
+	TagCondition       Tag = 0x3F
+	TagSharedType      Tag = 0x40
+	// The following are new in DWARF 4.
+	TagTypeUnit            Tag = 0x41
+	TagRvalueReferenceType Tag = 0x42
+	TagTemplateAlias       Tag = 0x43
 )
 
 var tagNames = [...]string{
@@ -332,6 +340,11 @@ var tagNames = [...]string{
 	TagPartialUnit:            "PartialUnit",
 	TagImportedUnit:           "ImportedUnit",
 	TagMutableType:            "MutableType",
+	TagCondition:              "Condition",
+	TagSharedType:             "SharedType",
+	TagTypeUnit:               "TypeUnit",
+	TagRvalueReferenceType:    "RvalueReferenceType",
+	TagTemplateAlias:          "TemplateAlias",
 }
 
 func (t Tag) String() string {

src/pkg/debug/dwarf/open.go

--- a/src/pkg/debug/dwarf/open.go
+++ b/src/pkg/debug/dwarf/open.go
@@ -26,6 +26,7 @@ type Data struct {
 	abbrevCache map[uint32]abbrevTable
 	order       binary.ByteOrder
 	typeCache   map[Offset]Type
+	typeSigs    map[uint64]*typeUnit
 	unit        []unit
 }
 
@@ -49,6 +50,7 @@ func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Dat
 		str:         str,
 		abbrevCache: make(map[uint32]abbrevTable),
 		typeCache:   make(map[Offset]Type),
+		typeSigs:    make(map[uint64]*typeUnit),
 	}
 
 	// Sniff .debug_info to figure out byte order.
@@ -75,3 +77,11 @@ func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Dat
 	d.unit = u
 	return d, nil
 }
+
+// AddTypes will add one .debug_types section to the DWARF data.  A
+// typical object with DWARF version 4 debug info will have multiple
+// .debug_types sections.  The name is used for error reporting only,
+// and serves to distinguish one .debug_types section from another.
+func (d *Data) AddTypes(name string, types []byte) error {
+	return d.parseTypes(name, types)
+}

src/pkg/debug/dwarf/type.go

--- a/src/pkg/debug/dwarf/type.go
+++ b/src/pkg/debug/dwarf/type.go
@@ -251,23 +251,37 @@ func (t *TypedefType) String() string { return t.Name }\n \n func (t *TypedefType) Size() int64 { return t.Type.Size() }\n \n+// typeReader is used to read from either the info section or the\n+// types section.\n+type typeReader interface {\n+\tSeek(Offset)\n+\tNext() (*Entry, error)\n+\tclone() typeReader\n+\toffset() Offset\n+}\n+\n+// Type reads the type at off in the DWARF ``info'' section.\n func (d *Data) Type(off Offset) (Type, error) {\n-\tif t, ok := d.typeCache[off]; ok {\n+\treturn d.readType("info", d.Reader(), off, d.typeCache)\n+}\n+\n+// readType reads a type from r at off of name using and updating a\n+// type cache.\n+func (d *Data) readType(name string, r typeReader, off Offset, typeCache map[Offset]Type) (Type, error) {\n+\tif t, ok := typeCache[off]; ok {\n \t\treturn t, nil\n \t}\n-\n-\tr := d.Reader()\n \tr.Seek(off)\n \te, err := r.Next()\n \tif err != nil {\n \t\treturn nil, err\n \t}\n \tif e == nil || e.Offset != off {\n-\t\treturn nil, DecodeError{"info", off, "no type at offset"}\n+\t\treturn nil, DecodeError{name, off, "no type at offset"}\n \t}\n \n \t// Parse type from Entry.\n-\t// Must always set d.typeCache[off] before calling\n+\t// Must always set typeCache[off] before calling\n \t// d.Type recursively, to handle circular types correctly.\n \tvar typ Type\n \n@@ -290,7 +304,7 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t\t\treturn nil\n \t\t\t}\n \t\t\tif kid == nil {\n-\t\t\t\terr = DecodeError{"info", r.b.off, "unexpected end of DWARF entries"}\n+\t\t\t\terr = DecodeError{name, r.offset(), "unexpected end of DWARF entries"}\n \t\t\t\treturn nil\n \t\t\t}\n \t\t\tif kid.Tag == 0 {\n@@ -313,15 +327,21 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t// Get Type referred to by Entry's AttrType field.\n \t// Set err if error happens.  Not having a type is an error.\n \ttypeOf := func(e *Entry) Type {\n-\t\ttoff, ok := e.Val(AttrType).(Offset)\n-\t\tif !ok {\n+\t\t\ttval := e.Val(AttrType)\n+\t\t\tvar t Type\n+\t\t\tswitch toff := tval.(type) {\n+\t\t\tcase Offset:\n+\t\t\t\tif t, err = d.readType(name, r.clone(), toff, typeCache); err != nil {\n+\t\t\t\t\treturn nil\n+\t\t\t\t}\n+\t\t\tcase uint64:\n+\t\t\t\tif t, err = d.sigToType(toff); err != nil {\n+\t\t\t\t\treturn nil\n+\t\t\t\t}\n+\t\t\tdefault:\
 \t\t\t// It appears that no Type means "void".\n \t\t\treturn new(VoidType)\n \t\t}\n-\t\tvar t Type\n-\t\tif t, err = d.Type(toff); err != nil {\n-\t\t\treturn nil\n-\t\t}\n \t\treturn t\n \t}\n \n@@ -337,7 +357,7 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t//\tdimensions are in left to right order.\n \t\tt := new(ArrayType)\n \t\ttyp = t\n-\t\td.typeCache[off] = t\n+\t\ttypeCache[off] = t\n \t\tif t.Type = typeOf(e); err != nil {\n \t\t\tgoto Error\n \t\t}\n@@ -363,7 +383,7 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t\t\t}\n \t\t\t\tndim++\n \t\t\tcase TagEnumerationType:\n-\t\t\t\terr = DecodeError{"info", kid.Offset, "cannot handle enumeration type as array bound"}\n+\t\t\t\terr = DecodeError{name, kid.Offset, "cannot handle enumeration type as array bound"}\n \t\t\t\tgoto Error\n \t\t\t}\n \t\t}\n@@ -383,12 +403,12 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\tname, _ := e.Val(AttrName).(string)\n \t\tenc, ok := e.Val(AttrEncoding).(int64)\n \t\tif !ok {\n-\t\t\terr = DecodeError{"info", e.Offset, "missing encoding attribute for " + name}\n+\t\t\terr = DecodeError{name, e.Offset, "missing encoding attribute for " + name}\n \t\t\tgoto Error\n \t\t}\n \t\tswitch enc {\n \t\tdefault:\n-\t\t\terr = DecodeError{"info", e.Offset, "unrecognized encoding attribute value"}\n+\t\t\terr = DecodeError{name, e.Offset, "unrecognized encoding attribute value"}\n \t\t\tgoto Error\n \n \t\tcase encAddress:\n@@ -408,7 +428,7 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\tcase encUnsignedChar:\n \t\t\ttyp = new(UcharType)\n \t\t}\n-\t\td.typeCache[off] = typ\n+\t\ttypeCache[off] = typ\n \t\tt := typ.(interface {\n \t\t\tBasic() *BasicType\n \t\t}).Basic()\n@@ -433,7 +453,7 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t// There is much more to handle C++, all ignored for now.\n \t\tt := new(StructType)\n \t\ttyp = t\n-\t\td.typeCache[off] = t\n+\t\ttypeCache[off] = t\n \t\tswitch e.Tag {\n \t\tcase TagClassType:\n \t\t\tt.Kind = "class"\n@@ -453,12 +473,13 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t\t\tif f.Type = typeOf(kid); err != nil {\n \t\t\t\t\tgoto Error\n \t\t\t\t}\n-\t\t\t\tif loc, ok := kid.Val(AttrDataMemberLoc).([]byte); ok {\n+\t\t\t\tswitch loc := kid.Val(AttrDataMemberLoc).(type) {\n+\t\t\t\tcase []byte:\n \t\t\t\t\t// TODO: Should have original compilation\n \t\t\t\t\t// unit here, not unknownFormat.\n \t\t\t\t\tb := makeBuf(d, unknownFormat{}, "location", 0, loc)\n \t\t\t\t\tif b.uint8() != opPlusUconst {\n-\t\t\t\t\t\terr = DecodeError{"info", kid.Offset, "unexpected opcode"}\n+\t\t\t\t\t\terr = DecodeError{name, kid.Offset, "unexpected opcode"}\n \t\t\t\t\t\tgoto Error\n \t\t\t\t\t}\n \t\t\t\t\tf.ByteOffset = int64(b.uint())\n@@ -466,6 +487,8 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t\t\t\t\terr = b.err\n \t\t\t\t\t\tgoto Error\n \t\t\t\t\t}\n+\t\t\t\tcase int64:\n+\t\t\t\t\tf.ByteOffset = loc\n \t\t\t\t}\n \n \t\t\t\thaveBitOffset := false\n@@ -502,7 +525,7 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t//\tAttrType: subtype\n \t\tt := new(QualType)\n \t\ttyp = t\n-\t\td.typeCache[off] = t\n+\t\ttypeCache[off] = t\n \t\tif t.Type = typeOf(e); err != nil {\n \t\t\tgoto Error\n \t\t}\n@@ -526,7 +549,7 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t//\t\tAttrConstValue: value of constant\n \t\tt := new(EnumType)\n \t\ttyp = t\n-\t\td.typeCache[off] = t\n+\t\ttypeCache[off] = t\n \t\tt.EnumName, _ = e.Val(AttrName).(string)\n \t\tt.Val = make([]*EnumValue, 0, 8)\n \t\tfor kid := next(); kid != nil; kid = next() {\n@@ -552,7 +575,7 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t//\tAttrAddrClass: address class [ignored]\n \t\tt := new(PtrType)\n \t\ttyp = t\n-\t\td.typeCache[off] = t\n+\t\ttypeCache[off] = t\n \t\tif e.Val(AttrType) == nil {\n \t\t\tt.Type = &VoidType{}\n \t\t\tbreak\n@@ -571,7 +594,7 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t//\tTagUnspecifiedParameter: final ...\n \t\tt := new(FuncType)\n \t\ttyp = t\n-\t\td.typeCache[off] = t\n+\t\ttypeCache[off] = t\n \t\tif t.ReturnType = typeOf(e); err != nil {\n \t\t\tgoto Error\n \t\t}\n@@ -598,7 +621,7 @@ func (d *Data) Type(off Offset) (Type, error) {\n \t\t//\tAttrType: type definition [required]\n \t\tt := new(TypedefType)\n \t\ttyp = t\n-\t\td.typeCache[off] = t\n+\t\ttypeCache[off] = t\n \t\tt.Name, _ = e.Val(AttrName).(string)\n \t\tt.Type = typeOf(e)\n \t}\n@@ -620,7 +643,7 @@ Error:\n \t// If the parse fails, take the type out of the cache\n \t// so that the next call with this offset doesn't hit\n \t// the cache and return success.\n-\tdelete(d.typeCache, off)\n+\tdelete(typeCache, off)\n \treturn nil, err\n }\n \ndiff --git a/src/pkg/debug/dwarf/type_test.go b/src/pkg/debug/dwarf/type_test.go\nindex b5b255f6f4..2cb85e74bb 100644\n--- a/src/pkg/debug/dwarf/type_test.go\n+++ b/src/pkg/debug/dwarf/type_test.go\n@@ -73,6 +73,8 @@ func TestTypedefsMachO(t *testing.T) {\n \ttestTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho")\n }\n \n+func TestTypedefsELFDwarf4(t *testing.T) { testTypedefs(t, elfData(t, "testdata/typedef.elf4"), "elf") }\n+\n func testTypedefs(t *testing.T, d *Data, kind string) {\n \tr := d.Reader()\n \tseen := make(map[string]bool)\n```

### `src/pkg/debug/dwarf/typeunit.go` (新規ファイル)

```go
// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package dwarf

import (
	"fmt"
	"strconv"
)

// Parse the type units stored in a DWARF4 .debug_types section.  Each
// type unit defines a single primary type and an 8-byte signature.
// Other sections may then use formRefSig8 to refer to the type.

// The typeUnit format is a single type with a signature.  It holds
// the same data as a compilation unit.
type typeUnit struct {
	unit
	toff  Offset // Offset to signature type within data.
	name  string // Name of .debug_type section.
	cache Type   // Cache the type, nil to start.
}

// Parse a .debug_types section.
func (d *Data) parseTypes(name string, types []byte) error {
	b := makeBuf(d, unknownFormat{}, name, 0, types)
	for len(b.data) > 0 {
		base := b.off
		dwarf64 := false
		n := b.uint32()
		if n == 0xffffffff {
			n64 := b.uint64()
			if n64 != uint64(uint32(n64)) {
				b.error("type unit length overflow")
				return b.err
			}
			n = uint32(n64)
			dwarf64 = true
		}
		hdroff := b.off
		vers := b.uint16()
		if vers != 4 {
			b.error("unsupported DWARF version " + strconv.Itoa(int(vers)))
			return b.err
		}
		var ao uint32
		if !dwarf64 {
			ao = b.uint32()
		} else {
			ao64 := b.uint64()
			if ao64 != uint64(uint32(ao64)) {
				b.error("type unit abbrev offset overflow")
				return b.err
			}
			ao = uint32(ao64)
		}
		atable, err := d.parseAbbrev(ao)
		if err != nil {
			return err
		}
		asize := b.uint8()
		sig := b.uint64()

		var toff uint32
		if !dwarf64 {
			toff = b.uint32()
		} else {
			to64 := b.uint64()
			if to64 != uint64(uint32(to64)) {
				b.error("type unit type offset overflow")
				return b.err
			}
			toff = uint32(to64)
		}

		boff := b.off
		d.typeSigs[sig] = &typeUnit{
			unit: unit{
				base:   base,
				off:    boff,
				data:   b.bytes(int(Offset(n) - (b.off - hdroff))),
				atable: atable,
				asize:  int(asize),
				vers:   int(vers),
				is64:   dwarf64,
			},
			toff: Offset(toff),
			name: name,
		}
		if b.err != nil {
			return b.err
		}
	}
	return nil
}

// Return the type for a type signature.
func (d *Data) sigToType(sig uint64) (Type, error) {
	tu := d.typeSigs[sig]
	if tu == nil {
		return nil, fmt.Errorf("no type unit with signature %v", sig)
	}
	if tu.cache != nil {
		return tu.cache, nil
	}

	b := makeBuf(d, tu, tu.name, tu.off, tu.data)
	r := &typeUnitReader{d: d, tu: tu, b: b}
	t, err := d.readType(tu.name, r, Offset(tu.toff), make(map[Offset]Type))
	if err != nil {
		return nil, err
	}

	tu.cache = t
	return t, nil
}

// typeUnitReader is a typeReader for a tagTypeUnit.
type typeUnitReader struct {
	d   *Data
	tu  *typeUnit
	b   buf
	err error
}

// Seek to a new position in the type unit.
func (tur *typeUnitReader) Seek(off Offset) {
	tur.err = nil
	doff := off - tur.tu.off
	if doff < 0 || doff >= Offset(len(tur.tu.data)) {
		tur.err = fmt.Errorf("%s: offset %d out of range; max %d", tur.tu.name, doff, len(tur.tu.data))
		return
	}
	tur.b = makeBuf(tur.d, tur.tu, tur.tu.name, off, tur.tu.data[doff:])
}

// Next reads the next Entry from the type unit.
func (tur *typeUnitReader) Next() (*Entry, error) {
	if tur.err != nil {
		return nil, tur.err
	}
	if len(tur.tu.data) == 0 {
		return nil, nil
	}
	e := tur.b.entry(tur.tu.atable, tur.tu.base)
	if tur.b.err != nil {
		tur.err = tur.b.err
		return nil, tur.err
	}
	return e, nil
}

// clone returns a new reader for the type unit.
func (tur *typeUnitReader) clone() typeReader {
	return &typeUnitReader{
		d:  tur.d,
		tu: tur.tu,
		b:  makeBuf(tur.d, tur.tu, tur.tu.name, tur.tu.off, tur.tu.data),
	}
}

// offset returns the current offset.
func (tur *typeUnitReader) offset() Offset {
	return tur.b.off
}

src/pkg/debug/elf/file.go

--- a/src/pkg/debug/elf/file.go
+++ b/src/pkg/debug/elf/file.go
@@ -601,7 +601,44 @@ func (f *File) DWARF() (*dwarf.Data, error) {\n \t}\n \n \tabbrev, info, str := dat[0], dat[1], dat[2]\n-\treturn dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)\n+\td, err := dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)\n+\tif err != nil {\n+\t\treturn nil, err\n+\t}\n+\n+\t// Look for DWARF4 .debug_types sections.\n+\tfor i, s := range f.Sections {\n+\t\tif s.Name == ".debug_types" {\n+\t\t\tb, err := s.Data()\n+\t\t\tif err != nil && uint64(len(b)) < s.Size {\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\n+\t\t\tfor _, r := range f.Sections {\n+\t\t\t\tif r.Type != SHT_RELA && r.Type != SHT_REL {\n+\t\t\t\t\tcontinue\n+\t\t\t\t}\n+\t\t\t\tif int(r.Info) != i {\n+\t\t\t\t\tcontinue\n+\t\t\t\t}\n+\t\t\t\trd, err := r.Data()\n+\t\t\t\tif err != nil {\n+\t\t\t\t\treturn nil, err\n+\t\t\t\t}\n+\t\t\t\terr = f.applyRelocations(b, rd)\n+\t\t\t\tif err != nil {\n+\t\t\t\t\treturn nil, err\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\terr = d.AddTypes(fmt.Sprintf("types-%d", i), b)\n+\t\t\tif err != nil {\n+\t\t\t\treturn nil, err\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn d, nil\n }\n \n // Symbols returns the symbol table for f.\n```

## コアとなるコードの解説

このコミットの核心は、DWARF 4の型単位(Type Unit)をGoの `debug/dwarf` パッケージで解析できるようにすることです。

1.  **`typeReader` インターフェースの導入**:
    *   `type.go` で定義された `typeReader` インターフェースは、デバッグ情報セクション(`.debug_info` または `.debug_types`)からDWARFエントリを読み込むための抽象化を提供します。これにより、型解析ロジックが、どのセクションからデータを読み込んでいるかに依存しなくなります。
    *   既存の `Reader` 型(`.debug_info` を読み込む)と、新しく追加される `typeUnitReader`(`.debug_types` を読み込む)がこのインターフェースを実装します。

2.  **`Data` 構造体への `typeSigs` の追加**:
    *   `open.go` の `Data` 構造体に `typeSigs map[uint64]*typeUnit` が追加されました。これは、DWARF 4の型単位が持つ8バイトのシグネチャをキーとして、対応する `typeUnit` オブジェクトを格納するためのマップです。これにより、シグネチャを使って型単位を効率的に検索できるようになります。

3.  **`AddTypes` メソッドと `parseTypes` 関数の実装**:
    *   `open.go` の `AddTypes` メソッドは、`debug/elf` パッケージから `.debug_types` セクションの生データを受け取り、`parseTypes` 関数を呼び出します。
    *   `typeunit.go` に実装された `parseTypes` 関数は、`.debug_types` セクションのデータを解析し、各型単位のヘッダ情報(長さ、バージョン、アブレビエーションオフセット、シグネチャ、型オフセットなど)を読み取ります。読み取った情報から `typeUnit` オブジェクトを作成し、そのシグネチャをキーとして `Data.typeSigs` マップに格納します。

4.  **`sigToType` メソッドの実装**:
    *   `typeunit.go` の `sigToType` メソッドは、与えられた型シグネチャに対応する `typeUnit` を `Data.typeSigs` から検索します。
    *   見つかった `typeUnit` から `typeUnitReader` を作成し、そのリーダーを使って型単位内の型情報を読み込みます。この際、`readType` ヘルパー関数が再利用されます。これにより、型単位内の型定義も、通常の `.debug_info` セクション内の型定義と同じロジックで解析できます。

5.  **`type.go` における `AttrType` の処理拡張**:
    *   `type.go` の `typeOf` クロージャ(属性 `AttrType` の値を型に変換する)が変更され、`AttrType` の値が `Offset`(従来の参照)だけでなく `uint64`(DWARF 4の型シグネチャ)である場合も処理できるようになりました。`uint64` の場合は、`d.sigToType()` を呼び出して、型シグネチャに対応する型情報を取得します。

6.  **構造体フィールドオフセットの `int64` サポート**:
    *   `type.go` の `StructType` の解析部分で、`AttrDataMemberLoc` 属性の値が `[]byte`(DWARF式)だけでなく `int64`(直接的なバイトオフセット)である場合も処理できるようになりました。これは、DWARF 3で導入された、構造体フィールドのオフセットをより直接的に表現する形式に対応するためです。

7.  **`debug/elf` における `.debug_types` セクションの検出と追加**:
    *   `debug/elf/file.go` の `DWARF()` メソッドが拡張され、ELFファイル内の `.debug_types` セクションを自動的に探し、見つかった場合は `dwarf.Data.AddTypes()` を呼び出して、その内容を `debug/dwarf` パッケージに渡すようになりました。これにより、GoのデバッグツールがDWARF 4形式のデバッグ情報を含むELFバイナリを透過的に処理できるようになります。

これらの変更により、Goのデバッグツールは、DWARF 4で生成されたデバッグ情報、特に型単位を正しく解釈し、デバッグ体験を向上させることができます。

## 関連リンク

*   [DWARF Debugging Information Format Standard](https://dwarfstd.org/)
*   [DWARF Version 4 Standard (PDF)](https://dwarfstd.org/doc/DWARF4.pdf)
*   [Go issue: cmd/ld: support DWARF4 .debug_types sections](https://github.com/golang/go/issues/6786) (このコミットの背景にあるGoのIssue)

## 参考にした情報源リンク

*   [DWARF Debugging Information Format Standard](https://dwarfstd.org/)
*   [DWARF Version 4 Standard (PDF)](https://dwarfstd.org/doc/DWARF4.pdf)
*   [Go issue: cmd/ld: support DWARF4 .debug_types sections](https://github.com/golang/go/issues/6786)
*   Go言語の `debug/dwarf` および `debug/elf` パッケージのソースコード
*   一般的なELFファイルフォーマットとDWARFデバッグ情報の解説記事