pkg/proc: Refactor Disassemble
This commit is contained in:
parent
583d335ffe
commit
9963458d77
@ -24,39 +24,24 @@ const (
|
|||||||
GoFlavour
|
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.
|
// 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
|
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble
|
||||||
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
|
// will evaluate the argument of the CALL instruction using the thread's registers.
|
||||||
func Disassemble(dbp Process, g *G, startPC, endPC uint64) ([]AsmInstruction, error) {
|
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size startAddr - endAddr.
|
||||||
if _, err := dbp.Valid(); err != nil {
|
func Disassemble(mem MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startAddr, endAddr uint64) ([]AsmInstruction, error) {
|
||||||
return nil, err
|
return disassemble(mem, regs, breakpoints, bi, startAddr, endAddr, false)
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startPC, endPC uint64, singleInstr bool) ([]AsmInstruction, error) {
|
func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startAddr, endAddr uint64, singleInstr bool) ([]AsmInstruction, error) {
|
||||||
mem := make([]byte, int(endPC-startPC))
|
mem := make([]byte, int(endAddr-startAddr))
|
||||||
_, err := memrw.ReadMemory(mem, uintptr(startPC))
|
_, err := memrw.ReadMemory(mem, uintptr(startAddr))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
r := make([]AsmInstruction, 0, len(mem)/15)
|
r := make([]AsmInstruction, 0, len(mem)/15)
|
||||||
pc := startPC
|
pc := startAddr
|
||||||
|
|
||||||
var curpc uint64
|
var curpc uint64
|
||||||
if regs != nil {
|
if regs != nil {
|
||||||
|
@ -115,33 +115,6 @@ func FindFunctionLocation(p Process, funcName string, lineOffset int) (uint64, e
|
|||||||
return breakAddr, err
|
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.
|
// Next continues execution until the next source line.
|
||||||
func Next(dbp Process) (err error) {
|
func Next(dbp Process) (err error) {
|
||||||
if _, err := dbp.Valid(); err != nil {
|
if _, err := dbp.Valid(); err != nil {
|
||||||
|
@ -2545,7 +2545,7 @@ func TestStepOnCallPtrInstr(t *testing.T) {
|
|||||||
regs, err := p.CurrentThread().Registers(false)
|
regs, err := p.CurrentThread().Registers(false)
|
||||||
assertNoError(err, t, "Registers()")
|
assertNoError(err, t, "Registers()")
|
||||||
pc := regs.PC()
|
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()")
|
assertNoError(err, t, "Disassemble()")
|
||||||
if text[0].IsCall() {
|
if text[0].IsCall() {
|
||||||
found = true
|
found = true
|
||||||
@ -3629,7 +3629,8 @@ func TestIssue1145(t *testing.T) {
|
|||||||
func TestDisassembleGlobalVars(t *testing.T) {
|
func TestDisassembleGlobalVars(t *testing.T) {
|
||||||
withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcess("teststepconcurrent", t, func(p proc.Process, fixture protest.Fixture) {
|
||||||
mainfn := p.BinInfo().LookupFunc["main.main"]
|
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")
|
assertNoError(err, t, "Disassemble")
|
||||||
found := false
|
found := false
|
||||||
for i := range text {
|
for i := range text {
|
||||||
|
@ -225,7 +225,7 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !csource {
|
if !csource {
|
||||||
deferreturns := findDeferReturnCalls(text)
|
deferreturns := FindDeferReturnCalls(text)
|
||||||
|
|
||||||
// Set breakpoint on the most recently deferred function (if any)
|
// Set breakpoint on the most recently deferred function (if any)
|
||||||
var deferpc uint64
|
var deferpc uint64
|
||||||
@ -325,7 +325,7 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findDeferReturnCalls(text []AsmInstruction) []uint64 {
|
func FindDeferReturnCalls(text []AsmInstruction) []uint64 {
|
||||||
const deferreturn = "runtime.deferreturn"
|
const deferreturn = "runtime.deferreturn"
|
||||||
deferreturns := []uint64{}
|
deferreturns := []uint64{}
|
||||||
|
|
||||||
|
@ -218,12 +218,42 @@ func (d *Debugger) LastModified() time.Time {
|
|||||||
return d.target.BinInfo().LastModified()
|
return d.target.BinInfo().LastModified()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const deferReturn = "runtime.deferreturn"
|
||||||
|
|
||||||
// FunctionReturnLocations returns all return locations
|
// FunctionReturnLocations returns all return locations
|
||||||
// for the given function. See the documentation for the
|
// for the given function, a list of addresses corresponding
|
||||||
// function of the same name within the `proc` package for
|
// to 'ret' or 'call runtime.deferreturn'.
|
||||||
// more information.
|
|
||||||
func (d *Debugger) FunctionReturnLocations(fnName string) ([]uint64, error) {
|
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.
|
// Detach detaches from the target process.
|
||||||
@ -1121,9 +1151,9 @@ func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Locat
|
|||||||
return locs, err
|
return locs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disassemble code between startPC and endPC
|
// Disassemble code between startPC and endPC.
|
||||||
// if endPC == 0 it will find the function containing startPC and disassemble the whole function
|
// 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) {
|
func (d *Debugger) Disassemble(goroutineID int, addr1, addr2 uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
|
||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
@ -1131,21 +1161,27 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if endPC == 0 {
|
if addr2 == 0 {
|
||||||
_, _, fn := d.target.BinInfo().PCToLine(startPC)
|
_, _, fn := d.target.BinInfo().PCToLine(addr1)
|
||||||
if fn == nil {
|
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
|
addr1 = fn.Entry
|
||||||
endPC = fn.End
|
addr2 = fn.End
|
||||||
}
|
}
|
||||||
|
|
||||||
g, err := proc.FindGoroutine(d.target, scope.GoroutineID)
|
g, err := proc.FindGoroutine(d.target, goroutineID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -317,6 +317,6 @@ type DisassembleRequest struct {
|
|||||||
|
|
||||||
func (c *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error {
|
func (c *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error {
|
||||||
var err 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
|
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.
|
// 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 {
|
func (c *RPCServer) Disassemble(arg DisassembleIn, out *DisassembleOut) error {
|
||||||
var err 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user