diff --git a/pkg/dwarf/dwarfbuilder/info.go b/pkg/dwarf/dwarfbuilder/info.go index 58cb4160..32880467 100644 --- a/pkg/dwarf/dwarfbuilder/info.go +++ b/pkg/dwarf/dwarfbuilder/info.go @@ -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 diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go index 16d058c1..9bc0752b 100644 --- a/pkg/proc/bininfo.go +++ b/pkg/proc/bininfo.go @@ -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. diff --git a/pkg/proc/dwarf_expr_test.go b/pkg/proc/dwarf_expr_test.go index abcf8de1..b2f11bed 100644 --- a/pkg/proc/dwarf_expr_test.go +++ b/pkg/proc/dwarf_expr_test.go @@ -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") + } + +} diff --git a/pkg/proc/types.go b/pkg/proc/types.go index 63b304fc..b560d29e 100644 --- a/pkg/proc/types.go +++ b/pkg/proc/types.go @@ -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