]> Cypherpunks repositories - gostls13.git/commitdiff
parse and present DWARF type information
authorRuss Cox <rsc@golang.org>
Wed, 16 Sep 2009 17:43:27 +0000 (10:43 -0700)
committerRuss Cox <rsc@golang.org>
Wed, 16 Sep 2009 17:43:27 +0000 (10:43 -0700)
R=r
DELTA=940  (929 added, 1 deleted, 10 changed)
OCL=34679
CL=34686

src/pkg/Makefile
src/pkg/debug/dwarf/Makefile
src/pkg/debug/dwarf/buf.go
src/pkg/debug/dwarf/const.go
src/pkg/debug/dwarf/entry.go
src/pkg/debug/dwarf/open.go
src/pkg/debug/dwarf/testdata/typedef.c [new file with mode: 0644]
src/pkg/debug/dwarf/testdata/typedef.elf [new file with mode: 0755]
src/pkg/debug/dwarf/type.go [new file with mode: 0644]
src/pkg/debug/dwarf/type_test.go [new file with mode: 0644]

index 78a2d6dbe14b820005409d6e5809301565141f75..263b9fedaadef4cedb5bc5ed573f0d6a02918191 100644 (file)
@@ -82,7 +82,6 @@ DIRS=\
        utf8\
 
 NOTEST=\
-       debug/dwarf\
        debug/proc\
        go/ast\
        go/doc\
index dfa0d900992e89fb79661608a3d3194458d1b888..8825c597ec36cbb85405c4e11a9b305e6f2cb5dc 100644 (file)
@@ -10,6 +10,7 @@ GOFILES=\
        const.go\
        entry.go\
        open.go\
+       type.go\
        unit.go\
 
 include $(GOROOT)/src/Make.pkg
index a9d45e527fe392f22c86e96e885a1fb8c727a753..3089180ac0e831f6b95f69443fc081be103e06aa 100644 (file)
@@ -152,6 +152,6 @@ type DecodeError struct {
 }
 
 func (e DecodeError) String() string {
-       return "decoding dwarf section " + e.Name + " at offset " + strconv.Itoa64(int64(e.Offset)) + ": " + e.Error;
+       return "decoding dwarf section " + e.Name + " at offset 0x" + strconv.Itob64(int64(e.Offset), 16) + ": " + e.Error;
 }
 
index 73abdb66a2ed51c865ebda614db3cbeb080d7229..c2878bd6f5224a1dead4816d945c4eec512b6f19 100644 (file)
@@ -350,3 +350,84 @@ func (t Tag) GoString() string {
        return "dwarf.Tag(" + strconv.Itoa64(int64(t)) + ")";
 }
 
+// Location expression operators.
+// The debug info encodes value locations like 8(R3)
+// as a sequence of these op codes.
+// This package does not implement full expressions;
+// the opPlusUconst operator is expected by the type parser.
+const (
+       opAddr = 0x03;  /* 1 op, const addr */
+       opDeref = 0x06;
+       opConst1u = 0x08;       /* 1 op, 1 byte const */
+       opConst1s = 0x09;       /*      " signed */
+       opConst2u = 0x0A;       /* 1 op, 2 byte const  */
+       opConst2s = 0x0B;       /*      " signed */
+       opConst4u = 0x0C;       /* 1 op, 4 byte const */
+       opConst4s = 0x0D;       /*      " signed */
+       opConst8u = 0x0E;       /* 1 op, 8 byte const */
+       opConst8s = 0x0F;       /*      " signed */
+       opConstu = 0x10;        /* 1 op, LEB128 const */
+       opConsts = 0x11;        /*      " signed */
+       opDup = 0x12;
+       opDrop = 0x13;
+       opOver = 0x14;
+       opPick = 0x15;          /* 1 op, 1 byte stack index */
+       opSwap = 0x16;
+       opRot = 0x17;
+       opXderef = 0x18;
+       opAbs = 0x19;
+       opAnd = 0x1A;
+       opDiv = 0x1B;
+       opMinus = 0x1C;
+       opMod = 0x1D;
+       opMul = 0x1E;
+       opNeg = 0x1F;
+       opNot = 0x20;
+       opOr = 0x21;
+       opPlus = 0x22;
+       opPlusUconst = 0x23;    /* 1 op, ULEB128 addend */
+       opShl = 0x24;
+       opShr = 0x25;
+       opShra = 0x26;
+       opXor = 0x27;
+       opSkip = 0x2F;          /* 1 op, signed 2-byte constant */
+       opBra = 0x28;           /* 1 op, signed 2-byte constant */
+       opEq = 0x29;
+       opGe = 0x2A;
+       opGt = 0x2B;
+       opLe = 0x2C;
+       opLt = 0x2D;
+       opNe = 0x2E;
+       opLit0 = 0x30;
+               /* OpLitN = OpLit0 + N for N = 0..31 */
+       opReg0 = 0x50;
+               /* OpRegN = OpReg0 + N for N = 0..31 */
+       opBreg0 = 0x70; /* 1 op, signed LEB128 constant */
+               /* OpBregN = OpBreg0 + N for N = 0..31 */
+       opRegx = 0x90;  /* 1 op, ULEB128 register */
+       opFbreg = 0x91; /* 1 op, SLEB128 offset */
+       opBregx = 0x92; /* 2 op, ULEB128 reg; SLEB128 off */
+       opPiece = 0x93; /* 1 op, ULEB128 size of piece */
+       opDerefSize = 0x94;     /* 1-byte size of data retrieved */
+       opXderefSize = 0x95;    /* 1-byte size of data retrieved */
+       opNop = 0x96;
+       /* next four new in Dwarf v3 */
+       opPushObjAddr = 0x97;
+       opCall2 = 0x98; /* 2-byte offset of DIE */
+       opCall4 = 0x99; /* 4-byte offset of DIE */
+       opCallRef = 0x9A        /* 4- or 8- byte offset of DIE */
+       /* 0xE0-0xFF reserved for user-specific */
+)
+
+// Basic type encodings -- the value for AttrEncoding in a TagBaseType Entry.
+const (
+       encAddress = 0x01;
+       encBoolean = 0x02;
+       encComplexFloat = 0x03;
+       encFloat = 0x04;
+       encSigned = 0x05;
+       encSignedChar = 0x06;
+       encUnsigned = 0x07;
+       encUnsignedChar = 0x08;
+       encImaginaryFloat = 0x09;
+)
index 472ee91d8a9226a58357ac0d3165175c91c5a582..98a8b2ea04365977981b0372e690890c7afae98c 100644 (file)
@@ -107,6 +107,22 @@ type Field struct {
        Val interface{};
 }
 
+// Val returns the value associated with attribute Attr in Entry,
+// or nil if there is no such attribute.
+//
+// A common idiom is to merge the check for nil return with
+// the check that the value has the expected dynamic type, as in:
+//     v, ok := e.Val(AttrSibling).(int64);
+//
+func (e *Entry) Val(a Attr) interface{} {
+       for _, f := range e.Field {
+               if f.Attr == a {
+                       return f.Val;
+               }
+       }
+       return nil;
+}
+
 // An Offset represents the location of an Entry within the DWARF info.
 // (See Reader.Seek.)
 type Offset uint32
@@ -157,17 +173,17 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
 
                // constant
                case formData1:
-                       val = uint64(b.uint8());
+                       val = int64(b.uint8());
                case formData2:
-                       val = uint64(b.uint16());
+                       val = int64(b.uint16());
                case formData4:
-                       val = uint64(b.uint32());
+                       val = int64(b.uint32());
                case formData8:
-                       val = uint64(b.uint64());
+                       val = int64(b.uint64());
                case formSdata:
                        val = int64(b.int());
                case formUdata:
-                       val = uint64(b.uint());
+                       val = int64(b.uint());
 
                // flag
                case formFlag:
@@ -212,11 +228,17 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
 }
 
 // A Reader allows reading Entry structures from a DWARF ``info'' section.
+// The Entry structures are arranged in a tree.  The Reader's Next function
+// return successive entries from a pre-order traversal of the tree.
+// If an entry has children, its Children field will be true, and the children
+// follow, terminated by an Entry with Tag 0.
 type Reader struct {
        b buf;
        d *Data;
        err os.Error;
        unit int;
+       lastChildren bool;      // .Children of last entry returned by Next
+       lastSibling Offset;     // .Val(AttrSibling) of last entry returned by Next
 }
 
 // Reader returns a new Reader for Data.
@@ -232,6 +254,7 @@ func (d *Data) Reader() *Reader {
 func (r *Reader) Seek(off Offset) {
        d := r.d;
        r.err = nil;
+       r.lastChildren = false;
        if off == 0 {
                if len(d.unit) == 0 {
                        return;
@@ -258,7 +281,7 @@ func (r *Reader) Seek(off Offset) {
 
 // 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) {
+       for len(r.b.data) == 0 && r.unit+1 < len(r.d.unit) {
                r.unit++;
                u := &r.d.unit[r.unit];
                r.b = makeBuf(r.d, "info", u.off, u.data, u.addrsize);
@@ -279,6 +302,46 @@ func (r *Reader) Next() (*Entry, os.Error) {
        }
        u := &r.d.unit[r.unit];
        e := r.b.entry(u.atable, u.base);
-       r.err = r.b.err;
-       return e, r.err;
+       if r.b.err != nil {
+               r.err = r.b.err;
+               return nil, r.err;
+       }
+       if e != nil {
+               r.lastChildren = e.Children;
+               if r.lastChildren {
+                       r.lastSibling, _ = e.Val(AttrSibling).(Offset);
+               }
+       } else {
+               r.lastChildren = false;
+       }
+       return e, nil;
 }
+
+// SkipChildren skips over the child entries associated with
+// the last Entry returned by Next.  If that Entry did not have
+// children or Next has not been called, SkipChildren is a no-op.
+func (r *Reader) SkipChildren() {
+       if r.err != nil || !r.lastChildren{
+               return;
+       }
+
+       // If the last entry had a sibling attribute,
+       // that attribute gives the offset of the next
+       // sibling, so we can avoid decoding the
+       // child subtrees.
+       if r.lastSibling >= r.b.off {
+               r.Seek(r.lastSibling);
+               return;
+       }
+
+       for {
+               e, err := r.Next();
+               if err != nil || e == nil || e.Tag == 0 {
+                       break;
+               }
+               if e.Children {
+                       r.SkipChildren();
+               }
+       }
+}
+
index 8694508386c9aa444b433830a7e44021d1798924..e252ce5f4e88d1e28059f21b573a9bb556d1ac1e 100644 (file)
@@ -30,6 +30,7 @@ type Data struct {
        abbrevCache map[uint32] abbrevTable;
        addrsize int;
        order binary.ByteOrder;
+       typeCache map[Offset] Type;
        unit []unit;
 }
 
@@ -51,6 +52,7 @@ func New(abbrev, aranges, frame, info, line, pubnames, ranges, str []byte) (*Dat
                ranges: ranges,
                str: str,
                abbrevCache: make(map[uint32]abbrevTable),
+               typeCache: make(map[uint32]Type),
        };
 
        // Sniff .debug_info to figure out byte order.
diff --git a/src/pkg/debug/dwarf/testdata/typedef.c b/src/pkg/debug/dwarf/testdata/typedef.c
new file mode 100644 (file)
index 0000000..9a46d42
--- /dev/null
@@ -0,0 +1,68 @@
+// 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.
+
+/*
+gcc -gdwarf-2 -c typedef.c && gcc -gdwarf-2 -o typedef.elf typedef.o
+*/
+
+typedef volatile int* t_ptr_volatile_int;
+typedef const char *t_ptr_const_char;
+typedef long t_long;
+typedef unsigned short t_ushort;
+typedef int t_func_int_of_float_double(float, double);
+typedef int (*t_ptr_func_int_of_float_double)(float, double);
+typedef int *t_func_ptr_int_of_char_schar_uchar(char, signed char, unsigned char);
+typedef void t_func_void_of_char(char);
+typedef void t_func_void_of_void(void);
+typedef void t_func_void_of_ptr_char_dots(char*, ...);
+typedef struct my_struct {
+       volatile int vi;
+       char x : 1;
+       int y : 4;
+       long long array[40];
+} t_my_struct;
+typedef union my_union {
+       volatile int vi;
+       char x : 1;
+       int y : 4;
+       long long array[40];
+} t_my_union;
+typedef enum my_enum {
+       e1 = 1,
+       e2 = 2,
+       e3 = -5,
+       e4 = 1000000000000000LL,
+} t_my_enum;
+
+typedef struct list t_my_list;
+struct list {
+       short val;
+       t_my_list *next;
+};
+
+typedef struct tree {
+       struct tree *left, *right;
+       unsigned long long val;
+} t_my_tree;
+
+t_ptr_volatile_int *a2;
+t_ptr_const_char **a3a;
+t_long *a4;
+t_ushort *a5;
+t_func_int_of_float_double *a6;
+t_ptr_func_int_of_float_double *a7;
+t_func_ptr_int_of_char_schar_uchar *a8;
+t_func_void_of_char *a9;
+t_func_void_of_void *a10;
+t_func_void_of_ptr_char_dots *a11;
+t_my_struct *a12;
+t_my_union *a12a;
+t_my_enum *a13;
+t_my_list *a14;
+t_my_tree *a15;
+
+int main()
+{
+       return 0;
+}
diff --git a/src/pkg/debug/dwarf/testdata/typedef.elf b/src/pkg/debug/dwarf/testdata/typedef.elf
new file mode 100755 (executable)
index 0000000..ea9291f
Binary files /dev/null and b/src/pkg/debug/dwarf/testdata/typedef.elf differ
diff --git a/src/pkg/debug/dwarf/type.go b/src/pkg/debug/dwarf/type.go
new file mode 100644 (file)
index 0000000..335ef31
--- /dev/null
@@ -0,0 +1,607 @@
+// 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.
+
+// DWARF type information structures.
+// The format is heavily biased toward C, but for simplicity
+// the String methods use a pseudo-Go syntax.
+
+package dwarf
+
+import (
+       "os";
+       "strconv";
+)
+
+// A CommonType holds fields common to multiple types.
+// If a field is not known or not applicable for a given type,
+// the zero value is used.
+type CommonType struct {
+       ByteSize int64;         // size of value of this type, in bytes
+       Name string;            // name that can be used to refer to type
+}
+
+func (c *CommonType) Common() *CommonType {
+       return c;
+}
+
+// Basic types
+
+// A BasicType holds fields common to all basic types.
+type BasicType struct {
+       CommonType;
+       BitSize int64;
+       BitOffset int64;
+}
+
+func (b *BasicType) Basic() *BasicType {
+       return b;
+}
+
+func (t *BasicType) String() string {
+       if t.Name != "" {
+               return t.Name;
+       }
+       return "?"
+}
+
+// A CharType represents a signed character type.
+type CharType struct {
+       BasicType;
+}
+
+// A UcharType represents an unsigned character type.
+type UcharType struct {
+       BasicType;
+}
+
+// An IntType represents a signed integer type.
+type IntType struct {
+       BasicType;
+}
+
+// A UintType represents an unsigned integer type.
+type UintType struct {
+       BasicType;
+}
+
+// A FloatType represents a floating point type.
+type FloatType struct {
+       BasicType;
+}
+
+// A ComplexType represents a complex floating point type.
+type ComplexType struct {
+       BasicType;
+}
+
+// A BoolType represents a boolean type.
+type BoolType struct {
+       BasicType;
+}
+
+// An AddrType represents a machine address type.
+type AddrType struct {
+       BasicType;
+}
+
+// qualifiers
+
+// A QualType represents a type that has the C/C++ "const", "restrict", or "volatile" qualifier.
+type QualType struct {
+       CommonType;
+       Qual string;
+       Type Type;
+}
+
+func (t *QualType) String() string {
+       return t.Qual + " " + t.Type.String();
+}
+
+// An ArrayType represents a fixed size array type.
+type ArrayType struct {
+       CommonType;
+       Type Type;
+       StrideBitSize int64;    // if > 0, number of bits to hold each element
+       Count int64;
+}
+
+func (t *ArrayType) String() string {
+       return "[" + strconv.Itoa64(t.Count) + "]" + t.Type.String();
+}
+
+// A VoidType represents the C void type.
+// It is only used as the subtype for a pointer:
+// a FuncType that returns no value has a nil ReturnType.
+type VoidType struct {
+       CommonType;
+}
+
+func (t *VoidType) String() string {
+       return "void";
+}
+
+// A PtrType represents a pointer type.
+type PtrType struct {
+       CommonType;
+       Type Type;
+}
+
+func (t *PtrType) String() string {
+       return "*" + t.Type.String();
+}
+
+// A StructType represents a struct, union, or C++ class type.
+type StructType struct {
+       CommonType;
+       StructName string;
+       Kind string;    // "struct", "union", or "class".
+       Field []*StructField;
+       Incomplete bool;        // if true, struct, union, class is declared but not defined
+}
+
+// A StructField represents a field in a struct, union, or C++ class type.
+type StructField struct {
+       Name string;
+       Type Type;
+       ByteOffset int64;
+       ByteSize int64;
+       BitOffset int64;        // within the ByteSize bytes at ByteOffset
+       BitSize int64;  // zero if not a bit field
+}
+
+func (t *StructType) String() string {
+       if t.StructName != "" {
+               return t.Kind + " " + t.StructName;
+       }
+       return t.Defn();
+}
+
+func (t *StructType) Defn() string {
+       s := t.Kind;
+       if t.StructName != "" {
+               s += " " + t.StructName;
+       }
+       if t.Incomplete {
+               s += " /*incomplete*/";
+               return s;
+       }
+       s += " {";
+       for i, f := range t.Field {
+               if i > 0 {
+                       s += "; ";
+               }
+               s += f.Name + " " + f.Type.String();
+               s += "@" + strconv.Itoa64(f.ByteOffset);
+               if f.BitSize > 0 {
+                       s += " : " + strconv.Itoa64(f.BitSize);
+                       s += "@" + strconv.Itoa64(f.BitOffset);
+               }
+       }
+       s += "}";
+       return s;
+}
+
+// An EnumType represents an enumerated type.
+// The only indication of its native integer type is its ByteSize
+// (inside CommonType).
+type EnumType struct {
+       CommonType;
+       EnumName string;
+       Val []*EnumValue;
+}
+
+// An EnumValue represents a single enumeration value.
+type EnumValue struct {
+       Name string;
+       Val int64;
+}
+
+func (t *EnumType) String() string {
+       s := "enum";
+       if t.EnumName != "" {
+               s += " " + t.EnumName;
+       }
+       s += " {";
+       for i, v := range t.Val {
+               if i > 0 {
+                       s += "; ";
+               }
+               s += v.Name + "=" + strconv.Itoa64(v.Val);
+       }
+       s += "}";
+       return s;
+}
+
+// A FuncType represents a function type.
+type FuncType struct {
+       CommonType;
+       ReturnType Type;
+       ParamType []Type;
+}
+
+func (t *FuncType) String() string {
+       s := "func(";
+       for i, t := range t.ParamType {
+               if i > 0 {
+                       s += ", ";
+               }
+               s += t.String();
+       }
+       s += ")";
+       if t.ReturnType != nil {
+               s += " " + t.ReturnType.String();
+       }
+       return s;
+}
+
+// A DotDotDotType represents the variadic ... function parameter.
+type DotDotDotType struct {
+       CommonType;
+}
+
+func (t *DotDotDotType) String() string {
+       return "...";
+}
+
+// A TypedefType represents a named type.
+type TypedefType struct {
+       CommonType;
+       Type Type;
+}
+
+func (t *TypedefType) String() string {
+       return t.Name;
+}
+
+// A Type conventionally represents a pointer to any of the
+// specific Type structures (CharType, StructType, etc.).
+type Type interface {
+       Common() *CommonType;
+       String() string;
+}
+
+func (d *Data) Type(off Offset) (Type, os.Error) {
+       if t, ok := d.typeCache[off]; ok {
+               return t, nil;
+       }
+
+       r := d.Reader();
+       r.Seek(off);
+       e, err := r.Next();
+       if err != nil {
+               return nil, err;
+       }
+       if e == nil || e.Offset != off {
+               return nil, DecodeError{"info", off, "no type at offset"};
+       }
+
+       // Parse type from Entry.
+       // Must always set d.typeCache[off] before calling
+       // d.Type recursively, to handle circular types correctly.
+       var typ Type;
+
+       // Get next child; set err if error happens.
+       next := func() *Entry {
+               if !e.Children {
+                       return nil;
+               }
+               kid, err1 := r.Next();
+               if err1 != nil {
+                       err = err1;
+                       return nil;
+               }
+               if kid == nil {
+                       err = DecodeError{"info", r.b.off, "unexpected end of DWARF entries"};
+                       return nil;
+               }
+               if kid.Tag == 0 {
+                       return nil;
+               }
+               return kid;
+       };
+
+       // Get Type referred to by Entry's AttrType field.
+       // Set err if error happens.  Not having a type is an error.
+       typeOf := func(e *Entry) Type {
+               toff, ok := e.Val(AttrType).(Offset);
+               if !ok {
+                       err = DecodeError{"info", e.Offset, "missing type attribute"};
+                       return nil;
+               }
+               var t Type;
+               if t, err = d.Type(toff); err != nil {
+                       return nil;
+               }
+               return t;
+       };
+
+       switch e.Tag {
+       case TagArrayType:
+               // Multi-dimensional array.  (DWARF v2 §5.4)
+               // Attributes:
+               //      AttrType:subtype [required]
+               //      AttrStrideSize: size in bits of each element of the array
+               //      AttrByteSize: size of entire array
+               // Children:
+               //      TagSubrangeType or TagEnumerationType giving one dimension.
+               //      dimensions are in left to right order.
+               t := new(ArrayType);
+               typ = t;
+               d.typeCache[off] = t;
+               if t.Type = typeOf(e); err != nil {
+                       goto Error;
+               }
+               t.StrideBitSize, _ = e.Val(AttrStrideSize).(int64);
+
+               // Accumulate dimensions,
+               ndim := 0;
+               for kid := next(); kid != nil; kid = next() {
+                       // TODO(rsc): Can also be TagEnumerationType
+                       // but haven't seen that in the wild yet.
+                       switch kid.Tag {
+                       case TagSubrangeType:
+                               max, ok := kid.Val(AttrUpperBound).(int64);
+                               if !ok {
+                                       err = DecodeError{"info", kid.Offset, "missing upper bound"};
+                                       goto Error;
+                               }
+                               if ndim == 0 {
+                                       t.Count = max+1;
+                               } else {
+                                       // Multidimensional array.
+                                       // Create new array type underneath this one.
+                                       t.Type = &ArrayType{Type: t.Type, Count: max+1};
+                               }
+                               ndim++;
+                       case TagEnumerationType:
+                               err = DecodeError{"info", kid.Offset, "cannot handle enumeration type as array bound"};
+                               goto Error;
+                       }
+               }
+               if ndim == 0 {
+                       err = DecodeError{"info", e.Offset, "missing dimension for array"};
+                       goto Error;
+               }
+
+       case TagBaseType:
+               // Basic type.  (DWARF v2 §5.1)
+               // Attributes:
+               //      AttrName: name of base type in programming language of the compilation unit [required]
+               //      AttrEncoding: encoding value for type (encFloat etc) [required]
+               //      AttrByteSize: size of type in bytes [required]
+               //      AttrBitOffset: for sub-byte types, size in bits
+               //      AttrBitSize: for sub-byte types, bit offset of high order bit in the AttrByteSize bytes
+               name, _ := e.Val(AttrName).(string);
+               enc, ok := e.Val(AttrEncoding).(int64);
+               if !ok {
+                       err = DecodeError{"info", e.Offset, "missing encoding attribute for " + name};
+                       goto Error;
+               }
+               switch enc {
+               default:
+                       err = DecodeError{"info", e.Offset, "unrecognized encoding attribute value"};
+                       goto Error;
+
+               case encAddress:
+                       typ = new(AddrType);
+               case encBoolean:
+                       typ = new(BoolType);
+               case encComplexFloat:
+                       typ = new(ComplexType);
+               case encFloat:
+                       typ = new(FloatType);
+               case encSigned:
+                       typ = new(IntType);
+               case encUnsigned:
+                       typ = new(UintType);
+               case encSignedChar:
+                       typ = new(CharType);
+               case encUnsignedChar:
+                       typ = new(UcharType);
+               }
+               d.typeCache[off] = typ;
+               t := typ.(interface{Basic() *BasicType}).Basic();
+               t.Name = name;
+               t.BitSize, _ = e.Val(AttrBitSize).(int64);
+               t.BitOffset, _ = e.Val(AttrBitOffset).(int64);
+
+       case TagClassType, TagStructType, TagUnionType:
+               // Structure, union, or class type.  (DWARF v2 §5.5)
+               // Attributes:
+               //      AttrName: name of struct, union, or class
+               //      AttrByteSize: byte size [required]
+               //      AttrDeclaration: if true, struct/union/class is incomplete
+               // Children:
+               //      TagMember to describe one member.
+               //              AttrName: name of member [required]
+               //              AttrType: type of member [required]
+               //              AttrByteSize: size in bytes
+               //              AttrBitOffset: bit offset within bytes for bit fields
+               //              AttrBitSize: bit size for bit fields
+               //              AttrDataMemberLoc: location within struct [required for struct, class]
+               // There is much more to handle C++, all ignored for now.
+               t := new(StructType);
+               typ = t;
+               d.typeCache[off] = t;
+               switch e.Tag {
+               case TagClassType:
+                       t.Kind = "class";
+               case TagStructType:
+                       t.Kind = "struct";
+               case TagUnionType:
+                       t.Kind = "union";
+               }
+               t.StructName, _ = e.Val(AttrName).(string);
+               t.Incomplete = e.Val(AttrDeclaration) != nil;
+               t.Field = make([]*StructField, 0, 8);
+               for kid := next(); kid != nil; kid = next() {
+                       if kid.Tag == TagMember {
+                               f := new(StructField);
+                               if f.Type = typeOf(kid); err != nil {
+                                       goto Error;
+                               }
+                               if loc, ok := kid.Val(AttrDataMemberLoc).([]byte); ok {
+                                       b := makeBuf(d, "location", 0, loc, d.addrsize);
+                                       if b.uint8() != opPlusUconst {
+                                               err = DecodeError{"info", kid.Offset, "unexpected opcode"};
+                                               goto Error;
+                                       }
+                                       f.ByteOffset = int64(b.uint());
+                                       if b.err != nil {
+                                               err = b.err;
+                                               goto Error;
+                                       }
+                               }
+                               f.Name, _ = kid.Val(AttrName).(string);
+                               f.ByteSize, _ = kid.Val(AttrByteSize).(int64);
+                               f.BitOffset, _ = kid.Val(AttrBitOffset).(int64);
+                               f.BitSize, _ = kid.Val(AttrBitSize).(int64);
+                               n := len(t.Field);
+                               if n >= cap(t.Field) {
+                                       fld := make([]*StructField, n, n*2);
+                                       for i, f := range t.Field {
+                                               fld[i] = f;
+                                       }
+                                       t.Field = fld;
+                               }
+                               t.Field = t.Field[0:n+1];
+                               t.Field[n] = f;
+                       }
+               }
+
+       case TagConstType, TagVolatileType, TagRestrictType:
+               // Type modifier (DWARF v2 §5.2)
+               // Attributes:
+               //      AttrType: subtype
+               t := new(QualType);
+               typ = t;
+               d.typeCache[off] = t;
+               if t.Type = typeOf(e); err != nil {
+                       goto Error;
+               }
+               switch e.Tag {
+               case TagConstType:
+                       t.Qual = "const";
+               case TagRestrictType:
+                       t.Qual = "restrict";
+               case TagVolatileType:
+                       t.Qual = "volatile";
+               }
+
+       case TagEnumerationType:
+               // Enumeration type (DWARF v2 §5.6)
+               // Attributes:
+               //      AttrName: enum name if any
+               //      AttrByteSize: bytes required to represent largest value
+               // Children:
+               //      TagEnumerator:
+               //              AttrName: name of constant
+               //              AttrConstValue: value of constant
+               t := new(EnumType);
+               typ = t;
+               d.typeCache[off] = t;
+               t.EnumName, _ = e.Val(AttrName).(string);
+               t.Val = make([]*EnumValue, 0, 8);
+               for kid := next(); kid != nil; kid = next() {
+                       if kid.Tag == TagEnumerator {
+                               f := new(EnumValue);
+                               f.Name, _ = kid.Val(AttrName).(string);
+                               f.Val, _ = kid.Val(AttrConstValue).(int64);
+                               n := len(t.Val);
+                               if n >= cap(t.Val) {
+                                       val := make([]*EnumValue, n, n*2);
+                                       for i, f := range t.Val {
+                                               val[i] = f;
+                                       }
+                                       t.Val = val;
+                               }
+                               t.Val = t.Val[0:n+1];
+                               t.Val[n] = f;
+                       }
+               }
+
+       case TagPointerType:
+               // Type modifier (DWARF v2 §5.2)
+               // Attributes:
+               //      AttrType: subtype [not required!  void* has no AttrType]
+               //      AttrAddrClass: address class [ignored]
+               t := new(PtrType);
+               typ = t;
+               d.typeCache[off] = t;
+               if e.Val(AttrType) == nil {
+                       t.Type = &VoidType{};
+                       break;
+               }
+               t.Type = typeOf(e);
+
+       case TagSubroutineType:
+               // Subroutine type.  (DWARF v2 §5.7)
+               // Attributes:
+               //      AttrType: type of return value if any
+               //      AttrName: possible name of type [ignored]
+               //      AttrPrototyped: whether used ANSI C prototye [ignored]
+               // Children:
+               //      TagFormalParameter: typed parameter
+               //              AttrType: type of parameter
+               //      TagUnspecifiedParameter: final ...
+               t := new(FuncType);
+               typ = t;
+               d.typeCache[off] = t;
+               if e.Val(AttrType) != nil {
+                       if t.ReturnType = typeOf(e); err != nil {
+                               goto Error;
+                       }
+               }
+               t.ParamType = make([]Type, 0, 8);
+               for kid := next(); kid != nil; kid = next() {
+                       var tkid Type;
+                       switch kid.Tag {
+                       default:
+                               continue;
+                       case TagFormalParameter:
+                               if tkid = typeOf(kid); err != nil {
+                                       goto Error;
+                               }
+                       case TagUnspecifiedParameters:
+                               tkid = &DotDotDotType{};
+                       }
+                       n := len(t.ParamType);
+                       if n >= cap(t.ParamType) {
+                               param := make([]Type, n, n*2);
+                               for i, t := range t.ParamType {
+                                       param[i] = t;
+                               }
+                               t.ParamType = param;
+                       }
+                       t.ParamType = t.ParamType[0:n+1];
+                       t.ParamType[n] = tkid;
+               }
+
+       case TagTypedef:
+               // Typedef (DWARF v2 §5.3)
+               // Attributes:
+               //      AttrName: name [required]
+               //      AttrType: type definition [required]
+               t := new(TypedefType);
+               typ = t;
+               d.typeCache[off] = t;
+               t.Name, _ = e.Val(AttrName).(string);
+               t.Type = typeOf(e);
+       }
+
+       if err != nil {
+               goto Error;
+       }
+
+       typ.Common().ByteSize, _ = e.Val(AttrByteSize).(int64);
+
+       return typ, nil;
+
+Error:
+       // If the parse fails, take the type out of the cache
+       // so that the next call with this offset doesn't hit
+       // the cache and return success.
+       d.typeCache[off] = nil, false;
+       return nil, err;
+}
diff --git a/src/pkg/debug/dwarf/type_test.go b/src/pkg/debug/dwarf/type_test.go
new file mode 100644 (file)
index 0000000..ea7f219
--- /dev/null
@@ -0,0 +1,102 @@
+// 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 (
+       "debug/elf";
+       "testing";
+)
+
+var typedefTests = map[string]string {
+       "t_ptr_volatile_int": "*volatile int",
+       "t_ptr_const_char": "*const char",
+       "t_long": "long int",
+       "t_ushort": "short unsigned int",
+       "t_func_int_of_float_double": "func(float, double) int",
+       "t_ptr_func_int_of_float_double": "*func(float, double) int",
+       "t_func_ptr_int_of_char_schar_uchar": "func(char, signed char, unsigned char) *int",
+       "t_func_void_of_char": "func(char)",
+       "t_func_void_of_void": "func()",
+       "t_func_void_of_ptr_char_dots": "func(*char, ...)",
+       "t_my_struct": "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; array [40]long long int@8}",
+       "t_my_union": "union my_union {vi volatile int@0; x char@0 : 1@7; y int@0 : 4@28; array [40]long long int@0}",
+       "t_my_enum": "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}",
+       "t_my_list": "struct list {val short int@0; next *t_my_list@8}",
+       "t_my_tree": "struct tree {left *struct tree@0; right *struct tree@8; val long long unsigned int@16}"
+};
+
+func elfData(t *testing.T, name string) *Data {
+       f, err := elf.Open(name);
+       if err != nil {
+               t.Fatal(err);
+       }
+       
+       dat := func(name string) []byte {
+               s := f.Section(".debug_" + name);
+               if s == nil {
+                       return nil
+               }
+               b, err := s.Data();
+               if err != nil {
+                       t.Fatal(".debug_"+name+":", err);
+               }
+               return b;
+       };
+       
+       d, err := New(dat("abbrev"), nil, nil, dat("info"), nil, nil, nil, dat("str"));
+       if err != nil {
+               t.Fatal("New:", err);
+       }
+       
+       return d;
+}
+
+
+func TestTypedefs(t *testing.T) {
+       d := elfData(t, "testdata/typedef.elf");
+       r := d.Reader();
+       seen := make(map[string]bool);
+       for {
+               e, err := r.Next();
+               if err != nil {
+                       t.Fatal("r.Next:", err);
+               }
+               if e == nil {
+                       break;
+               }
+               if e.Tag == TagTypedef {
+                       typ, err := d.Type(e.Offset);
+                       if err != nil {
+                               t.Fatal("d.Type:", err);
+                       }
+                       t1 := typ.(*TypedefType);
+                       var typstr string;
+                       if ts, ok := t1.Type.(*StructType); ok {
+                               typstr = ts.Defn();
+                       } else {
+                               typstr = t1.Type.String();
+                       }
+                       
+                       if want, ok := typedefTests[t1.Name]; ok {
+                               if _, ok := seen[t1.Name]; ok {
+                                       t.Errorf("multiple definitions for %s", t1.Name);
+                               }
+                               seen[t1.Name] = true;
+                               if typstr != want {
+                                       t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want);
+                               }
+                       }
+               }
+               if e.Tag != TagCompileUnit {
+                       r.SkipChildren();
+               }
+       }
+       
+       for k := range typedefTests {
+               if _, ok := seen[k]; !ok {
+                       t.Errorf("missing %s", k);
+               }
+       }
+}