// 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 debug information entry parser. // An entry is a sequence of data items of a given format. // The first word in the entry is an index into what DWARF // calls the ``abbreviation table.'' An abbreviation is really // just a type descriptor: it's an array of attribute tag/value format pairs. package dwarf import ( "encoding/binary" "errors" "fmt" "strconv" ) // a single entry's description: a sequence of attributes type abbrev struct { tag Tag children bool field []afield } type afield struct { attr Attr fmt format class Class val int64 // for formImplicitConst } // a map from entry format ids to their descriptions 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 uint64, vers int) (abbrevTable, error) { if m, ok := d.abbrevCache[off]; ok { return m, nil } data := d.abbrev if off > uint64(len(data)) { data = nil } else { data = data[off:] } b := makeBuf(d, unknownFormat{}, "abbrev", 0, data) // Error handling is simplified by the buf getters // returning an endless stream of 0s after an error. m := make(abbrevTable) for { // Table ends with id == 0. id := uint32(b.uint()) if id == 0 { break } // Walk over attributes, counting. n := 0 b1 := b // Read from copy of b. b1.uint() b1.uint8() for { tag := b1.uint() fmt := b1.uint() if tag == 0 && fmt == 0 { break } if format(fmt) == formImplicitConst { b1.int() } n++ } if b1.err != nil { return nil, b1.err } // Walk over attributes again, this time writing them down. var a abbrev a.tag = Tag(b.uint()) a.children = b.uint8() != 0 a.field = make([]afield, n) 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) if a.field[i].fmt == formImplicitConst { a.field[i].val = b.int() } } b.uint() b.uint() m[id] = a } if b.err != nil { return nil, b.err } d.abbrevCache[off] = m 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, // The following are new in DWARF 5. AttrStrOffsetsBase: ClassStrOffsetsPtr, AttrAddrBase: ClassAddrPtr, AttrRnglistsBase: ClassRngListsPtr, AttrLoclistsBase: ClassLocListPtr, } // 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 formIndirect: return ClassUnknown case formAddr, formAddrx, formAddrx1, formAddrx2, formAddrx3, formAddrx4: 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, formData16, formImplicitConst: // 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, formRefSup4, formRefSup8: return ClassReference case formRefSig8: return ClassReferenceSig case formString, formStrp, formStrx, formStrpSup, formLineStrp, formStrx1, formStrx2, formStrx3, formStrx4: 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 } return ClassUnknown case formExprloc: return ClassExprLoc case formGnuRefAlt: return ClassReferenceAlt case formGnuStrpAlt: return ClassStringAlt case formLoclistx: return ClassLocList case formRnglistx: return ClassRngList } } // An entry is a sequence of attribute/value pairs. type Entry struct { Offset Offset // offset of Entry in DWARF info Tag Tag // tag (kind of Entry) Children bool // whether Entry is followed by children Field []Field } // A Field is a single attribute/value pair in an Entry. // // A value can be one of several "attribute classes" defined by DWARF. // The Go types corresponding to each class are: // // DWARF class Go type Class // ----------- ------- ----- // address uint64 ClassAddress // block []byte ClassBlock // constant int64 ClassConstant // flag bool ClassFlag // reference // 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 // // For unrecognized or vendor-defined attributes, Class may be // ClassUnknown. type Field struct { Attr Attr Val any Class Class } // A Class is the DWARF 4 class of an attribute 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 ( // ClassUnknown represents values of unknown DWARF class. ClassUnknown Class = iota // ClassAddress represents values of type uint64 that are // addresses on the target machine. ClassAddress // 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 represents values that are an int64 offset // into the "loclist" section. ClassLocListPtr // ClassMacPtr represents values that are an int64 offset into // the "mac" section. ClassMacPtr // ClassRangeListPtr 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 // ClassAddrPtr represents values that are an int64 offset // into the "addr" section. ClassAddrPtr // ClassLocList represents values that are an int64 offset // into the "loclists" section. ClassLocList // ClassRngList represents values that are a uint64 offset // from the base of the "rnglists" section. ClassRngList // ClassRngListsPtr represents values that are an int64 offset // into the "rnglists" section. These are used as the base for // ClassRngList values. ClassRngListsPtr // ClassStrOffsetsPtr represents values that are an int64 // offset into the "str_offsets" section. ClassStrOffsetsPtr ) //go:generate stringer -type=Class func (i Class) GoString() string { return "dwarf." + i.String() } // 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) any { if f := e.AttrField(a); f != nil { return f.Val } return nil } // AttrField returns the Field associated with attribute Attr in // Entry, or nil if there is no such attribute. func (e *Entry) AttrField(a Attr) *Field { for i, f := range e.Field { if f.Attr == a { return &e.Field[i] } } return nil } // An Offset represents the location of an Entry within the DWARF info. // (See Reader.Seek.) type Offset uint32 // Entry reads a single entry from buf, decoding // according to the given abbreviation table. func (b *buf) entry(cu *Entry, atab abbrevTable, ubase Offset, vers int) *Entry { off := b.off id := uint32(b.uint()) if id == 0 { return &Entry{} } a, ok := atab[id] if !ok { b.error("unknown abbreviation table index") return nil } e := &Entry{ Offset: off, Tag: a.tag, Children: a.children, Field: make([]Field, len(a.field)), } // If we are currently parsing the compilation unit, // we can't evaluate Addrx or Strx until we've seen the // relevant base entry. type delayed struct { idx int off uint64 fmt format } var delay []delayed resolveStrx := func(strBase, off uint64) string { off += strBase if uint64(int(off)) != off { b.error("DW_FORM_strx offset out of range") } b1 := makeBuf(b.dwarf, b.format, "str_offsets", 0, b.dwarf.strOffsets) b1.skip(int(off)) is64, _ := b.format.dwarf64() if is64 { off = b1.uint64() } else { off = uint64(b1.uint32()) } if b1.err != nil { b.err = b1.err return "" } if uint64(int(off)) != off { b.error("DW_FORM_strx indirect offset out of range") } b1 = makeBuf(b.dwarf, b.format, "str", 0, b.dwarf.str) b1.skip(int(off)) val := b1.string() if b1.err != nil { b.err = b1.err } return val } resolveRnglistx := func(rnglistsBase, off uint64) uint64 { is64, _ := b.format.dwarf64() if is64 { off *= 8 } else { off *= 4 } off += rnglistsBase if uint64(int(off)) != off { b.error("DW_FORM_rnglistx offset out of range") } b1 := makeBuf(b.dwarf, b.format, "rnglists", 0, b.dwarf.rngLists) b1.skip(int(off)) if is64 { off = b1.uint64() } else { off = uint64(b1.uint32()) } if b1.err != nil { b.err = b1.err return 0 } if uint64(int(off)) != off { b.error("DW_FORM_rnglistx indirect offset out of range") } return rnglistsBase + off } 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()) e.Field[i].Class = formToClass(fmt, a.field[i].attr, vers, b) } var val any switch fmt { default: b.error("unknown entry attr format 0x" + strconv.FormatInt(int64(fmt), 16)) // address case formAddr: val = b.addr() case formAddrx, formAddrx1, formAddrx2, formAddrx3, formAddrx4: var off uint64 switch fmt { case formAddrx: off = b.uint() case formAddrx1: off = uint64(b.uint8()) case formAddrx2: off = uint64(b.uint16()) case formAddrx3: off = uint64(b.uint24()) case formAddrx4: off = uint64(b.uint32()) } if b.dwarf.addr == nil { b.error("DW_FORM_addrx with no .debug_addr section") } if b.err != nil { return nil } // We have to adjust by the offset of the // compilation unit. This won't work if the // program uses Reader.Seek to skip over the // unit. Not much we can do about that. var addrBase int64 if cu != nil { addrBase, _ = cu.Val(AttrAddrBase).(int64) } else if a.tag == TagCompileUnit { delay = append(delay, delayed{i, off, formAddrx}) break } var err error val, err = b.dwarf.debugAddr(b.format, uint64(addrBase), off) if err != nil { if b.err == nil { b.err = err } return nil } // block case formDwarfBlock1: val = b.bytes(int(b.uint8())) case formDwarfBlock2: val = b.bytes(int(b.uint16())) case formDwarfBlock4: val = b.bytes(int(b.uint32())) case formDwarfBlock: val = b.bytes(int(b.uint())) // constant case formData1: val = int64(b.uint8()) case formData2: val = int64(b.uint16()) case formData4: val = int64(b.uint32()) case formData8: val = int64(b.uint64()) case formData16: val = b.bytes(16) case formSdata: val = int64(b.int()) case formUdata: val = int64(b.uint()) case formImplicitConst: val = a.field[i].val // flag case formFlag: val = b.uint8() == 1 // New in DWARF 4. case formFlagPresent: // The attribute is implicitly indicated as present, and no value is // encoded in the debugging information entry itself. val = true // reference to other entry case formRefAddr: vers := b.format.version() if vers == 0 { b.error("unknown version for DW_FORM_ref_addr") } else if vers == 2 { val = Offset(b.addr()) } else { is64, known := b.format.dwarf64() if !known { b.error("unknown size for DW_FORM_ref_addr") } else if is64 { val = Offset(b.uint64()) } else { val = Offset(b.uint32()) } } case formRef1: val = Offset(b.uint8()) + ubase case formRef2: val = Offset(b.uint16()) + ubase case formRef4: val = Offset(b.uint32()) + ubase case formRef8: val = Offset(b.uint64()) + ubase case formRefUdata: val = Offset(b.uint()) + ubase // string case formString: val = b.string() case formStrp, formLineStrp: var off uint64 // offset into .debug_str is64, known := b.format.dwarf64() if !known { b.error("unknown size for DW_FORM_strp/line_strp") } else if is64 { off = b.uint64() } else { off = uint64(b.uint32()) } if uint64(int(off)) != off { b.error("DW_FORM_strp/line_strp offset out of range") } if b.err != nil { return nil } var b1 buf if fmt == formStrp { b1 = makeBuf(b.dwarf, b.format, "str", 0, b.dwarf.str) } else { if len(b.dwarf.lineStr) == 0 { b.error("DW_FORM_line_strp with no .debug_line_str section") return nil } b1 = makeBuf(b.dwarf, b.format, "line_str", 0, b.dwarf.lineStr) } b1.skip(int(off)) val = b1.string() if b1.err != nil { b.err = b1.err return nil } case formStrx, formStrx1, formStrx2, formStrx3, formStrx4: var off uint64 switch fmt { case formStrx: off = b.uint() case formStrx1: off = uint64(b.uint8()) case formStrx2: off = uint64(b.uint16()) case formStrx3: off = uint64(b.uint24()) case formStrx4: off = uint64(b.uint32()) } if len(b.dwarf.strOffsets) == 0 { b.error("DW_FORM_strx with no .debug_str_offsets section") } is64, known := b.format.dwarf64() if !known { b.error("unknown offset size for DW_FORM_strx") } if b.err != nil { return nil } if is64 { off *= 8 } else { off *= 4 } // We have to adjust by the offset of the // compilation unit. This won't work if the // program uses Reader.Seek to skip over the // unit. Not much we can do about that. var strBase int64 if cu != nil { strBase, _ = cu.Val(AttrStrOffsetsBase).(int64) } else if a.tag == TagCompileUnit { delay = append(delay, delayed{i, off, formStrx}) break } val = resolveStrx(uint64(strBase), off) case formStrpSup: is64, known := b.format.dwarf64() if !known { b.error("unknown size for DW_FORM_strp_sup") } else if is64 { val = b.uint64() } else { val = b.uint32() } // lineptr, loclistptr, macptr, rangelistptr // New in DWARF 4, but clang can generate them with -gdwarf-2. // Section reference, replacing use of formData4 and formData8. case formSecOffset, formGnuRefAlt, formGnuStrpAlt: is64, known := b.format.dwarf64() if !known { b.error("unknown size for form 0x" + strconv.FormatInt(int64(fmt), 16)) } else if is64 { val = int64(b.uint64()) } else { val = int64(b.uint32()) } // exprloc // New in DWARF 4. case formExprloc: val = b.bytes(int(b.uint())) // reference // New in DWARF 4. case formRefSig8: // 64-bit type signature. val = b.uint64() case formRefSup4: val = b.uint32() case formRefSup8: val = b.uint64() // loclist case formLoclistx: val = b.uint() // rnglist case formRnglistx: off := b.uint() // We have to adjust by the rnglists_base of // the compilation unit. This won't work if // the program uses Reader.Seek to skip over // the unit. Not much we can do about that. var rnglistsBase int64 if cu != nil { rnglistsBase, _ = cu.Val(AttrRnglistsBase).(int64) } else if a.tag == TagCompileUnit { delay = append(delay, delayed{i, off, formRnglistx}) break } val = resolveRnglistx(uint64(rnglistsBase), off) } e.Field[i].Val = val } if b.err != nil { return nil } for _, del := range delay { switch del.fmt { case formAddrx: addrBase, _ := e.Val(AttrAddrBase).(int64) val, err := b.dwarf.debugAddr(b.format, uint64(addrBase), del.off) if err != nil { b.err = err return nil } e.Field[del.idx].Val = val case formStrx: strBase, _ := e.Val(AttrStrOffsetsBase).(int64) e.Field[del.idx].Val = resolveStrx(uint64(strBase), del.off) if b.err != nil { return nil } case formRnglistx: rnglistsBase, _ := e.Val(AttrRnglistsBase).(int64) e.Field[del.idx].Val = resolveRnglistx(uint64(rnglistsBase), del.off) if b.err != nil { return nil } } } return e } // 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 error unit int lastUnit bool // set if last entry returned by Next is TagCompileUnit/TagPartialUnit lastChildren bool // .Children of last entry returned by Next lastSibling Offset // .Val(AttrSibling) of last entry returned by Next cu *Entry // current compilation unit } // Reader returns a new Reader for Data. // The reader is positioned at byte offset 0 in the DWARF “info” section. func (d *Data) Reader() *Reader { r := &Reader{d: d} r.Seek(0) return r } // AddressSize returns the size in bytes of addresses in the current compilation // unit. func (r *Reader) AddressSize() int { return r.d.unit[r.unit].asize } // ByteOrder returns the byte order in the current compilation unit. func (r *Reader) ByteOrder() binary.ByteOrder { return r.b.order } // Seek positions the Reader at offset off in the encoded entry stream. // Offset 0 can be used to denote the first entry. func (r *Reader) Seek(off Offset) { d := r.d r.err = nil r.lastChildren = false if off == 0 { if len(d.unit) == 0 { return } u := &d.unit[0] r.unit = 0 r.b = makeBuf(r.d, u, "info", u.off, u.data) r.cu = nil return } i := d.offsetToUnit(off) if i == -1 { r.err = errors.New("offset out of range") return } if i != r.unit { r.cu = nil } u := &d.unit[i] r.unit = i r.b = makeBuf(r.d, u, "info", off, u.data[off-u.off:]) } // maybeNextUnit advances to the next unit if this one is finished. func (r *Reader) maybeNextUnit() { for len(r.b.data) == 0 && r.unit+1 < len(r.d.unit) { r.nextUnit() } } // nextUnit advances to the next unit. func (r *Reader) nextUnit() { r.unit++ u := &r.d.unit[r.unit] r.b = makeBuf(r.d, u, "info", u.off, u.data) r.cu = nil } // Next reads the next entry from the encoded entry stream. // It returns nil, nil when it reaches the end of the section. // It returns an error if the current offset is invalid or the data at the // offset cannot be decoded as a valid Entry. func (r *Reader) Next() (*Entry, error) { if r.err != nil { return nil, r.err } r.maybeNextUnit() if len(r.b.data) == 0 { return nil, nil } u := &r.d.unit[r.unit] e := r.b.entry(r.cu, u.atable, u.base, u.vers) if r.b.err != nil { r.err = r.b.err return nil, r.err } r.lastUnit = false if e != nil { r.lastChildren = e.Children if r.lastChildren { r.lastSibling, _ = e.Val(AttrSibling).(Offset) } if e.Tag == TagCompileUnit || e.Tag == TagPartialUnit { r.lastUnit = true r.cu = e } } 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 } if r.lastUnit && r.unit+1 < len(r.d.unit) { r.nextUnit() return } for { e, err := r.Next() if err != nil || e == nil || e.Tag == 0 { break } if e.Children { r.SkipChildren() } } } // clone returns a copy of the reader. This is used by the typeReader // interface. func (r *Reader) clone() typeReader { return r.d.Reader() } // offset returns the current buffer offset. This is used by the // typeReader interface. func (r *Reader) offset() Offset { return r.b.off } // SeekPC returns the Entry for the compilation unit that includes pc, // and positions the reader to read the children of that unit. If pc // is not covered by any unit, SeekPC returns ErrUnknownPC and the // position of the reader is undefined. // // Because compilation units can describe multiple regions of the // executable, in the worst case SeekPC must search through all the // ranges in all the compilation units. Each call to SeekPC starts the // search at the compilation unit of the last call, so in general // looking up a series of PCs will be faster if they are sorted. If // the caller wishes to do repeated fast PC lookups, it should build // an appropriate index using the Ranges method. func (r *Reader) SeekPC(pc uint64) (*Entry, error) { unit := r.unit for i := 0; i < len(r.d.unit); i++ { if unit >= len(r.d.unit) { unit = 0 } r.err = nil r.lastChildren = false r.unit = unit r.cu = nil u := &r.d.unit[unit] r.b = makeBuf(r.d, u, "info", u.off, u.data) e, err := r.Next() if err != nil || e == nil || e.Tag == 0 { return nil, err } ranges, err := r.d.Ranges(e) if err != nil { return nil, err } for _, pcs := range ranges { if pcs[0] <= pc && pc < pcs[1] { return e, nil } } unit++ } return nil, ErrUnknownPC } // Ranges returns the PC ranges covered by e, a slice of [low,high) pairs. // Only some entry types, such as TagCompileUnit or TagSubprogram, have PC // ranges; for others, this will return nil with no error. func (d *Data) Ranges(e *Entry) ([][2]uint64, error) { var ret [][2]uint64 low, lowOK := e.Val(AttrLowpc).(uint64) var high uint64 var highOK bool highField := e.AttrField(AttrHighpc) if highField != nil { switch highField.Class { case ClassAddress: high, highOK = highField.Val.(uint64) case ClassConstant: off, ok := highField.Val.(int64) if ok { high = low + uint64(off) highOK = true } } } if lowOK && highOK { ret = append(ret, [2]uint64{low, high}) } var u *unit if uidx := d.offsetToUnit(e.Offset); uidx >= 0 && uidx < len(d.unit) { u = &d.unit[uidx] } if u != nil && u.vers >= 5 && d.rngLists != nil { // DWARF version 5 and later field := e.AttrField(AttrRanges) if field == nil { return ret, nil } switch field.Class { case ClassRangeListPtr: ranges, rangesOK := field.Val.(int64) if !rangesOK { return ret, nil } cu, base, err := d.baseAddressForEntry(e) if err != nil { return nil, err } return d.dwarf5Ranges(u, cu, base, ranges, ret) case ClassRngList: rnglist, ok := field.Val.(uint64) if !ok { return ret, nil } cu, base, err := d.baseAddressForEntry(e) if err != nil { return nil, err } return d.dwarf5Ranges(u, cu, base, int64(rnglist), ret) default: return ret, nil } } // DWARF version 2 through 4 ranges, rangesOK := e.Val(AttrRanges).(int64) if rangesOK && d.ranges != nil { _, base, err := d.baseAddressForEntry(e) if err != nil { return nil, err } return d.dwarf2Ranges(u, base, ranges, ret) } return ret, nil } // baseAddressForEntry returns the initial base address to be used when // looking up the range list of entry e. // DWARF specifies that this should be the lowpc attribute of the enclosing // compilation unit, however comments in gdb/dwarf2read.c say that some // versions of GCC use the entrypc attribute, so we check that too. func (d *Data) baseAddressForEntry(e *Entry) (*Entry, uint64, error) { var cu *Entry if e.Tag == TagCompileUnit { cu = e } else { i := d.offsetToUnit(e.Offset) if i == -1 { return nil, 0, errors.New("no unit for entry") } u := &d.unit[i] b := makeBuf(d, u, "info", u.off, u.data) cu = b.entry(nil, u.atable, u.base, u.vers) if b.err != nil { return nil, 0, b.err } } if cuEntry, cuEntryOK := cu.Val(AttrEntrypc).(uint64); cuEntryOK { return cu, cuEntry, nil } else if cuLow, cuLowOK := cu.Val(AttrLowpc).(uint64); cuLowOK { return cu, cuLow, nil } return cu, 0, nil } func (d *Data) dwarf2Ranges(u *unit, base uint64, ranges int64, ret [][2]uint64) ([][2]uint64, error) { if ranges < 0 || ranges > int64(len(d.ranges)) { return nil, fmt.Errorf("invalid range offset %d (max %d)", ranges, len(d.ranges)) } buf := makeBuf(d, u, "ranges", Offset(ranges), d.ranges[ranges:]) for len(buf.data) > 0 { low := buf.addr() high := buf.addr() if low == 0 && high == 0 { break } if low == ^uint64(0)>>uint((8-u.addrsize())*8) { base = high } else { ret = append(ret, [2]uint64{base + low, base + high}) } } return ret, nil } // dwarf5Ranges interprets a debug_rnglists sequence, see DWARFv5 section // 2.17.3 (page 53). func (d *Data) dwarf5Ranges(u *unit, cu *Entry, base uint64, ranges int64, ret [][2]uint64) ([][2]uint64, error) { if ranges < 0 || ranges > int64(len(d.rngLists)) { return nil, fmt.Errorf("invalid rnglist offset %d (max %d)", ranges, len(d.ranges)) } var addrBase int64 if cu != nil { addrBase, _ = cu.Val(AttrAddrBase).(int64) } buf := makeBuf(d, u, "rnglists", 0, d.rngLists) buf.skip(int(ranges)) for { opcode := buf.uint8() switch opcode { case rleEndOfList: if buf.err != nil { return nil, buf.err } return ret, nil case rleBaseAddressx: baseIdx := buf.uint() var err error base, err = d.debugAddr(u, uint64(addrBase), baseIdx) if err != nil { return nil, err } case rleStartxEndx: startIdx := buf.uint() endIdx := buf.uint() start, err := d.debugAddr(u, uint64(addrBase), startIdx) if err != nil { return nil, err } end, err := d.debugAddr(u, uint64(addrBase), endIdx) if err != nil { return nil, err } ret = append(ret, [2]uint64{start, end}) case rleStartxLength: startIdx := buf.uint() len := buf.uint() start, err := d.debugAddr(u, uint64(addrBase), startIdx) if err != nil { return nil, err } ret = append(ret, [2]uint64{start, start + len}) case rleOffsetPair: off1 := buf.uint() off2 := buf.uint() ret = append(ret, [2]uint64{base + off1, base + off2}) case rleBaseAddress: base = buf.addr() case rleStartEnd: start := buf.addr() end := buf.addr() ret = append(ret, [2]uint64{start, end}) case rleStartLength: start := buf.addr() len := buf.uint() ret = append(ret, [2]uint64{start, start + len}) } } } // debugAddr returns the address at idx in debug_addr func (d *Data) debugAddr(format dataFormat, addrBase, idx uint64) (uint64, error) { off := idx*uint64(format.addrsize()) + addrBase if uint64(int(off)) != off { return 0, errors.New("offset out of range") } b := makeBuf(d, format, "addr", 0, d.addr) b.skip(int(off)) val := b.addr() if b.err != nil { return 0, b.err } return val, nil }