pkg/proc: Refactor Disassemble

This commit is contained in:
Derek Parker 2019-08-08 11:54:56 -07:00 committed by Alessandro Arzilli
parent 583d335ffe
commit 9963458d77
7 changed files with 67 additions and 72 deletions

@ -24,39 +24,24 @@ const (
GoFlavour
)
// Disassemble disassembles target memory between startPC and endPC, marking
// Disassemble disassembles target memory between startAddr and endAddr, marking
// the current instruction being executed in goroutine g.
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
func Disassemble(dbp Process, g *G, startPC, endPC uint64) ([]AsmInstruction, error) {
if _, err := dbp.Valid(); err != nil {
return nil, err
}
if g == nil {
ct := dbp.CurrentThread()
regs, _ := ct.Registers(false)
return disassemble(ct, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC, false)
}
var regs Registers
var mem MemoryReadWriter = dbp.CurrentThread()
if g.Thread != nil {
mem = g.Thread
regs, _ = g.Thread.Registers(false)
}
return disassemble(mem, regs, dbp.Breakpoints(), dbp.BinInfo(), startPC, endPC, false)
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble
// will evaluate the argument of the CALL instruction using the thread's registers.
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size startAddr - endAddr.
func Disassemble(mem MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startAddr, endAddr uint64) ([]AsmInstruction, error) {
return disassemble(mem, regs, breakpoints, bi, startAddr, endAddr, false)
}
func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startPC, endPC uint64, singleInstr bool) ([]AsmInstruction, error) {
mem := make([]byte, int(endPC-startPC))
_, err := memrw.ReadMemory(mem, uintptr(startPC))
func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startAddr, endAddr uint64, singleInstr bool) ([]AsmInstruction, error) {
mem := make([]byte, int(endAddr-startAddr))
_, err := memrw.ReadMemory(mem, uintptr(startAddr))
if err != nil {
return nil, err
}
r := make([]AsmInstruction, 0, len(mem)/15)
pc := startPC
pc := startAddr
var curpc uint64
if regs != nil {

@ -115,33 +115,6 @@ func FindFunctionLocation(p Process, funcName string, lineOffset int) (uint64, e
return breakAddr, err
}
// FunctionReturnLocations will return a list of addresses corresponding
// to 'ret' or 'call runtime.deferreturn'.
func FunctionReturnLocations(p Process, funcName string) ([]uint64, error) {
const deferReturn = "runtime.deferreturn"
g := p.SelectedGoroutine()
fn, ok := p.BinInfo().LookupFunc[funcName]
if !ok {
return nil, fmt.Errorf("unable to find function %s", funcName)
}
instructions, err := Disassemble(p, g, fn.Entry, fn.End)
if err != nil {
return nil, err
}
var addrs []uint64
for _, instruction := range instructions {
if instruction.IsRet() {
addrs = append(addrs, instruction.Loc.PC)
}
}
addrs = append(addrs, findDeferReturnCalls(instructions)...)
return addrs, nil
}
// Next continues execution until the next source line.
func Next(dbp Process) (err error) {
if _, err := dbp.Valid(); err != nil {

@ -2545,7 +2545,7 @@ func TestStepOnCallPtrInstr(t *testing.T) {
regs, err := p.CurrentThread().Registers(false)
assertNoError(err, t, "Registers()")
pc := regs.PC()
text, err := proc.Disassemble(p, nil, pc, pc+maxInstructionLength)
text, err := proc.Disassemble(p.CurrentThread(), regs, p.Breakpoints(), p.BinInfo(), pc, pc+maxInstructionLength)
assertNoError(err, t, "Disassemble()")
if text[0].IsCall() {
found = true
@ -3629,7 +3629,8 @@ 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)
regs, _ := p.CurrentThread().Registers(false)
text, err := proc.Disassemble(p.CurrentThread(), regs, p.Breakpoints(), p.BinInfo(), mainfn.Entry, mainfn.End)
assertNoError(err, t, "Disassemble")
found := false
for i := range text {

@ -225,7 +225,7 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
}
if !csource {
deferreturns := findDeferReturnCalls(text)
deferreturns := FindDeferReturnCalls(text)
// Set breakpoint on the most recently deferred function (if any)
var deferpc uint64
@ -325,7 +325,7 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
return nil
}
func findDeferReturnCalls(text []AsmInstruction) []uint64 {
func FindDeferReturnCalls(text []AsmInstruction) []uint64 {
const deferreturn = "runtime.deferreturn"
deferreturns := []uint64{}

@ -218,12 +218,42 @@ func (d *Debugger) LastModified() time.Time {
return d.target.BinInfo().LastModified()
}
const deferReturn = "runtime.deferreturn"
// FunctionReturnLocations returns all return locations
// for the given function. See the documentation for the
// function of the same name within the `proc` package for
// more information.
// for the given function, a list of addresses corresponding
// to 'ret' or 'call runtime.deferreturn'.
func (d *Debugger) FunctionReturnLocations(fnName string) ([]uint64, error) {
return proc.FunctionReturnLocations(d.target, fnName)
var (
p = d.target
g = p.SelectedGoroutine()
)
fn, ok := p.BinInfo().LookupFunc[fnName]
if !ok {
return nil, fmt.Errorf("unable to find function %s", fnName)
}
var regs proc.Registers
var mem proc.MemoryReadWriter = p.CurrentThread()
if g.Thread != nil {
mem = g.Thread
regs, _ = g.Thread.Registers(false)
}
instructions, err := proc.Disassemble(mem, regs, p.Breakpoints(), p.BinInfo(), fn.Entry, fn.End)
if err != nil {
return nil, err
}
var addrs []uint64
for _, instruction := range instructions {
if instruction.IsRet() {
addrs = append(addrs, instruction.Loc.PC)
}
}
addrs = append(addrs, proc.FindDeferReturnCalls(instructions)...)
return addrs, nil
}
// Detach detaches from the target process.
@ -1121,9 +1151,9 @@ func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Locat
return locs, err
}
// Disassemble code between startPC and endPC
// if endPC == 0 it will find the function containing startPC and disassemble the whole function
func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
// Disassemble code between startPC and endPC.
// if endPC == 0 it will find the function containing startPC and disassemble the whole function.
func (d *Debugger) Disassemble(goroutineID int, addr1, addr2 uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
d.processMutex.Lock()
defer d.processMutex.Unlock()
@ -1131,21 +1161,27 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo
return nil, err
}
if endPC == 0 {
_, _, fn := d.target.BinInfo().PCToLine(startPC)
if addr2 == 0 {
_, _, fn := d.target.BinInfo().PCToLine(addr1)
if fn == nil {
return nil, fmt.Errorf("Address 0x%x does not belong to any function", startPC)
return nil, fmt.Errorf("address %#x does not belong to any function", addr1)
}
startPC = fn.Entry
endPC = fn.End
addr1 = fn.Entry
addr2 = fn.End
}
g, err := proc.FindGoroutine(d.target, scope.GoroutineID)
g, err := proc.FindGoroutine(d.target, goroutineID)
if err != nil {
return nil, err
}
insts, err := proc.Disassemble(d.target, g, startPC, endPC)
var regs proc.Registers
var mem proc.MemoryReadWriter = d.target.CurrentThread()
if g.Thread != nil {
mem = g.Thread
regs, _ = g.Thread.Registers(false)
}
insts, err := proc.Disassemble(mem, regs, d.target.Breakpoints(), d.target.BinInfo(), addr1, addr2)
if err != nil {
return nil, err
}

@ -317,6 +317,6 @@ type DisassembleRequest struct {
func (c *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error {
var err error
*answer, err = c.debugger.Disassemble(args.Scope, args.StartPC, args.EndPC, args.Flavour)
*answer, err = c.debugger.Disassemble(args.Scope.GoroutineID, args.StartPC, args.EndPC, args.Flavour)
return err
}

@ -607,7 +607,7 @@ type DisassembleOut struct {
// Disassemble will also try to calculate the destination address of an absolute indirect CALL if it happens to be the instruction the selected goroutine is stopped at.
func (c *RPCServer) Disassemble(arg DisassembleIn, out *DisassembleOut) error {
var err error
out.Disassemble, err = c.debugger.Disassemble(arg.Scope, arg.StartPC, arg.EndPC, arg.Flavour)
out.Disassemble, err = c.debugger.Disassemble(arg.Scope.GoroutineID, arg.StartPC, arg.EndPC, arg.Flavour)
return err
}