| // 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 |
| } |