proc: add LocationCover method to BinaryInfo (#1573)
Also fixes findCompileUnitForOffset which was broken in some edge cases (when looking up an offset inside the last child of the compilation unit) which don't happen in normal executables (we only look up types, and those are always direct childs of compile units).
This commit is contained in:
parent
79ad269bbb
commit
a7c2d837d5
@ -143,6 +143,9 @@ func (b *Builder) Attr(attr dwarf.Attr, val interface{}) {
|
|||||||
case uint16:
|
case uint16:
|
||||||
tag.form = append(tag.form, DW_FORM_data2)
|
tag.form = append(tag.form, DW_FORM_data2)
|
||||||
binary.Write(&b.info, binary.LittleEndian, x)
|
binary.Write(&b.info, binary.LittleEndian, x)
|
||||||
|
case uint64:
|
||||||
|
tag.form = append(tag.form, DW_FORM_data8)
|
||||||
|
binary.Write(&b.info, binary.LittleEndian, x)
|
||||||
case Address:
|
case Address:
|
||||||
tag.form = append(tag.form, DW_FORM_addr)
|
tag.form = append(tag.form, DW_FORM_addr)
|
||||||
binary.Write(&b.info, binary.LittleEndian, x)
|
binary.Write(&b.info, binary.LittleEndian, x)
|
||||||
@ -232,6 +235,12 @@ func (b *Builder) makeAbbrevTable() []byte {
|
|||||||
return abbrev.Bytes()
|
return abbrev.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) AddCompileUnit(name string, lowPC uint64) dwarf.Offset {
|
||||||
|
r := b.TagOpen(dwarf.TagCompileUnit, name)
|
||||||
|
b.Attr(dwarf.AttrLowpc, lowPC)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
// AddSubprogram adds a subprogram declaration to debug_info, must call
|
// AddSubprogram adds a subprogram declaration to debug_info, must call
|
||||||
// TagClose after adding all local variables and parameters.
|
// TagClose after adding all local variables and parameters.
|
||||||
// Will write an abbrev corresponding to a DW_TAG_subprogram, followed by a
|
// Will write an abbrev corresponding to a DW_TAG_subprogram, followed by a
|
||||||
|
@ -61,9 +61,11 @@ type BinaryInfo struct {
|
|||||||
packageMap map[string]string
|
packageMap map[string]string
|
||||||
|
|
||||||
frameEntries frame.FrameDescriptionEntries
|
frameEntries frame.FrameDescriptionEntries
|
||||||
compileUnits []*compileUnit
|
|
||||||
types map[string]dwarfRef
|
compileUnits []*compileUnit // compileUnits is sorted by increasing DWARF offset
|
||||||
packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
|
|
||||||
|
types map[string]dwarfRef
|
||||||
|
packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
|
||||||
|
|
||||||
gStructOffset uint64
|
gStructOffset uint64
|
||||||
|
|
||||||
@ -112,7 +114,7 @@ type compileUnit struct {
|
|||||||
optimized bool // this compile unit is optimized
|
optimized bool // this compile unit is optimized
|
||||||
producer string // producer attribute
|
producer string // producer attribute
|
||||||
|
|
||||||
startOffset, endOffset dwarf.Offset // interval of offsets contained in this compile unit
|
offset dwarf.Offset // offset of the entry describing the compile unit
|
||||||
|
|
||||||
image *Image // parent image of this compilation unit.
|
image *Image // parent image of this compilation unit.
|
||||||
}
|
}
|
||||||
@ -608,6 +610,45 @@ func (bi *BinaryInfo) locationExpr(entry reader.Entry, attr dwarf.Attr, pc uint6
|
|||||||
return instr, descr.String(), nil
|
return instr, descr.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LocationCovers returns the list of PC addresses that is covered by the
|
||||||
|
// location attribute 'attr' of entry 'entry'.
|
||||||
|
func (bi *BinaryInfo) LocationCovers(entry *dwarf.Entry, attr dwarf.Attr) ([][2]uint64, error) {
|
||||||
|
a := entry.Val(attr)
|
||||||
|
if a == nil {
|
||||||
|
return nil, fmt.Errorf("attribute %s not found", attr)
|
||||||
|
}
|
||||||
|
if _, isblock := a.([]byte); isblock {
|
||||||
|
return [][2]uint64{[2]uint64{0, ^uint64(0)}}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
off, ok := a.(int64)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("attribute %s of unsupported type %T", attr, a)
|
||||||
|
}
|
||||||
|
cu := bi.findCompileUnitForOffset(entry.Offset)
|
||||||
|
if cu == nil {
|
||||||
|
return nil, errors.New("could not find compile unit")
|
||||||
|
}
|
||||||
|
|
||||||
|
image := cu.image
|
||||||
|
base := cu.lowPC
|
||||||
|
if image == nil || image.loclist.data == nil {
|
||||||
|
return nil, errors.New("malformed executable")
|
||||||
|
}
|
||||||
|
|
||||||
|
r := [][2]uint64{}
|
||||||
|
image.loclist.Seek(int(off))
|
||||||
|
var e loclistEntry
|
||||||
|
for image.loclist.Next(&e) {
|
||||||
|
if e.BaseAddressSelection() {
|
||||||
|
base = e.highpc
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r = append(r, [2]uint64{e.lowpc + base, e.highpc + base})
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Location returns the location described by attribute attr of entry.
|
// Location returns the location described by attribute attr of entry.
|
||||||
// This will either be an int64 address or a slice of Pieces for locations
|
// This will either be an int64 address or a slice of Pieces for locations
|
||||||
// that don't correspond to a single memory address (registers, composite
|
// that don't correspond to a single memory address (registers, composite
|
||||||
@ -662,12 +703,13 @@ func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
|
func (bi *BinaryInfo) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
|
||||||
for _, cu := range bi.compileUnits {
|
i := sort.Search(len(bi.compileUnits), func(i int) bool {
|
||||||
if off >= cu.startOffset && off < cu.endOffset {
|
return bi.compileUnits[i].offset >= off
|
||||||
return cu
|
})
|
||||||
}
|
if i > 0 {
|
||||||
|
i--
|
||||||
}
|
}
|
||||||
return nil
|
return bi.compileUnits[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Producer returns the value of DW_AT_producer.
|
// Producer returns the value of DW_AT_producer.
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
|
|
||||||
const defaultCFA = 0xc420051d00
|
const defaultCFA = 0xc420051d00
|
||||||
|
|
||||||
func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) *proc.BinaryInfo {
|
func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) (*proc.BinaryInfo, *dwarf.Data) {
|
||||||
abbrev, aranges, frame, info, line, pubnames, ranges, str, loc, err := dwb.Build()
|
abbrev, aranges, frame, info, line, pubnames, ranges, str, loc, err := dwb.Build()
|
||||||
assertNoError(err, t, "dwarfbuilder.Build")
|
assertNoError(err, t, "dwarfbuilder.Build")
|
||||||
dwdata, err := dwarf.New(abbrev, aranges, frame, info, line, pubnames, ranges, str)
|
dwdata, err := dwarf.New(abbrev, aranges, frame, info, line, pubnames, ranges, str)
|
||||||
@ -30,7 +30,7 @@ func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) *proc.BinaryInfo {
|
|||||||
bi := proc.NewBinaryInfo("linux", "amd64")
|
bi := proc.NewBinaryInfo("linux", "amd64")
|
||||||
bi.LoadImageFromData(dwdata, frame, line, loc)
|
bi.LoadImageFromData(dwdata, frame, line, loc)
|
||||||
|
|
||||||
return bi
|
return bi, dwdata
|
||||||
}
|
}
|
||||||
|
|
||||||
// fakeMemory implements proc.MemoryReadWriter by reading from a byte slice.
|
// fakeMemory implements proc.MemoryReadWriter by reading from a byte slice.
|
||||||
@ -114,7 +114,7 @@ func TestDwarfExprRegisters(t *testing.T) {
|
|||||||
dwb.AddVariable("c", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_regx, int(1)))
|
dwb.AddVariable("c", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_regx, int(1)))
|
||||||
dwb.TagClose()
|
dwb.TagClose()
|
||||||
|
|
||||||
bi := fakeBinaryInfo(t, dwb)
|
bi, _ := fakeBinaryInfo(t, dwb)
|
||||||
|
|
||||||
mainfn := bi.LookupFunc["main.main"]
|
mainfn := bi.LookupFunc["main.main"]
|
||||||
|
|
||||||
@ -166,7 +166,7 @@ func TestDwarfExprComposite(t *testing.T) {
|
|||||||
dwb.AddVariable("n", intoff, dwarfbuilder.LocationBlock(op.DW_OP_reg3))
|
dwb.AddVariable("n", intoff, dwarfbuilder.LocationBlock(op.DW_OP_reg3))
|
||||||
dwb.TagClose()
|
dwb.TagClose()
|
||||||
|
|
||||||
bi := fakeBinaryInfo(t, dwb)
|
bi, _ := fakeBinaryInfo(t, dwb)
|
||||||
|
|
||||||
mainfn := bi.LookupFunc["main.main"]
|
mainfn := bi.LookupFunc["main.main"]
|
||||||
|
|
||||||
@ -206,7 +206,7 @@ func TestDwarfExprLoclist(t *testing.T) {
|
|||||||
})
|
})
|
||||||
dwb.TagClose()
|
dwb.TagClose()
|
||||||
|
|
||||||
bi := fakeBinaryInfo(t, dwb)
|
bi, _ := fakeBinaryInfo(t, dwb)
|
||||||
|
|
||||||
mainfn := bi.LookupFunc["main.main"]
|
mainfn := bi.LookupFunc["main.main"]
|
||||||
|
|
||||||
@ -241,7 +241,7 @@ func TestIssue1419(t *testing.T) {
|
|||||||
dwb.AddVariable("a", sliceoff, dwarfbuilder.LocationBlock(op.DW_OP_reg2, op.DW_OP_piece, uint(8), op.DW_OP_reg2, op.DW_OP_piece, uint(8), op.DW_OP_reg2, op.DW_OP_piece, uint(8)))
|
dwb.AddVariable("a", sliceoff, dwarfbuilder.LocationBlock(op.DW_OP_reg2, op.DW_OP_piece, uint(8), op.DW_OP_reg2, op.DW_OP_piece, uint(8), op.DW_OP_reg2, op.DW_OP_piece, uint(8)))
|
||||||
dwb.TagClose()
|
dwb.TagClose()
|
||||||
|
|
||||||
bi := fakeBinaryInfo(t, dwb)
|
bi, _ := fakeBinaryInfo(t, dwb)
|
||||||
|
|
||||||
mainfn := bi.LookupFunc["main.main"]
|
mainfn := bi.LookupFunc["main.main"]
|
||||||
|
|
||||||
@ -260,3 +260,35 @@ func TestIssue1419(t *testing.T) {
|
|||||||
t.Fatalf("wrong unreadable reason for variable 'a': %v", va.Unreadable)
|
t.Fatalf("wrong unreadable reason for variable 'a': %v", va.Unreadable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLocationCovers(t *testing.T) {
|
||||||
|
const before = 0x1234
|
||||||
|
const after = 0x4321
|
||||||
|
|
||||||
|
dwb := dwarfbuilder.New()
|
||||||
|
|
||||||
|
uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
|
||||||
|
|
||||||
|
dwb.AddCompileUnit("main", 0x0)
|
||||||
|
dwb.AddSubprogram("main.main", 0x40100, 0x41000)
|
||||||
|
aOff := dwb.AddVariable("a", uint16off, []dwarfbuilder.LocEntry{
|
||||||
|
{0x40100, 0x40700, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa)},
|
||||||
|
{0x40700, 0x41000, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa, op.DW_OP_consts, int(2), op.DW_OP_plus)},
|
||||||
|
})
|
||||||
|
dwb.TagClose()
|
||||||
|
dwb.TagClose()
|
||||||
|
|
||||||
|
bi, dwdata := fakeBinaryInfo(t, dwb)
|
||||||
|
|
||||||
|
dwrdr := dwdata.Reader()
|
||||||
|
dwrdr.Seek(aOff)
|
||||||
|
aEntry, err := dwrdr.Next()
|
||||||
|
assertNoError(err, t, "reading 'a' entry")
|
||||||
|
ranges, err := bi.LocationCovers(aEntry, dwarf.AttrLocation)
|
||||||
|
assertNoError(err, t, "LocationCovers")
|
||||||
|
t.Logf("%x", ranges)
|
||||||
|
if fmt.Sprintf("%x", ranges) != "[[40100 40700] [40700 41000]]" {
|
||||||
|
t.Error("wrong value returned by LocationCover")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -164,11 +164,11 @@ func (v functionsDebugInfoByEntry) Len() int { return len(v) }
|
|||||||
func (v functionsDebugInfoByEntry) Less(i, j int) bool { return v[i].Entry < v[j].Entry }
|
func (v functionsDebugInfoByEntry) Less(i, j int) bool { return v[i].Entry < v[j].Entry }
|
||||||
func (v functionsDebugInfoByEntry) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
|
func (v functionsDebugInfoByEntry) Swap(i, j int) { v[i], v[j] = v[j], v[i] }
|
||||||
|
|
||||||
type compileUnitsByLowpc []*compileUnit
|
type compileUnitsByOffset []*compileUnit
|
||||||
|
|
||||||
func (v compileUnitsByLowpc) Len() int { return len(v) }
|
func (v compileUnitsByOffset) Len() int { return len(v) }
|
||||||
func (v compileUnitsByLowpc) Less(i int, j int) bool { return v[i].lowPC < v[j].lowPC }
|
func (v compileUnitsByOffset) Less(i int, j int) bool { return v[i].offset < v[j].offset }
|
||||||
func (v compileUnitsByLowpc) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
|
func (v compileUnitsByOffset) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
|
||||||
|
|
||||||
type packageVarsByAddr []packageVar
|
type packageVarsByAddr []packageVar
|
||||||
|
|
||||||
@ -223,7 +223,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
|
|||||||
cu := &compileUnit{}
|
cu := &compileUnit{}
|
||||||
cu.image = image
|
cu.image = image
|
||||||
cu.entry = entry
|
cu.entry = entry
|
||||||
cu.startOffset = entry.Offset
|
cu.offset = entry.Offset
|
||||||
if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
|
if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
|
||||||
cu.isgo = true
|
cu.isgo = true
|
||||||
}
|
}
|
||||||
@ -264,7 +264,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
|
|||||||
}
|
}
|
||||||
bi.compileUnits = append(bi.compileUnits, cu)
|
bi.compileUnits = append(bi.compileUnits, cu)
|
||||||
if entry.Children {
|
if entry.Children {
|
||||||
cu.endOffset = bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu)
|
bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu)
|
||||||
}
|
}
|
||||||
|
|
||||||
case dwarf.TagPartialUnit:
|
case dwarf.TagPartialUnit:
|
||||||
@ -276,7 +276,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sort.Sort(compileUnitsByLowpc(bi.compileUnits))
|
sort.Sort(compileUnitsByOffset(bi.compileUnits))
|
||||||
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
|
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
|
||||||
sort.Sort(packageVarsByAddr(bi.packageVars))
|
sort.Sort(packageVarsByAddr(bi.packageVars))
|
||||||
|
|
||||||
@ -302,19 +302,15 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
|
|||||||
}
|
}
|
||||||
|
|
||||||
// loadDebugInfoMapsCompileUnit loads entry from a single compile unit.
|
// loadDebugInfoMapsCompileUnit loads entry from a single compile unit.
|
||||||
func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContext, image *Image, reader *reader.Reader, cu *compileUnit) dwarf.Offset {
|
func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContext, image *Image, reader *reader.Reader, cu *compileUnit) {
|
||||||
var lastOffset dwarf.Offset
|
|
||||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
image.setLoadError("error reading debug_info: %v", err)
|
image.setLoadError("error reading debug_info: %v", err)
|
||||||
return lastOffset + 1
|
return
|
||||||
}
|
|
||||||
if entry.Tag != 0 {
|
|
||||||
lastOffset = entry.Offset
|
|
||||||
}
|
}
|
||||||
switch entry.Tag {
|
switch entry.Tag {
|
||||||
case 0:
|
case 0:
|
||||||
return lastOffset + 1
|
return
|
||||||
case dwarf.TagImportedUnit:
|
case dwarf.TagImportedUnit:
|
||||||
bi.loadDebugInfoMapsImportedUnit(entry, ctxt, image, cu)
|
bi.loadDebugInfoMapsImportedUnit(entry, ctxt, image, cu)
|
||||||
reader.SkipChildren()
|
reader.SkipChildren()
|
||||||
@ -409,7 +405,7 @@ func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContex
|
|||||||
entry, err = reader.Next()
|
entry, err = reader.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
image.setLoadError("error reading debug_info: %v", err)
|
image.setLoadError("error reading debug_info: %v", err)
|
||||||
return 0
|
return
|
||||||
}
|
}
|
||||||
if entry.Tag == 0 {
|
if entry.Tag == 0 {
|
||||||
break
|
break
|
||||||
@ -441,8 +437,6 @@ func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContex
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return lastOffset + 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadDebugInfoMapsImportedUnit loads entries into cu from the partial unit
|
// loadDebugInfoMapsImportedUnit loads entries into cu from the partial unit
|
||||||
|
Loading…
Reference in New Issue
Block a user