proc,vendor: show global variables in disassembly

updates vendored version of x86asm, adds a symbol lookup function to
pass to the disassembler.

This will show global symbol names in the disassembly like go tool
objdump does.
This commit is contained in:
aarzilli
2017-10-26 13:37:19 +02:00
committed by Derek Parker
parent 84ce278352
commit ec8dc3a10d
30 changed files with 4571 additions and 35 deletions

View File

@ -38,10 +38,10 @@ type BinaryInfo struct {
loclist loclistReader
compileUnits []*compileUnit
types map[string]dwarf.Offset
packageVars map[string]dwarf.Offset
packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
gStructOffset uint64
// Functions is a list of all DW_TAG_subprogram entries in debug_info.
// Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point
Functions []Function
// Sources is a list of all source files found in debug_line.
Sources []string
@ -149,6 +149,15 @@ type constantValue struct {
singleBit bool
}
// packageVar represents a package-level variable (or a C global variable).
// If a global variable does not have an address (for example it's stored in
// a register, or non-contiguously) addr will be 0.
type packageVar struct {
name string
offset dwarf.Offset
addr uint64
}
type loclistReader struct {
data []byte
cur int

View File

@ -1,5 +1,7 @@
package proc
import "sort"
type AsmInstruction struct {
Loc Location
DestLoc *Location
@ -14,6 +16,7 @@ type AssemblyFlavour int
const (
GNUFlavour = AssemblyFlavour(iota)
IntelFlavour
GoFlavour
)
// Disassemble disassembles target memory between startPC and endPC, marking
@ -83,3 +86,31 @@ func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *Breakpoint
}
return r, nil
}
// Looks up symbol (either functions or global variables) at address addr.
// Used by disassembly formatter.
func (bi *BinaryInfo) symLookup(addr uint64) (string, uint64) {
fn := bi.PCToFunc(addr)
if fn != nil {
if fn.Entry == addr {
// only report the function name if it's the exact address because it's
// easier to read the absolute address than function_name+offset.
return fn.Name, fn.Entry
}
return "", 0
}
i := sort.Search(len(bi.packageVars), func(i int) bool {
return bi.packageVars[i].addr >= addr
})
if i >= len(bi.packageVars) {
return "", 0
}
if bi.packageVars[i].addr > addr {
// report previous variable + offset if i-th variable starts after addr
i--
}
if i > 0 {
return bi.packageVars[i].name, bi.packageVars[i].addr
}
return "", 0
}

View File

@ -34,7 +34,7 @@ func patchPCRel(pc uint64, inst *x86asm.Inst) {
}
}
func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
func (inst *AsmInstruction) Text(flavour AssemblyFlavour, bi *BinaryInfo) string {
if inst.Inst == nil {
return "?"
}
@ -43,15 +43,13 @@ func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
switch flavour {
case GNUFlavour:
text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst))
text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
case GoFlavour:
text = x86asm.GoSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
case IntelFlavour:
fallthrough
default:
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst))
}
if inst.IsCall() && inst.DestLoc != nil && inst.DestLoc.Fn != nil {
text += " " + inst.DestLoc.Fn.Name
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst), inst.Loc.PC, bi.symLookup)
}
return text

View File

@ -3455,3 +3455,21 @@ func TestIssue1145(t *testing.T) {
}
})
}
func TestDisassembleGlobalVars(t *testing.T) {
withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) {
mainfn := p.BinInfo().LookupFunc["main.main"]
text, err := proc.Disassemble(p, nil, mainfn.Entry, mainfn.End)
assertNoError(err, t, "Disassemble")
found := false
for i := range text {
if strings.Index(text[i].Text(proc.IntelFlavour, p.BinInfo()), "main.v") > 0 {
found = true
break
}
}
if !found {
t.Fatalf("could not find main.v reference in disassembly")
}
})
}

View File

@ -3,6 +3,7 @@ package proc
import (
"bytes"
"debug/dwarf"
"encoding/binary"
"errors"
"fmt"
"go/ast"
@ -18,6 +19,7 @@ import (
"github.com/derekparker/delve/pkg/dwarf/godwarf"
"github.com/derekparker/delve/pkg/dwarf/line"
"github.com/derekparker/delve/pkg/dwarf/op"
"github.com/derekparker/delve/pkg/dwarf/reader"
)
@ -169,12 +171,18 @@ 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] }
type packageVarsByAddr []packageVar
func (v packageVarsByAddr) Len() int { return len(v) }
func (v packageVarsByAddr) Less(i int, j int) bool { return v[i].addr < v[j].addr }
func (v packageVarsByAddr) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGroup) {
if wg != nil {
defer wg.Done()
}
bi.types = make(map[string]dwarf.Offset)
bi.packageVars = make(map[string]dwarf.Offset)
bi.packageVars = []packageVar{}
bi.Functions = []Function{}
bi.compileUnits = []*compileUnit{}
bi.consts = make(map[dwarf.Offset]*constantType)
@ -226,7 +234,13 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
if !cu.isgo {
n = "C." + n
}
bi.packageVars[n] = entry.Offset
var addr uint64
if loc, ok := entry.Val(dwarf.AttrLocation).([]byte); ok {
if len(loc) == bi.Arch.PtrSize()+1 && op.Opcode(loc[0]) == op.DW_OP_addr {
addr = binary.LittleEndian.Uint64(loc[1:])
}
}
bi.packageVars = append(bi.packageVars, packageVar{n, entry.Offset, addr})
}
case dwarf.TagConstant:
@ -271,6 +285,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
}
sort.Sort(compileUnitsByLowpc(bi.compileUnits))
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
sort.Sort(packageVarsByAddr(bi.packageVars))
bi.LookupFunc = make(map[string]*Function)
for i := range bi.Functions {

View File

@ -653,10 +653,10 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
}
func (scope *EvalScope) findGlobal(name string) (*Variable, error) {
for n, off := range scope.BinInfo.packageVars {
if n == name || strings.HasSuffix(n, "/"+name) {
for _, pkgvar := range scope.BinInfo.packageVars {
if pkgvar.name == name || strings.HasSuffix(pkgvar.name, "/"+name) {
reader := scope.DwarfReader()
reader.Seek(off)
reader.Seek(pkgvar.offset)
entry, err := reader.Next()
if err != nil {
return nil, err