From bd997b24f7a7cdb57bb9bf02fbfacef1f59f0f89 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 27 Jan 2014 10:18:22 -0800 Subject: [PATCH] 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 --- src/pkg/debug/dwarf/const.go | 31 ++-- src/pkg/debug/dwarf/entry.go | 12 ++ src/pkg/debug/dwarf/open.go | 10 ++ src/pkg/debug/dwarf/testdata/typedef.elf4 | Bin 0 -> 9496 bytes src/pkg/debug/dwarf/type.go | 75 ++++++---- src/pkg/debug/dwarf/type_test.go | 2 + src/pkg/debug/dwarf/typeunit.go | 166 ++++++++++++++++++++++ src/pkg/debug/dwarf/unit.go | 2 +- src/pkg/debug/elf/file.go | 39 ++++- 9 files changed, 300 insertions(+), 37 deletions(-) create mode 100644 src/pkg/debug/dwarf/testdata/typedef.elf4 create mode 100644 src/pkg/debug/dwarf/typeunit.go diff --git a/src/pkg/debug/dwarf/const.go b/src/pkg/debug/dwarf/const.go index 9d32a0af2a..987812b152 100644 --- 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 { diff --git a/src/pkg/debug/dwarf/entry.go b/src/pkg/debug/dwarf/entry.go index c0c2889923..934416e6c1 100644 --- a/src/pkg/debug/dwarf/entry.go +++ b/src/pkg/debug/dwarf/entry.go @@ -387,3 +387,15 @@ func (r *Reader) SkipChildren() { } } } + +// clone returns a copy of the reader. This is used by the typeReader +// interface. +func (r *Reader) clone() typeReader { + return r.d.Reader() +} + +// offset returns the current buffer offset. This is used by the +// typeReader interface. +func (r *Reader) offset() Offset { + return r.b.off +} diff --git a/src/pkg/debug/dwarf/open.go b/src/pkg/debug/dwarf/open.go index 7579892529..c1b3f37aca 100644 --- 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) +} diff --git a/src/pkg/debug/dwarf/testdata/typedef.elf4 b/src/pkg/debug/dwarf/testdata/typedef.elf4 new file mode 100644 index 0000000000000000000000000000000000000000..3d5a5a1b1620badce20ec4ea8c630f3bfd58c4d2 GIT binary patch literal 9496 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 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 +} diff --git a/src/pkg/debug/dwarf/unit.go b/src/pkg/debug/dwarf/unit.go index 270cd2e331..0fbc8e0825 100644 --- a/src/pkg/debug/dwarf/unit.go +++ b/src/pkg/debug/dwarf/unit.go @@ -66,7 +66,7 @@ func (d *Data) parseUnits() ([]unit, error) { n = uint32(b.uint64()) } vers := b.uint16() - if vers != 2 && vers != 3 { + if vers != 2 && vers != 3 && vers != 4 { b.error("unsupported DWARF version " + strconv.Itoa(int(vers))) break } diff --git a/src/pkg/debug/elf/file.go b/src/pkg/debug/elf/file.go index a55c37ea99..07661aa166 100644 --- 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) { } abbrev, info, str := dat[0], dat[1], dat[2] - return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) + d, err := dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str) + if err != nil { + return nil, err + } + + // Look for DWARF4 .debug_types sections. + for i, s := range f.Sections { + if s.Name == ".debug_types" { + b, err := s.Data() + if err != nil && uint64(len(b)) < s.Size { + return nil, err + } + + for _, r := range f.Sections { + if r.Type != SHT_RELA && r.Type != SHT_REL { + continue + } + if int(r.Info) != i { + continue + } + rd, err := r.Data() + if err != nil { + return nil, err + } + err = f.applyRelocations(b, rd) + if err != nil { + return nil, err + } + } + + err = d.AddTypes(fmt.Sprintf("types-%d", i), b) + if err != nil { + return nil, err + } + } + } + + return d, nil } // Symbols returns the symbol table for f. -- 2.48.1