1
0
mirror of https://github.com/golang/go synced 2024-11-25 22:07:58 -07:00

parse and present DWARF type information

R=r
DELTA=940  (929 added, 1 deleted, 10 changed)
OCL=34679
CL=34686
This commit is contained in:
Russ Cox 2009-09-16 10:43:27 -07:00
parent f249c4114c
commit 1bbc044df9
10 changed files with 933 additions and 10 deletions

View File

@ -82,7 +82,6 @@ DIRS=\
utf8\
NOTEST=\
debug/dwarf\
debug/proc\
go/ast\
go/doc\

View File

@ -10,6 +10,7 @@ GOFILES=\
const.go\
entry.go\
open.go\
type.go\
unit.go\
include $(GOROOT)/src/Make.pkg

View 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;
}

View 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;
)

View 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();
}
}
}

View 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.

68
src/pkg/debug/dwarf/testdata/typedef.c vendored Normal file
View File

@ -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;
}

BIN
src/pkg/debug/dwarf/testdata/typedef.elf vendored Executable file

Binary file not shown.

607
src/pkg/debug/dwarf/type.go Normal file
View File

@ -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;
}

View File

@ -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);
}
}
}