Plan 9 from Bell Labs’s /usr/web/sources/contrib/ericvh/go-plan9/src/pkg/debug/dwarf/type.go

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


// 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 Type conventionally represents a pointer to any of the
// specific Type structures (CharType, StructType, etc.).
type Type interface {
	Common() *CommonType;
	String() string;
	Size() int64;
}

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

func (c *CommonType) Size() int64	{ return c.ByteSize }

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

func (t *QualType) Size() int64	{ return t.Type.Size() }

// 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;	// if == -1, an incomplete array, like char x[].
}

func (t *ArrayType) String() string {
	return "[" + strconv.Itoa64(t.Count) + "]" + t.Type.String()
}

func (t *ArrayType) Size() int64	{ return t.Count * t.Type.Size() }

// A VoidType represents the C void type.
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 }

func (t *TypedefType) Size() int64	{ return t.Type.Size() }

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 {
			// It appears that no Type means "void".
			return new(VoidType)
		}
		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 {
					max = -2	// Count == -1, as in x[].
				}
				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 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
	}

	b, ok := e.Val(AttrByteSize).(int64);
	if !ok {
		b = -1
	}
	typ.Common().ByteSize = b;

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

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.