// 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 dwarfimport// A Type conventionally represents a pointer to any of the// specific Type structures ([CharType], [StructType], etc.).typeTypeinterface {Common() *CommonTypeString() stringSize() 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.typeCommonTypestruct { ByteSize int64// size of value of this type, in bytes Name string// name that can be used to refer to type}func ( *CommonType) () *CommonType { return }func ( *CommonType) () int64 { return .ByteSize }// Basic types// A BasicType holds fields common to all basic types.//// See the documentation for [StructField] for more info on the interpretation of// the BitSize/BitOffset/DataBitOffset fields.typeBasicTypestruct {CommonType BitSize int64 BitOffset int64 DataBitOffset int64}func ( *BasicType) () *BasicType { return }func ( *BasicType) () string {if .Name != "" {return .Name }return"?"}// A CharType represents a signed character type.typeCharTypestruct {BasicType}// A UcharType represents an unsigned character type.typeUcharTypestruct {BasicType}// An IntType represents a signed integer type.typeIntTypestruct {BasicType}// A UintType represents an unsigned integer type.typeUintTypestruct {BasicType}// A FloatType represents a floating point type.typeFloatTypestruct {BasicType}// A ComplexType represents a complex floating point type.typeComplexTypestruct {BasicType}// A BoolType represents a boolean type.typeBoolTypestruct {BasicType}// An AddrType represents a machine address type.typeAddrTypestruct {BasicType}// An UnspecifiedType represents an implicit, unknown, ambiguous or nonexistent type.typeUnspecifiedTypestruct {BasicType}// qualifiers// A QualType represents a type that has the C/C++ "const", "restrict", or "volatile" qualifier.typeQualTypestruct {CommonType Qual string Type Type}func ( *QualType) () string { return .Qual + " " + .Type.String() }func ( *QualType) () int64 { return .Type.Size() }// An ArrayType represents a fixed size array type.typeArrayTypestruct {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 ( *ArrayType) () string {return"[" + strconv.FormatInt(.Count, 10) + "]" + .Type.String()}func ( *ArrayType) () int64 {if .Count == -1 {return0 }return .Count * .Type.Size()}// A VoidType represents the C void type.typeVoidTypestruct {CommonType}func ( *VoidType) () string { return"void" }// A PtrType represents a pointer type.typePtrTypestruct {CommonType Type Type}func ( *PtrType) () string { return"*" + .Type.String() }// A StructType represents a struct, union, or C++ class type.typeStructTypestruct {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.//// # Bit Fields//// The BitSize, BitOffset, and DataBitOffset fields describe the bit// size and offset of data members declared as bit fields in C/C++// struct/union/class types.//// BitSize is the number of bits in the bit field.//// DataBitOffset, if non-zero, is the number of bits from the start of// the enclosing entity (e.g. containing struct/class/union) to the// start of the bit field. This corresponds to the DW_AT_data_bit_offset// DWARF attribute that was introduced in DWARF 4.//// BitOffset, if non-zero, is the number of bits between the most// significant bit of the storage unit holding the bit field to the// most significant bit of the bit field. Here "storage unit" is the// type name before the bit field (for a field "unsigned x:17", the// storage unit is "unsigned"). BitOffset values can vary depending on// the endianness of the system. BitOffset corresponds to the// DW_AT_bit_offset DWARF attribute that was deprecated in DWARF 4 and// removed in DWARF 5.//// At most one of DataBitOffset and BitOffset will be non-zero;// DataBitOffset/BitOffset will only be non-zero if BitSize is// non-zero. Whether a C compiler uses one or the other// will depend on compiler vintage and command line options.//// Here is an example of C/C++ bit field use, along with what to// expect in terms of DWARF bit offset info. Consider this code://// struct S {// int q;// int j:5;// int k:6;// int m:5;// int n:8;// } s;//// For the code above, one would expect to see the following for// DW_AT_bit_offset values (using GCC 8)://// Little | Big// Endian | Endian// |// "j": 27 | 0// "k": 21 | 5// "m": 16 | 11// "n": 8 | 16//// Note that in the above the offsets are purely with respect to the// containing storage unit for j/k/m/n -- these values won't vary based// on the size of prior data members in the containing struct.//// If the compiler emits DW_AT_data_bit_offset, the expected values// would be://// "j": 32// "k": 37// "m": 43// "n": 48//// Here the value 32 for "j" reflects the fact that the bit field is// preceded by other data members (recall that DW_AT_data_bit_offset// values are relative to the start of the containing struct). Hence// DW_AT_data_bit_offset values can be quite large for structs with// many fields.//// DWARF also allow for the possibility of base types that have// non-zero bit size and bit offset, so this information is also// captured for base types, but it is worth noting that it is not// possible to trigger this behavior using mainstream languages.typeStructFieldstruct { Name string Type Type ByteOffset int64 ByteSize int64// usually zero; use Type.Size() for normal fields BitOffset int64 DataBitOffset int64 BitSize int64// zero if not a bit field}func ( *StructType) () string {if .StructName != "" {return .Kind + " " + .StructName }return .Defn()}func ( *StructField) () int64 {if .BitOffset != 0 {return .BitOffset }return .DataBitOffset}func ( *StructType) () string { := .Kindif .StructName != "" { += " " + .StructName }if .Incomplete { += " /*incomplete*/"return } += " {"for , := range .Field {if > 0 { += "; " } += .Name + " " + .Type.String() += "@" + strconv.FormatInt(.ByteOffset, 10)if .BitSize > 0 { += " : " + strconv.FormatInt(.BitSize, 10) += "@" + strconv.FormatInt(.bitOffset(), 10) } } += "}"return}// An EnumType represents an enumerated type.// The only indication of its native integer type is its ByteSize// (inside [CommonType]).typeEnumTypestruct {CommonType EnumName string Val []*EnumValue}// An EnumValue represents a single enumeration value.typeEnumValuestruct { Name string Val int64}func ( *EnumType) () string { := "enum"if .EnumName != "" { += " " + .EnumName } += " {"for , := range .Val {if > 0 { += "; " } += .Name + "=" + strconv.FormatInt(.Val, 10) } += "}"return}// A FuncType represents a function type.typeFuncTypestruct {CommonType ReturnType Type ParamType []Type}func ( *FuncType) () string { := "func("for , := range .ParamType {if > 0 { += ", " } += .String() } += ")"if .ReturnType != nil { += " " + .ReturnType.String() }return}// A DotDotDotType represents the variadic ... function parameter.typeDotDotDotTypestruct {CommonType}func ( *DotDotDotType) () string { return"..." }// A TypedefType represents a named type.typeTypedefTypestruct {CommonType Type Type}func ( *TypedefType) () string { return .Name }func ( *TypedefType) () int64 { return .Type.Size() }// An UnsupportedType is a placeholder returned in situations where we// encounter a type that isn't supported.typeUnsupportedTypestruct {CommonType Tag Tag}func ( *UnsupportedType) () string {if .Name != "" {return .Name }return .Name + "(unsupported type " + .Tag.String() + ")"}// typeReader is used to read from either the info section or the// types section.type typeReader interface { Seek(Offset) Next() (*Entry, error) clone() typeReader offset() Offset// AddressSize returns the size in bytes of addresses in the current // compilation unit. AddressSize() int}// Type reads the type at off in the DWARF “info” section.func ( *Data) ( Offset) (Type, error) {return .readType("info", .Reader(), , .typeCache, nil)}type typeFixer struct { typedefs []*TypedefType arraytypes []*Type}func ( *typeFixer) ( *Type) {if == nil {return } , := (*).(*ArrayType)if { .arraytypes = append(.arraytypes, ) }}func ( *typeFixer) () {for , := range .typedefs { .Common().ByteSize = .Type.Size() }for , := range .arraytypes {zeroArray() }}// readType reads a type from r at off of name. It adds types to the// type cache, appends new typedef types to typedefs, and computes the// sizes of types. Callers should pass nil for typedefs; this is used// for internal recursion.func ( *Data) ( string, typeReader, Offset, map[Offset]Type, *typeFixer) (Type, error) {if , := []; {return , nil } .Seek() , := .Next()if != nil {returnnil, } := .AddressSize()if == nil || .Offset != {returnnil, DecodeError{, , "no type at offset"} }// If this is the root of the recursion, prepare to resolve // typedef sizes and perform other fixups once the recursion is // done. This must be done after the type graph is constructed // because it may need to resolve cycles in a different order than // readType encounters them.if == nil {vartypeFixerdeferfunc() { .apply() }() = & }// Parse type from Entry. // Must always set typeCache[off] before calling // d.readType recursively, to handle circular types correctly.varType := 0// Get next child; set err if error happens. := func() *Entry {if !.Children {returnnil }// Only return direct children. // Skip over composite entries that happen to be nested // inside this one. Most DWARF generators wouldn't generate // such a thing, but clang does. // See golang.org/issue/6472.for { , := .Next()if != nil { = returnnil }if == nil { = DecodeError{, .offset(), "unexpected end of DWARF entries"}returnnil }if .Tag == 0 {if > 0 { --continue }returnnil }if .Children { ++ }if > 0 {continue }return } }// Get Type referred to by Entry's AttrType field. // Set err if error happens. Not having a type is an error. := func( *Entry) Type { := .Val(AttrType)varTypeswitch toff := .(type) {caseOffset:if , = .(, .clone(), , , ); != nil {returnnil }caseuint64:if , = .sigToType(); != nil {returnnil }default:// It appears that no Type means "void".returnnew(VoidType) }return }switch .Tag {caseTagArrayType:// 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. := new(ArrayType) = [] = if .Type = (); != nil {goto } .StrideBitSize, _ = .Val(AttrStrideSize).(int64)// Accumulate dimensions,var []int64for := (); != nil; = () {// TODO(rsc): Can also be TagEnumerationType // but haven't seen that in the wild yet.switch .Tag {caseTagSubrangeType: , := .Val(AttrCount).(int64)if ! {// Old binaries may have an upper bound instead. , = .Val(AttrUpperBound).(int64)if { ++ // Length is one more than upper bound. } elseiflen() == 0 { = -1// As in x[]. } } = append(, )caseTagEnumerationType: = DecodeError{, .Offset, "cannot handle enumeration type as array bound"}goto } }iflen() == 0 {// LLVM generates this for x[]. = []int64{-1} } .Count = [0]for := len() - 1; >= 1; -- { .Type = &ArrayType{Type: .Type, Count: []} }caseTagBaseType:// 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: bit offset of value within containing storage unit // AttrDataBitOffset: bit offset of value within containing storage unit // AttrBitSize: size in bits // // For most languages BitOffset/DataBitOffset/BitSize will not be present // for base types. , := .Val(AttrName).(string) , := .Val(AttrEncoding).(int64)if ! { = DecodeError{, .Offset, "missing encoding attribute for " + }goto }switch {default: = DecodeError{, .Offset, "unrecognized encoding attribute value"}gotocaseencAddress: = new(AddrType)caseencBoolean: = new(BoolType)caseencComplexFloat: = new(ComplexType)if == "complex" {// clang writes out 'complex' instead of 'complex float' or 'complex double'. // clang also writes out a byte size that we can use to distinguish. // See issue 8694.switch , := .Val(AttrByteSize).(int64); {case8: = "complex float"case16: = "complex double" } }caseencFloat: = new(FloatType)caseencSigned: = new(IntType)caseencUnsigned: = new(UintType)caseencSignedChar: = new(CharType)caseencUnsignedChar: = new(UcharType) } [] = := .(interface { () *BasicType }).() .Name = .BitSize, _ = .Val(AttrBitSize).(int64) := false := false .BitOffset, = .Val(AttrBitOffset).(int64) .DataBitOffset, = .Val(AttrDataBitOffset).(int64)if && { = DecodeError{, .Offset, "duplicate bit offset attributes"}goto }caseTagClassType, 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 // AttrDataBitOffset: field bit offset relative to struct start // 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. := new(StructType) = [] = switch .Tag {caseTagClassType: .Kind = "class"caseTagStructType: .Kind = "struct"caseTagUnionType: .Kind = "union" } .StructName, _ = .Val(AttrName).(string) .Incomplete = .Val(AttrDeclaration) != nil .Field = make([]*StructField, 0, 8)var *Typevarint64varint64for := (); != nil; = () {if .Tag != TagMember {continue } := new(StructField)if .Type = (); != nil {goto }switch loc := .Val(AttrDataMemberLoc).(type) {case []byte:// TODO: Should have original compilation // unit here, not unknownFormat. := makeBuf(, unknownFormat{}, "location", 0, )if .uint8() != opPlusUconst { = DecodeError{, .Offset, "unexpected opcode"}goto } .ByteOffset = int64(.uint())if .err != nil { = .errgoto }caseint64: .ByteOffset = } .Name, _ = .Val(AttrName).(string) .ByteSize, _ = .Val(AttrByteSize).(int64) := false := false .BitOffset, = .Val(AttrBitOffset).(int64) .DataBitOffset, = .Val(AttrDataBitOffset).(int64)if && { = DecodeError{, .Offset, "duplicate bit offset attributes"}goto } .BitSize, _ = .Val(AttrBitSize).(int64) .Field = append(.Field, )if == 0 && == .ByteOffset && .Kind != "union" {// Last field was zero width. Fix array length. // (DWARF writes out 0-length arrays as if they were 1-length arrays.) .recordArrayType() } = &.Type = .ByteOffset = .BitSize }if .Kind != "union" { , := .Val(AttrByteSize).(int64)if && == {// Final field must be zero width. Fix array length. .recordArrayType() } }caseTagConstType, TagVolatileType, TagRestrictType:// Type modifier (DWARF v2 §5.2) // Attributes: // AttrType: subtype := new(QualType) = [] = if .Type = (); != nil {goto }switch .Tag {caseTagConstType: .Qual = "const"caseTagRestrictType: .Qual = "restrict"caseTagVolatileType: .Qual = "volatile" }caseTagEnumerationType:// 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 := new(EnumType) = [] = .EnumName, _ = .Val(AttrName).(string) .Val = make([]*EnumValue, 0, 8)for := (); != nil; = () {if .Tag == TagEnumerator { := new(EnumValue) .Name, _ = .Val(AttrName).(string) .Val, _ = .Val(AttrConstValue).(int64) := len(.Val)if >= cap(.Val) { := make([]*EnumValue, , *2)copy(, .Val) .Val = } .Val = .Val[0 : +1] .Val[] = } }caseTagPointerType:// Type modifier (DWARF v2 §5.2) // Attributes: // AttrType: subtype [not required! void* has no AttrType] // AttrAddrClass: address class [ignored] := new(PtrType) = [] = if .Val(AttrType) == nil { .Type = &VoidType{}break } .Type = ()caseTagSubroutineType:// 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 prototype [ignored] // Children: // TagFormalParameter: typed parameter // AttrType: type of parameter // TagUnspecifiedParameter: final ... := new(FuncType) = [] = if .ReturnType = (); != nil {goto } .ParamType = make([]Type, 0, 8)for := (); != nil; = () {varTypeswitch .Tag {default:continuecaseTagFormalParameter:if = (); != nil {goto }caseTagUnspecifiedParameters: = &DotDotDotType{} } .ParamType = append(.ParamType, ) }caseTagTypedef:// Typedef (DWARF v2 §5.3) // Attributes: // AttrName: name [required] // AttrType: type definition [required] := new(TypedefType) = [] = .Name, _ = .Val(AttrName).(string) .Type = ()caseTagUnspecifiedType:// Unspecified type (DWARF v3 §5.2) // Attributes: // AttrName: name := new(UnspecifiedType) = [] = .Name, _ = .Val(AttrName).(string)default:// This is some other type DIE that we're currently not // equipped to handle. Return an abstract "unsupported type" // object in such cases. := new(UnsupportedType) = [] = .Tag = .Tag .Name, _ = .Val(AttrName).(string) }if != nil {goto } { , := .Val(AttrByteSize).(int64)if ! { = -1switch t := .(type) {case *TypedefType:// Record that we need to resolve this // type's size once the type graph is // constructed. .typedefs = append(.typedefs, )case *PtrType: = int64() } } .Common().ByteSize = }return , nil:// 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.delete(, )returnnil, }func zeroArray( *Type) { := (*).(*ArrayType)if .Type.Size() == 0 {return }// Make a copy to avoid invalidating typeCache. := * .Count = 0 * = &}
The pages are generated with Goldsv0.7.0-preview. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.