diff options
Diffstat (limited to 'libgo/go/debug')
-rw-r--r-- | libgo/go/debug/dwarf/buf.go | 66 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/entry.go | 27 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/line.go | 2 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/type.go | 4 | ||||
-rw-r--r-- | libgo/go/debug/dwarf/unit.go | 48 | ||||
-rw-r--r-- | libgo/go/debug/elf/file.go | 18 | ||||
-rw-r--r-- | libgo/go/debug/gosym/symtab.go | 123 | ||||
-rw-r--r-- | libgo/go/debug/macho/file.go | 2 |
8 files changed, 224 insertions, 66 deletions
diff --git a/libgo/go/debug/dwarf/buf.go b/libgo/go/debug/dwarf/buf.go index 08e37be4b38..53c46eb4b81 100644 --- a/libgo/go/debug/dwarf/buf.go +++ b/libgo/go/debug/dwarf/buf.go @@ -13,17 +13,45 @@ import ( // Data buffer being decoded. type buf struct { - dwarf *Data - u *unit - order binary.ByteOrder - name string - off Offset - data []byte - err error + dwarf *Data + order binary.ByteOrder + format dataFormat + name string + off Offset + data []byte + err error } -func makeBuf(d *Data, u *unit, name string, off Offset, data []byte) buf { - return buf{d, u, d.order, name, off, data, nil} +// Data format, other than byte order. This affects the handling of +// certain field formats. +type dataFormat interface { + // DWARF version number. Zero means unknown. + version() int + + // 64-bit DWARF format? + dwarf64() (dwarf64 bool, isKnown bool) + + // Size of an address, in bytes. Zero means unknown. + addrsize() int +} + +// Some parts of DWARF have no data format, e.g., abbrevs. +type unknownFormat struct{} + +func (u unknownFormat) version() int { + return 0 +} + +func (u unknownFormat) dwarf64() (bool, bool) { + return false, false +} + +func (u unknownFormat) addrsize() int { + return 0 +} + +func makeBuf(d *Data, format dataFormat, name string, off Offset, data []byte) buf { + return buf{d, d.order, format, name, off, data, nil} } func (b *buf) uint8() uint8 { @@ -121,17 +149,15 @@ func (b *buf) int() int64 { // Address-sized uint. func (b *buf) addr() uint64 { - if b.u != nil { - switch b.u.addrsize { - case 1: - return uint64(b.uint8()) - case 2: - return uint64(b.uint16()) - case 4: - return uint64(b.uint32()) - case 8: - return uint64(b.uint64()) - } + switch b.format.addrsize() { + case 1: + return uint64(b.uint8()) + case 2: + return uint64(b.uint16()) + case 4: + return uint64(b.uint32()) + case 8: + return uint64(b.uint64()) } b.error("unknown address size") return 0 diff --git a/libgo/go/debug/dwarf/entry.go b/libgo/go/debug/dwarf/entry.go index 4761d74cd79..ada723163a4 100644 --- a/libgo/go/debug/dwarf/entry.go +++ b/libgo/go/debug/dwarf/entry.go @@ -40,7 +40,7 @@ func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) { } else { data = data[off:] } - b := makeBuf(d, nil, "abbrev", 0, data) + b := makeBuf(d, unknownFormat{}, "abbrev", 0, data) // Error handling is simplified by the buf getters // returning an endless stream of 0s after an error. @@ -190,13 +190,16 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { case formFlag: val = b.uint8() == 1 case formFlagPresent: + // The attribute is implicitly indicated as present, and no value is + // encoded in the debugging information entry itself. val = true // lineptr, loclistptr, macptr, rangelistptr case formSecOffset: - if b.u == nil { + is64, known := b.format.dwarf64() + if !known { b.error("unknown size for DW_FORM_sec_offset") - } else if b.u.dwarf64 { + } else if is64 { val = Offset(b.uint64()) } else { val = Offset(b.uint32()) @@ -204,14 +207,20 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { // reference to other entry case formRefAddr: - if b.u == nil { + vers := b.format.version() + if vers == 0 { b.error("unknown version for DW_FORM_ref_addr") - } else if b.u.version == 2 { + } else if vers == 2 { val = Offset(b.addr()) - } else if b.u.dwarf64 { - val = Offset(b.uint64()) } else { - val = Offset(b.uint32()) + is64, known := b.format.dwarf64() + if !known { + b.error("unknown size for DW_FORM_ref_addr") + } else if is64 { + val = Offset(b.uint64()) + } else { + val = Offset(b.uint32()) + } } case formRef1: val = Offset(b.uint8()) + ubase @@ -234,7 +243,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { if b.err != nil { return nil } - b1 := makeBuf(b.dwarf, b.u, "str", 0, b.dwarf.str) + b1 := makeBuf(b.dwarf, unknownFormat{}, "str", 0, b.dwarf.str) b1.skip(int(off)) val = b1.string() if b1.err != nil { diff --git a/libgo/go/debug/dwarf/line.go b/libgo/go/debug/dwarf/line.go index 3ab2f2b30cb..c463c3b0d8f 100644 --- a/libgo/go/debug/dwarf/line.go +++ b/libgo/go/debug/dwarf/line.go @@ -112,7 +112,7 @@ func (d *Data) readUnitLine(i int, u *unit) error { func (d *Data) readAddressRanges(off Offset, base uint64, u *unit) error { b := makeBuf(d, u, "ranges", off, d.ranges[off:]) var highest uint64 - switch u.addrsize { + switch u.addrsize() { case 1: highest = 0xff case 2: diff --git a/libgo/go/debug/dwarf/type.go b/libgo/go/debug/dwarf/type.go index 2ef8ca01bf8..54000fbd75e 100644 --- a/libgo/go/debug/dwarf/type.go +++ b/libgo/go/debug/dwarf/type.go @@ -435,7 +435,9 @@ func (d *Data) Type(off Offset) (Type, error) { goto Error } if loc, ok := kid.Val(AttrDataMemberLoc).([]byte); ok { - b := makeBuf(d, nil, "location", 0, loc) + // TODO: Should have original compilation + // unit here, not unknownFormat. + b := makeBuf(d, unknownFormat{}, "location", 0, loc) if b.uint8() != opPlusUconst { err = DecodeError{"info", kid.Offset, "unexpected opcode"} goto Error diff --git a/libgo/go/debug/dwarf/unit.go b/libgo/go/debug/dwarf/unit.go index b1903208715..8e09298a8b4 100644 --- a/libgo/go/debug/dwarf/unit.go +++ b/libgo/go/debug/dwarf/unit.go @@ -10,17 +10,31 @@ import "strconv" // Each unit has its own abbreviation table and address size. type unit struct { - base Offset // byte offset of header within the aggregate info - off Offset // byte offset of data within the aggregate info - lineoff Offset // byte offset of data within the line info - data []byte - atable abbrevTable - addrsize int - version int - dwarf64 bool // True for 64-bit DWARF format - dir string - pc []addrRange // PC ranges in this compilation unit - lines []mapLineInfo // PC -> line mapping + base Offset // byte offset of header within the aggregate info + off Offset // byte offset of data within the aggregate info + lineoff Offset // byte offset of data within the line info + data []byte + atable abbrevTable + asize int + vers int + is64 bool // True for 64-bit DWARF format + dir string + pc []addrRange // PC ranges in this compilation unit + lines []mapLineInfo // PC -> line mapping +} + +// Implement the dataFormat interface. + +func (u *unit) version() int { + return u.vers +} + +func (u *unit) dwarf64() (bool, bool) { + return u.is64, true +} + +func (u *unit) addrsize() int { + return u.asize } // A range is an address range. @@ -32,12 +46,12 @@ type addrRange struct { func (d *Data) parseUnits() ([]unit, error) { // Count units. nunit := 0 - b := makeBuf(d, nil, "info", 0, d.info) + b := makeBuf(d, unknownFormat{}, "info", 0, d.info) for len(b.data) > 0 { len := b.uint32() if len == 0xffffffff { len64 := b.uint64() - if len64 != uint64(int(len64)) { + if len64 != uint64(uint32(len64)) { b.error("unit length overflow") break } @@ -51,14 +65,14 @@ func (d *Data) parseUnits() ([]unit, error) { } // Again, this time writing them down. - b = makeBuf(d, nil, "info", 0, d.info) + b = makeBuf(d, unknownFormat{}, "info", 0, d.info) units := make([]unit, nunit) for i := range units { u := &units[i] u.base = b.off n := b.uint32() if n == 0xffffffff { - u.dwarf64 = true + u.is64 = true n = uint32(b.uint64()) } vers := b.uint16() @@ -66,6 +80,7 @@ func (d *Data) parseUnits() ([]unit, error) { b.error("unsupported DWARF version " + strconv.Itoa(int(vers))) break } + u.vers = int(vers) atable, err := d.parseAbbrev(b.uint32()) if err != nil { if b.err == nil { @@ -73,9 +88,8 @@ func (d *Data) parseUnits() ([]unit, error) { } break } - u.version = int(vers) u.atable = atable - u.addrsize = int(b.uint8()) + u.asize = int(b.uint8()) u.off = b.off u.data = b.bytes(int(n - (2 + 4 + 1))) } diff --git a/libgo/go/debug/elf/file.go b/libgo/go/debug/elf/file.go index eb3d72f7126..8023eb0a0b7 100644 --- a/libgo/go/debug/elf/file.go +++ b/libgo/go/debug/elf/file.go @@ -422,6 +422,10 @@ func (f *File) getSymbols32(typ SectionType) ([]Symbol, []byte, error) { return nil, nil, errors.New("cannot load string table section") } + // The first entry is all zeros. + var skip [Sym32Size]byte + symtab.Read(skip[:]) + symbols := make([]Symbol, symtab.Len()/Sym32Size) i := 0 @@ -461,6 +465,10 @@ func (f *File) getSymbols64(typ SectionType) ([]Symbol, []byte, error) { return nil, nil, errors.New("cannot load string table section") } + // The first entry is all zeros. + var skip [Sym64Size]byte + symtab.Read(skip[:]) + symbols := make([]Symbol, symtab.Len()/Sym64Size) i := 0 @@ -533,10 +541,10 @@ func (f *File) applyRelocationsAMD64(dst []byte, rels []byte) error { symNo := rela.Info >> 32 t := R_X86_64(rela.Info & 0xffff) - if symNo >= uint64(len(symbols)) { + if symNo == 0 || symNo > uint64(len(symbols)) { continue } - sym := &symbols[symNo] + sym := &symbols[symNo-1] if SymType(sym.Info&0xf) != STT_SECTION { // We don't handle non-section relocations for now. continue @@ -597,6 +605,10 @@ func (f *File) DWARF() (*dwarf.Data, error) { } // Symbols returns the symbol table for f. +// +// For compatibility with Go 1.0, Symbols omits the null symbol at index 0. +// After retrieving the symbols as symtab, an externally supplied index x +// corresponds to symtab[x-1], not symtab[x]. func (f *File) Symbols() ([]Symbol, error) { sym, _, err := f.getSymbols(SHT_SYMTAB) return sym, err @@ -706,7 +718,7 @@ func (f *File) gnuVersionInit(str []byte) { // which came from offset i of the symbol table. func (f *File) gnuVersion(i int, sym *ImportedSymbol) { // Each entry is two bytes. - i = i * 2 + i = (i + 1) * 2 if i >= len(f.gnuVersym) { return } diff --git a/libgo/go/debug/gosym/symtab.go b/libgo/go/debug/gosym/symtab.go index cc01e0b9d69..81ed4fb27dc 100644 --- a/libgo/go/debug/gosym/symtab.go +++ b/libgo/go/debug/gosym/symtab.go @@ -99,31 +99,116 @@ type Table struct { } type sym struct { - value uint32 - gotype uint32 + value uint64 + gotype uint64 typ byte name []byte } -var littleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00} +var littleEndianSymtab = []byte{0xFD, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00} +var bigEndianSymtab = []byte{0xFF, 0xFF, 0xFF, 0xFD, 0x00, 0x00, 0x00} + +var oldLittleEndianSymtab = []byte{0xFE, 0xFF, 0xFF, 0xFF, 0x00, 0x00} func walksymtab(data []byte, fn func(sym) error) error { var order binary.ByteOrder = binary.BigEndian - if bytes.HasPrefix(data, littleEndianSymtab) { + newTable := false + switch { + case bytes.HasPrefix(data, oldLittleEndianSymtab): + // Same as Go 1.0, but little endian. + // Format was used during interim development between Go 1.0 and Go 1.1. + // Should not be widespread, but easy to support. data = data[6:] order = binary.LittleEndian + case bytes.HasPrefix(data, bigEndianSymtab): + newTable = true + case bytes.HasPrefix(data, littleEndianSymtab): + newTable = true + order = binary.LittleEndian + } + var ptrsz int + if newTable { + if len(data) < 8 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + ptrsz = int(data[7]) + if ptrsz != 4 && ptrsz != 8 { + return &DecodingError{7, "invalid pointer size", ptrsz} + } + data = data[8:] } var s sym p := data - for len(p) >= 6 { - s.value = order.Uint32(p[0:4]) - typ := p[4] - if typ&0x80 == 0 { - return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ} + for len(p) >= 4 { + var typ byte + if newTable { + // Symbol type, value, Go type. + typ = p[0] & 0x3F + wideValue := p[0]&0x40 != 0 + goType := p[0]&0x80 != 0 + if typ < 26 { + typ += 'A' + } else { + typ += 'a' - 26 + } + s.typ = typ + p = p[1:] + if wideValue { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width value + if ptrsz == 8 { + s.value = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.value = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } else { + // varint value + s.value = 0 + shift := uint(0) + for len(p) > 0 && p[0]&0x80 != 0 { + s.value |= uint64(p[0]&0x7F) << shift + shift += 7 + p = p[1:] + } + if len(p) == 0 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + s.value |= uint64(p[0]) << shift + p = p[1:] + } + if goType { + if len(p) < ptrsz { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // fixed-width go type + if ptrsz == 8 { + s.gotype = order.Uint64(p[0:8]) + p = p[8:] + } else { + s.gotype = uint64(order.Uint32(p[0:4])) + p = p[4:] + } + } + } else { + // Value, symbol type. + s.value = uint64(order.Uint32(p[0:4])) + if len(p) < 5 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + typ = p[4] + if typ&0x80 == 0 { + return &DecodingError{len(data) - len(p) + 4, "bad symbol type", typ} + } + typ &^= 0x80 + s.typ = typ + p = p[5:] } - typ &^= 0x80 - s.typ = typ - p = p[5:] + + // Name. var i int var nnul int for i = 0; i < len(p); i++ { @@ -142,13 +227,21 @@ func walksymtab(data []byte, fn func(sym) error) error { } } } - if i+nnul+4 > len(p) { + if len(p) < i+nnul { return &DecodingError{len(data), "unexpected EOF", nil} } s.name = p[0:i] i += nnul - s.gotype = order.Uint32(p[i : i+4]) - p = p[i+4:] + p = p[i:] + + if !newTable { + if len(p) < 4 { + return &DecodingError{len(data), "unexpected EOF", nil} + } + // Go type. + s.gotype = uint64(order.Uint32(p[:4])) + p = p[4:] + } fn(s) } return nil diff --git a/libgo/go/debug/macho/file.go b/libgo/go/debug/macho/file.go index 6577803a075..9d912e7a087 100644 --- a/libgo/go/debug/macho/file.go +++ b/libgo/go/debug/macho/file.go @@ -142,6 +142,8 @@ type Dysymtab struct { * Mach-O reader */ +// FormatError is returned by some operations if the data does +// not have the correct format for an object file. type FormatError struct { off int64 msg string |