Commit d7d13373 authored by Austin Clements's avatar Austin Clements

debug/dwarf: add DWARF attribute value class to Field

To return DWARF attribute values, debug/dwarf maps the DWARF attribute
value classes to Go types. Unfortunately, this mapping is ambiguous in
a way that makes it impossible to correctly interpret some DWARF
attributes as of DWARF 4. For example, AttrStartScope can be either a
constant or a rangelistptr. The attribute is interpreted differently
depending on its class, but debug/dwarf maps both classes to int64, so
the caller can't distinguish them from the Go type.
AttrDataMemberLocation is similar.

To address this, this change adds a field to type Field that indicates
the exact DWARF attribute value class of that field's value. This
makes it possible to distinguish value classes that can't be
distinguished by their Go type alone.

The root of this type ambiguity was DWARF itself. For example, DWARF 2
made no distinction between constants that were just constants and
constants that were section offsets because no attribute could have
both meanings. Hence, the single int64 type was sufficient. To avoid
introducing just another layer of ambiguity, this change takes pains
to canonicalize ambiguous classes in DWARF 2 and 3 files into the
unambiguous classes of DWARF 4.

Of course, there's no guarantee that future DWARF versions won't do
the same thing again and further subdivide the DWARF 4 classes. This
change gets ahead of this somewhat by distinguishing the various *ptr
classes even though the encoding does not. If there's some other form
of split, we can handle this in a backwards-compatible way by
introducing, for example, a Class5 field and type.

Change-Id: I4ef96d1223b0fd7f96ecf44fcc0e704a36af02b4
Reviewed-on: https://go-review.googlesource.com/8502Reviewed-by: 's avatarIan Lance Taylor <iant@golang.org>
parent ced7ffe9
// generated by stringer -type=Class; DO NOT EDIT
package dwarf
import "fmt"
const _Class_name = "ClassAddressClassBlockClassConstantClassExprLocClassFlagClassLinePtrClassLocListPtrClassMacPtrClassRangeListPtrClassReferenceClassStringClassReferenceAltClassStringAlt"
var _Class_index = [...]uint8{0, 12, 22, 35, 47, 56, 68, 83, 94, 111, 125, 136, 153, 167}
func (i Class) String() string {
i -= 1
if i < 0 || i+1 >= Class(len(_Class_index)) {
return fmt.Sprintf("Class(%d)", i+1)
}
return _Class_name[_Class_index[i]:_Class_index[i+1]]
}
......@@ -23,8 +23,9 @@ type abbrev struct {
}
type afield struct {
attr Attr
fmt format
attr Attr
fmt format
class Class
}
// a map from entry format ids to their descriptions
......@@ -32,7 +33,7 @@ type abbrevTable map[uint32]abbrev
// ParseAbbrev returns the abbreviation table that starts at byte off
// in the .debug_abbrev section.
func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) {
func (d *Data) parseAbbrev(off uint32, vers int) (abbrevTable, error) {
if m, ok := d.abbrevCache[off]; ok {
return m, nil
}
......@@ -80,6 +81,7 @@ func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) {
for i := range a.field {
a.field[i].attr = Attr(b.uint())
a.field[i].fmt = format(b.uint())
a.field[i].class = formToClass(a.field[i].fmt, a.field[i].attr, vers, &b)
}
b.uint()
b.uint()
......@@ -93,6 +95,118 @@ func (d *Data) parseAbbrev(off uint32) (abbrevTable, error) {
return m, nil
}
// attrIsExprloc indicates attributes that allow exprloc values that
// are encoded as block values in DWARF 2 and 3. See DWARF 4, Figure
// 20.
var attrIsExprloc = map[Attr]bool{
AttrLocation: true,
AttrByteSize: true,
AttrBitOffset: true,
AttrBitSize: true,
AttrStringLength: true,
AttrLowerBound: true,
AttrReturnAddr: true,
AttrStrideSize: true,
AttrUpperBound: true,
AttrCount: true,
AttrDataMemberLoc: true,
AttrFrameBase: true,
AttrSegment: true,
AttrStaticLink: true,
AttrUseLocation: true,
AttrVtableElemLoc: true,
AttrAllocated: true,
AttrAssociated: true,
AttrDataLocation: true,
AttrStride: true,
}
// attrPtrClass indicates the *ptr class of attributes that have
// encoding formSecOffset in DWARF 4 or formData* in DWARF 2 and 3.
var attrPtrClass = map[Attr]Class{
AttrLocation: ClassLocListPtr,
AttrStmtList: ClassLinePtr,
AttrStringLength: ClassLocListPtr,
AttrReturnAddr: ClassLocListPtr,
AttrStartScope: ClassRangeListPtr,
AttrDataMemberLoc: ClassLocListPtr,
AttrFrameBase: ClassLocListPtr,
AttrMacroInfo: ClassMacPtr,
AttrSegment: ClassLocListPtr,
AttrStaticLink: ClassLocListPtr,
AttrUseLocation: ClassLocListPtr,
AttrVtableElemLoc: ClassLocListPtr,
AttrRanges: ClassRangeListPtr,
}
// formToClass returns the DWARF 4 Class for the given form. If the
// DWARF version is less then 4, it will disambiguate some forms
// depending on the attribute.
func formToClass(form format, attr Attr, vers int, b *buf) Class {
switch form {
default:
b.error("cannot determine class of unknown attribute form")
return 0
case formAddr:
return ClassAddress
case formDwarfBlock1, formDwarfBlock2, formDwarfBlock4, formDwarfBlock:
// In DWARF 2 and 3, ClassExprLoc was encoded as a
// block. DWARF 4 distinguishes ClassBlock and
// ClassExprLoc, but there are no attributes that can
// be both, so we also promote ClassBlock values in
// DWARF 4 that should be ClassExprLoc in case
// producers get this wrong.
if attrIsExprloc[attr] {
return ClassExprLoc
}
return ClassBlock
case formData1, formData2, formData4, formData8, formSdata, formUdata:
// In DWARF 2 and 3, ClassPtr was encoded as a
// constant. Unlike ClassExprLoc/ClassBlock, some
// DWARF 4 attributes need to distinguish Class*Ptr
// from ClassConstant, so we only do this promotion
// for versions 2 and 3.
if class, ok := attrPtrClass[attr]; vers < 4 && ok {
return class
}
return ClassConstant
case formFlag, formFlagPresent:
return ClassFlag
case formRefAddr, formRef1, formRef2, formRef4, formRef8, formRefUdata:
return ClassReference
case formRefSig8:
return ClassReferenceSig
case formString, formStrp:
return ClassString
case formSecOffset:
// DWARF 4 defines four *ptr classes, but doesn't
// distinguish them in the encoding. Disambiguate
// these classes using the attribute.
if class, ok := attrPtrClass[attr]; ok {
return class
}
b.error("cannot determine class of unknown attribute with formSecOffset")
return 0
case formExprloc:
return ClassExprLoc
case formGnuRefAlt:
return ClassReferenceAlt
case formGnuStrpAlt:
return ClassStringAlt
}
}
// An entry is a sequence of attribute/value pairs.
type Entry struct {
Offset Offset // offset of Entry in DWARF info
......@@ -106,24 +220,111 @@ type Entry struct {
// A value can be one of several "attribute classes" defined by DWARF.
// The Go types corresponding to each class are:
//
// Class Go type
// ----- -------
// address uint64
// block []byte
// constant int64
// flag bool
// DWARF class Go type Class
// ----------- ------- -----
// address uint64 ClassAddress
// block []byte ClassBlock
// constant int64 ClassConstant
// flag bool ClassFlag
// reference
// to info dwarf.Offset (for use with Reader.Seek)
// to type unit uint64 (type signature)
// string string
// exprloc []byte
// lineptr int64
// loclistptr int64
// macptr int64
// rangelistptr int64
// to info dwarf.Offset ClassReference
// to type unit uint64 ClassReferenceSig
// string string ClassString
// exprloc []byte ClassExprLoc
// lineptr int64 ClassLinePtr
// loclistptr int64 ClassLocListPtr
// macptr int64 ClassMacPtr
// rangelistptr int64 ClassRangeListPtr
type Field struct {
Attr Attr
Val interface{}
Attr Attr
Val interface{}
Class Class
}
// A Class is the DWARF 4 class of an attibute value.
//
// In general, a given attribute's value may take on one of several
// possible classes defined by DWARF, each of which leads to a
// slightly different interpretation of the attribute.
//
// DWARF version 4 distinguishes attribute value classes more finely
// than previous versions of DWARF. The reader will disambiguate
// coarser classes from earlier versions of DWARF into the appropriate
// DWARF 4 class. For example, DWARF 2 uses "constant" for constants
// as well as all types of section offsets, but the reader will
// canonicalize attributes in DWARF 2 files that refer to section
// offsets to one of the Class*Ptr classes, even though these classes
// were only defined in DWARF 3.
type Class int
const (
// ClassAddress represents values of type uint64 that are
// addresses on the target machine.
ClassAddress Class = 1 + iota
// ClassBlock represents values of type []byte whose
// interpretation depends on the attribute.
ClassBlock
// ClassConstant represents values of type int64 that are
// constants. The interpretation of this constant depends on
// the attribute.
ClassConstant
// ClassExprLoc represents values of type []byte that contain
// an encoded DWARF expression or location description.
ClassExprLoc
// ClassFlag represents values of type bool.
ClassFlag
// ClassLinePtr represents values that are an int64 offset
// into the "line" section.
ClassLinePtr
// ClassLocListPtr repersents values that are an int64 offset
// into the "loclist" section.
ClassLocListPtr
// ClassMacPtr represents values that are an int64 offset into
// the "mac" section.
ClassMacPtr
// ClassMacPtr represents values that are an int64 offset into
// the "rangelist" section.
ClassRangeListPtr
// ClassReference represents values that are an Offset offset
// of an Entry in the info section (for use with Reader.Seek).
// The DWARF specification combines ClassReference and
// ClassReferenceSig into class "reference".
ClassReference
// ClassReferenceSig represents values that are a uint64 type
// signature referencing a type Entry.
ClassReferenceSig
// ClassString represents values that are strings. If the
// compilation unit specifies the AttrUseUTF8 flag (strongly
// recommended), the string value will be encoded in UTF-8.
// Otherwise, the encoding is unspecified.
ClassString
// ClassReferenceAlt represents values of type int64 that are
// an offset into the DWARF "info" section of an alternate
// object file.
ClassReferenceAlt
// ClassStringAlt represents values of type int64 that are an
// offset into the DWARF string section of an alternate object
// file.
ClassStringAlt
)
//go:generate stringer -type=Class
func (i Class) GoString() string {
return "dwarf." + i.String()
}
// Val returns the value associated with attribute Attr in Entry,
......@@ -167,6 +368,7 @@ func (b *buf) entry(atab abbrevTable, ubase Offset) *Entry {
}
for i := range e.Field {
e.Field[i].Attr = a.field[i].attr
e.Field[i].Class = a.field[i].class
fmt := a.field[i].fmt
if fmt == formIndirect {
fmt = format(b.uint())
......
......@@ -33,9 +33,9 @@ func (d *Data) parseTypes(name string, types []byte) error {
return b.err
}
hdroff := b.off
vers := b.uint16()
vers := int(b.uint16())
if vers != 4 {
b.error("unsupported DWARF version " + strconv.Itoa(int(vers)))
b.error("unsupported DWARF version " + strconv.Itoa(vers))
return b.err
}
var ao uint32
......@@ -49,7 +49,7 @@ func (d *Data) parseTypes(name string, types []byte) error {
}
ao = uint32(ao64)
}
atable, err := d.parseAbbrev(ao)
atable, err := d.parseAbbrev(ao, vers)
if err != nil {
return err
}
......
......@@ -67,7 +67,7 @@ func (d *Data) parseUnits() ([]unit, error) {
break
}
u.vers = int(vers)
atable, err := d.parseAbbrev(b.uint32())
atable, err := d.parseAbbrev(b.uint32(), u.vers)
if err != nil {
if b.err == nil {
b.err = err
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment