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:
|
||||
tag.form = append(tag.form, DW_FORM_data2)
|
||||
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:
|
||||
tag.form = append(tag.form, DW_FORM_addr)
|
||||
binary.Write(&b.info, binary.LittleEndian, x)
|
||||
@ -232,6 +235,12 @@ func (b *Builder) makeAbbrevTable() []byte {
|
||||
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
|
||||
// TagClose after adding all local variables and parameters.
|
||||
// Will write an abbrev corresponding to a DW_TAG_subprogram, followed by a
|
||||
|
@ -61,9 +61,11 @@ type BinaryInfo struct {
|
||||
packageMap map[string]string
|
||||
|
||||
frameEntries frame.FrameDescriptionEntries
|
||||
compileUnits []*compileUnit
|
||||
types map[string]dwarfRef
|
||||
packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
|
||||
|
||||
compileUnits []*compileUnit // compileUnits is sorted by increasing DWARF offset
|
||||
|
||||
types map[string]dwarfRef
|
||||
packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
|
||||
|
||||
gStructOffset uint64
|
||||
|
||||
@ -112,7 +114,7 @@ type compileUnit struct {
|
||||
optimized bool // this compile unit is optimized
|
||||
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.
|
||||
}
|
||||
@ -608,6 +610,45 @@ func (bi *BinaryInfo) locationExpr(entry reader.Entry, attr dwarf.Attr, pc uint6
|
||||
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.
|
||||
// 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
|
||||
@ -662,12 +703,13 @@ func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
|
||||
for _, cu := range bi.compileUnits {
|
||||
if off >= cu.startOffset && off < cu.endOffset {
|
||||
return cu
|
||||
}
|
||||
i := sort.Search(len(bi.compileUnits), func(i int) bool {
|
||||
return bi.compileUnits[i].offset >= off
|
||||
})
|
||||
if i > 0 {
|
||||
i--
|
||||
}
|
||||
return nil
|
||||
return bi.compileUnits[i]
|
||||
}
|
||||
|
||||
// Producer returns the value of DW_AT_producer.
|
||||
|
@ -21,7 +21,7 @@ import (
|
||||
|
||||
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()
|
||||
assertNoError(err, t, "dwarfbuilder.Build")
|
||||
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.LoadImageFromData(dwdata, frame, line, loc)
|
||||
|
||||
return bi
|
||||
return bi, dwdata
|
||||
}
|
||||
|
||||
// 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.TagClose()
|
||||
|
||||
bi := fakeBinaryInfo(t, dwb)
|
||||
bi, _ := fakeBinaryInfo(t, dwb)
|
||||
|
||||
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.TagClose()
|
||||
|
||||
bi := fakeBinaryInfo(t, dwb)
|
||||
bi, _ := fakeBinaryInfo(t, dwb)
|
||||
|
||||
mainfn := bi.LookupFunc["main.main"]
|
||||
|
||||
@ -206,7 +206,7 @@ func TestDwarfExprLoclist(t *testing.T) {
|
||||
})
|
||||
dwb.TagClose()
|
||||
|
||||
bi := fakeBinaryInfo(t, dwb)
|
||||
bi, _ := fakeBinaryInfo(t, dwb)
|
||||
|
||||
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.TagClose()
|
||||
|
||||
bi := fakeBinaryInfo(t, dwb)
|
||||
bi, _ := fakeBinaryInfo(t, dwb)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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) 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 compileUnitsByLowpc) Less(i int, j int) bool { return v[i].lowPC < v[j].lowPC }
|
||||
func (v compileUnitsByLowpc) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
|
||||
func (v compileUnitsByOffset) Len() int { return len(v) }
|
||||
func (v compileUnitsByOffset) Less(i int, j int) bool { return v[i].offset < v[j].offset }
|
||||
func (v compileUnitsByOffset) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
|
||||
|
||||
type packageVarsByAddr []packageVar
|
||||
|
||||
@ -223,7 +223,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
|
||||
cu := &compileUnit{}
|
||||
cu.image = image
|
||||
cu.entry = entry
|
||||
cu.startOffset = entry.Offset
|
||||
cu.offset = entry.Offset
|
||||
if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
|
||||
cu.isgo = true
|
||||
}
|
||||
@ -264,7 +264,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
|
||||
}
|
||||
bi.compileUnits = append(bi.compileUnits, cu)
|
||||
if entry.Children {
|
||||
cu.endOffset = bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu)
|
||||
bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu)
|
||||
}
|
||||
|
||||
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(packageVarsByAddr(bi.packageVars))
|
||||
|
||||
@ -302,19 +302,15 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
|
||||
}
|
||||
|
||||
// loadDebugInfoMapsCompileUnit loads entry from a single compile unit.
|
||||
func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContext, image *Image, reader *reader.Reader, cu *compileUnit) dwarf.Offset {
|
||||
var lastOffset dwarf.Offset
|
||||
func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContext, image *Image, reader *reader.Reader, cu *compileUnit) {
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
image.setLoadError("error reading debug_info: %v", err)
|
||||
return lastOffset + 1
|
||||
}
|
||||
if entry.Tag != 0 {
|
||||
lastOffset = entry.Offset
|
||||
return
|
||||
}
|
||||
switch entry.Tag {
|
||||
case 0:
|
||||
return lastOffset + 1
|
||||
return
|
||||
case dwarf.TagImportedUnit:
|
||||
bi.loadDebugInfoMapsImportedUnit(entry, ctxt, image, cu)
|
||||
reader.SkipChildren()
|
||||
@ -409,7 +405,7 @@ func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContex
|
||||
entry, err = reader.Next()
|
||||
if err != nil {
|
||||
image.setLoadError("error reading debug_info: %v", err)
|
||||
return 0
|
||||
return
|
||||
}
|
||||
if entry.Tag == 0 {
|
||||
break
|
||||
@ -441,8 +437,6 @@ func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContex
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return lastOffset + 1
|
||||
}
|
||||
|
||||
// loadDebugInfoMapsImportedUnit loads entries into cu from the partial unit
|
||||
|
Loading…
Reference in New Issue
Block a user