From f249c4114c11b2f236395b08c66afc2a2be4c303 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Tue, 15 Sep 2009 21:58:45 -0700 Subject: [PATCH] basic DWARF reading. R=r DELTA=949 (949 added, 0 deleted, 0 changed) OCL=34676 CL=34678 --- src/pkg/Make.deps | 1 + src/pkg/Makefile | 2 + src/pkg/debug/dwarf/Makefile | 15 ++ src/pkg/debug/dwarf/buf.go | 157 ++++++++++++++++ src/pkg/debug/dwarf/const.go | 352 +++++++++++++++++++++++++++++++++++ src/pkg/debug/dwarf/entry.go | 284 ++++++++++++++++++++++++++++ src/pkg/debug/dwarf/open.go | 79 ++++++++ src/pkg/debug/dwarf/unit.go | 63 +++++++ 8 files changed, 953 insertions(+) create mode 100644 src/pkg/debug/dwarf/Makefile create mode 100644 src/pkg/debug/dwarf/buf.go create mode 100644 src/pkg/debug/dwarf/const.go create mode 100644 src/pkg/debug/dwarf/entry.go create mode 100644 src/pkg/debug/dwarf/open.go create mode 100644 src/pkg/debug/dwarf/unit.go diff --git a/src/pkg/Make.deps b/src/pkg/Make.deps index 3618863e3b..386af8cbb9 100644 --- a/src/pkg/Make.deps +++ b/src/pkg/Make.deps @@ -18,6 +18,7 @@ crypto/md5.install: hash.install os.install crypto/sha1.install: hash.install os.install datafmt.install: bytes.install container/vector.install fmt.install go/scanner.install go/token.install io.install os.install reflect.install runtime.install strconv.install strings.install debug/binary.install: io.install math.install os.install reflect.install +debug/dwarf.install: debug/binary.install fmt.install os.install strconv.install debug/elf.install: debug/binary.install fmt.install io.install os.install strconv.install debug/gosym.install: debug/binary.install fmt.install io.install os.install strconv.install strings.install debug/proc.install: container/vector.install fmt.install io.install os.install runtime.install strconv.install strings.install sync.install syscall.install diff --git a/src/pkg/Makefile b/src/pkg/Makefile index 6495f5705a..78a2d6dbe1 100644 --- a/src/pkg/Makefile +++ b/src/pkg/Makefile @@ -32,6 +32,7 @@ DIRS=\ crypto/sha1\ datafmt\ debug/binary\ + debug/dwarf\ debug/elf\ debug/gosym\ debug/proc\ @@ -81,6 +82,7 @@ DIRS=\ utf8\ NOTEST=\ + debug/dwarf\ debug/proc\ go/ast\ go/doc\ diff --git a/src/pkg/debug/dwarf/Makefile b/src/pkg/debug/dwarf/Makefile new file mode 100644 index 0000000000..dfa0d90099 --- /dev/null +++ b/src/pkg/debug/dwarf/Makefile @@ -0,0 +1,15 @@ +# Copyright 2009 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. + +include $(GOROOT)/src/Make.$(GOARCH) + +TARG=debug/dwarf +GOFILES=\ + buf.go\ + const.go\ + entry.go\ + open.go\ + unit.go\ + +include $(GOROOT)/src/Make.pkg diff --git a/src/pkg/debug/dwarf/buf.go b/src/pkg/debug/dwarf/buf.go new file mode 100644 index 0000000000..a9d45e527f --- /dev/null +++ b/src/pkg/debug/dwarf/buf.go @@ -0,0 +1,157 @@ +// Copyright 2009 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. + +// Buffered reading and decoding of DWARF data streams. + +package dwarf + +import ( + "debug/binary"; + "os"; + "strconv"; +) + +// Data buffer being decoded. +type buf struct { + dwarf *Data; + order binary.ByteOrder; + name string; + off Offset; + data []byte; + addrsize int; + err os.Error; +} + +func makeBuf(d *Data, name string, off Offset, data []byte, addrsize int) buf { + return buf{d, d.order, name, off, data, addrsize, nil} +} + +func (b *buf) uint8() uint8 { + if len(b.data) < 1 { + b.error("underflow"); + return 0; + } + val := b.data[0]; + b.data = b.data[1:len(b.data)]; + b.off++; + return val; +} + +func (b *buf) bytes(n int) []byte { + if len(b.data) < n { + b.error("underflow"); + return nil; + } + data := b.data[0:n]; + b.data = b.data[n:len(b.data)]; + b.off += Offset(n); + return data; +} + +func (b *buf) skip(n int) { + b.bytes(n); +} + +func (b *buf) string() string { + for i := 0; i < len(b.data); i++ { + if b.data[i] == 0 { + s := string(b.data[0:i]); + b.data = b.data[i+1:len(b.data)]; + b.off += Offset(i+1); + return s; + } + } + b.error("underflow"); + return ""; +} + +func (b *buf) uint16() uint16{ + a := b.bytes(2); + if a == nil { + return 0; + } + return b.order.Uint16(a); +} + +func (b *buf) uint32() uint32 { + a := b.bytes(4); + if a == nil { + return 0; + } + return b.order.Uint32(a); +} + +func (b *buf) uint64() uint64 { + a := b.bytes(8); + if a == nil { + return 0; + } + return b.order.Uint64(a); +} + +// Read a varint, which is 7 bits per byte, little endian. +// the 0x80 bit means read another byte. +func (b *buf) varint() (c uint64, bits uint) { + for i := 0; i < len(b.data); i++ { + byte := b.data[i]; + c |= uint64(byte&0x7F) << bits; + bits += 7; + if byte&0x80 == 0 { + b.off += Offset(i+1); + b.data = b.data[i+1:len(b.data)]; + return c, bits; + } + } + return 0, 0; +} + +// Unsigned int is just a varint. +func (b *buf) uint() uint64 { + x, _ := b.varint(); + return x; +} + +// Signed int is a sign-extended varint. +func (b *buf) int() int64 { + ux, bits := b.varint(); + x := int64(ux); + if x & (1<<(bits-1)) != 0 { + x |= -1< uint32(len(data)) { + data = nil; + } else { + data = data[off:len(data)]; + } + b := makeBuf(d, "abbrev", 0, data, 0); + + // Error handling is simplified by the buf getters + // returning an endless stream of 0s after an error. + m := make(abbrevTable); + for { + // Table ends with id == 0. + id := uint32(b.uint()); + if id == 0 { + break; + } + + // Walk over attributes, counting. + n := 0; + b1 := b; // Read from copy of b. + b1.uint(); + b1.uint8(); + for { + tag := b1.uint(); + fmt := b1.uint(); + if tag == 0 && fmt == 0 { + break; + } + n++; + } + if b1.err != nil { + return nil, b1.err; + } + + // Walk over attributes again, this time writing them down. + var a abbrev; + a.tag = Tag(b.uint()); + a.children = b.uint8() != 0; + a.field = make([]afield, n); + for i := range a.field { + a.field[i].attr = Attr(b.uint()); + a.field[i].fmt = format(b.uint()); + } + b.uint(); + b.uint(); + + m[id] = a; + } + if b.err != nil { + return nil, b.err; + } + d.abbrevCache[off] = m; + return m, nil; +} + +// An entry is a sequence of attribute/value pairs. +type Entry struct { + Offset Offset; // offset of Entry in DWARF info + Tag Tag; // tag (kind of Entry) + Children bool; // whether Entry is followed by children + Field []Field; +} + +// A Field is a single attribute/value pair in an Entry. +type Field struct { + Attr Attr; + Val interface{}; +} + +// An Offset represents the location of an Entry within the DWARF info. +// (See Reader.Seek.) +type Offset uint32 + +// Entry reads a single entry from buf, decoding +// according to the given abbreviation table. +func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry { + off := b.off; + id := uint32(b.uint()); + if id == 0 { + return &Entry{}; + } + a, ok := atab[id]; + if !ok { + b.error("unknown abbreviation table index"); + return nil; + } + e := &Entry{ + Offset: off, + Tag: a.tag, + Children: a.children, + Field: make([]Field, len(a.field)) + }; + for i := range e.Field { + e.Field[i].Attr = a.field[i].attr; + fmt := a.field[i].fmt; + if fmt == formIndirect { + fmt = format(b.uint()); + } + var val interface{}; + switch fmt { + default: + b.error("unknown entry attr format"); + + // address + case formAddr: + val = b.addr(); + + // block + case formDwarfBlock1: + val = b.bytes(int(b.uint8())); + case formDwarfBlock2: + val = b.bytes(int(b.uint16())); + case formDwarfBlock4: + val = b.bytes(int(b.uint32())); + case formDwarfBlock: + val = b.bytes(int(b.uint())); + + // constant + case formData1: + val = uint64(b.uint8()); + case formData2: + val = uint64(b.uint16()); + case formData4: + val = uint64(b.uint32()); + case formData8: + val = uint64(b.uint64()); + case formSdata: + val = int64(b.int()); + case formUdata: + val = uint64(b.uint()); + + // flag + case formFlag: + val = b.uint8() == 1; + + // reference to other entry + case formRefAddr: + val = Offset(b.addr()); + case formRef1: + val = Offset(b.uint8()) + ubase; + case formRef2: + val = Offset(b.uint16()) + ubase; + case formRef4: + val = Offset(b.uint32()) + ubase; + case formRef8: + val = Offset(b.uint64()) + ubase; + case formRefUdata: + val = Offset(b.uint()) + ubase; + + // string + case formString: + val = b.string(); + case formStrp: + off := b.uint32(); // offset into .debug_str + if b.err != nil { + return nil; + } + b1 := makeBuf(b.dwarf, "str", 0, b.dwarf.str, 0); + b1.skip(int(off)); + val = b1.string(); + if b1.err != nil { + b.err = b1.err; + return nil; + } + } + e.Field[i].Val = val; + } + if b.err != nil { + return nil; + } + return e; +} + +// A Reader allows reading Entry structures from a DWARF ``info'' section. +type Reader struct { + b buf; + d *Data; + err os.Error; + unit int; +} + +// Reader returns a new Reader for Data. +// The reader is positioned at byte offset 0 in the DWARF ``info'' section. +func (d *Data) Reader() *Reader { + r := &Reader{d: d}; + r.Seek(0); + return r; +} + +// Seek positions the Reader at offset off in the encoded entry stream. +// Offset 0 can be used to denote the first entry. +func (r *Reader) Seek(off Offset) { + d := r.d; + r.err = nil; + if off == 0 { + if len(d.unit) == 0 { + return; + } + u := &d.unit[0]; + r.unit = 0; + r.b = makeBuf(r.d, "info", u.off, u.data, u.addrsize); + return; + } + + // TODO(rsc): binary search (maybe a new package) + var i int; + var u *unit; + for i = range d.unit { + u = &d.unit[i]; + if u.off <= off && off < u.off+Offset(len(u.data)) { + r.unit = i; + r.b = makeBuf(r.d, "info", off, u.data[off-u.off:len(u.data)], u.addrsize); + return; + } + } + r.err = os.NewError("offset out of range"); +} + +// maybeNextUnit advances to the next unit if this one is finished. +func (r *Reader) maybeNextUnit() { + for len(r.b.data) == 0 && r.unit < len(r.d.unit) { + r.unit++; + u := &r.d.unit[r.unit]; + r.b = makeBuf(r.d, "info", u.off, u.data, u.addrsize); + } +} + +// Next reads the next entry from the encoded entry stream. +// It returns nil, nil when it reaches the end of the section. +// It returns an error if the current offset is invalid or the data at the +// offset cannot be decoded as a valid Entry. +func (r *Reader) Next() (*Entry, os.Error) { + if r.err != nil { + return nil, r.err; + } + r.maybeNextUnit(); + if len(r.b.data) == 0 { + return nil, nil; + } + u := &r.d.unit[r.unit]; + e := r.b.entry(u.atable, u.base); + r.err = r.b.err; + return e, r.err; +} diff --git a/src/pkg/debug/dwarf/open.go b/src/pkg/debug/dwarf/open.go new file mode 100644 index 0000000000..8694508386 --- /dev/null +++ b/src/pkg/debug/dwarf/open.go @@ -0,0 +1,79 @@ +// Copyright 2009 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. + +// This package provides access to DWARF debugging information +// loaded from executable files, as defined in the DWARF 2.0 Standard +// at http://dwarfstd.org/dwarf-2.0.0.pdf. +package dwarf + +import ( + "debug/binary"; + "fmt"; + "os"; +) + +// Data represents the DWARF debugging information +// loaded from an executable file (for example, an ELF or Mach-O executable). +type Data struct { + // raw data + abbrev []byte; + aranges []byte; + frame []byte; + info []byte; + line []byte; + pubnames []byte; + ranges []byte; + str []byte; + + // parsed data + abbrevCache map[uint32] abbrevTable; + addrsize int; + order binary.ByteOrder; + unit []unit; +} + +// New returns a new Data object initialized from the given parameters. +// Clients should typically use [TODO(rsc): method to be named later] instead of calling +// New directly. +// +// The []byte arguments are the data from the corresponding debug section +// in the object file; for example, for an ELF object, abbrev is the contents of +// the ".debug_abbrev" section. +func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Data, os.Error) { + d := &Data{ + abbrev: abbrev, + aranges: aranges, + frame: frame, + info: info, + line: line, + pubnames: pubnames, + ranges: ranges, + str: str, + abbrevCache: make(map[uint32]abbrevTable), + }; + + // Sniff .debug_info to figure out byte order. + // bytes 4:6 are the version, a tiny 16-bit number (1, 2, 3). + if len(d.info) < 6 { + return nil, DecodeError{"info", Offset(len(d.info)), "too short"}; + } + x, y := d.info[4], d.info[5]; + switch { + case x == 0 && y == 0: + return nil, DecodeError{"info", 4, "unsupported version 0"}; + case x == 0: + d.order = binary.BigEndian; + case y == 0: + d.order = binary.LittleEndian; + default: + return nil, DecodeError{"info", 4, "cannot determine byte order"}; + } + + u, err := d.parseUnits(); + if err != nil { + return nil, err; + } + d.unit = u; + return d, nil; +} diff --git a/src/pkg/debug/dwarf/unit.go b/src/pkg/debug/dwarf/unit.go new file mode 100644 index 0000000000..040151f39d --- /dev/null +++ b/src/pkg/debug/dwarf/unit.go @@ -0,0 +1,63 @@ +// Copyright 2009 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 ( + "os"; + "strconv"; +) + +// DWARF debug info is split into a sequence of compilation units. +// 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 + data []byte; + atable abbrevTable; + addrsize int; +} + +func (d *Data) parseUnits() ([]unit, os.Error) { + // Count units. + nunit := 0; + b := makeBuf(d, "info", 0, d.info, 0); + for len(b.data) > 0 { + b.skip(int(b.uint32())); + nunit++; + } + if b.err != nil { + return nil, b.err; + } + + // Again, this time writing them down. + b = makeBuf(d, "info", 0, d.info, 0); + units := make([]unit, nunit); + for i := range units { + u := &units[i]; + u.base = b.off; + n := b.uint32(); + if vers := b.uint16(); vers != 2 { + b.error("unsupported DWARF version " + strconv.Itoa(int(vers))); + break; + } + atable, err := d.parseAbbrev(b.uint32()); + if err != nil { + if b.err == nil { + b.err = err; + } + break; + } + u.atable = atable; + u.addrsize = int(b.uint8()); + u.off = b.off; + u.data = b.bytes(int(n - (2+4+1))); + } + if b.err != nil { + return nil, b.err; + } + return units, nil; +} + -- 2.48.1