From 4634cf80727e181568ca89d69665374b28a28092 Mon Sep 17 00:00:00 2001 From: aarzilli Date: Mon, 12 Nov 2018 13:06:24 +0100 Subject: [PATCH] proc/*: allow stepping into functions without debug_info symbols If proc.Step encounters a CALL instruction that points to an address that isn't associated with any function it should still follow the CALL. The circumstances creating this problem do not normally occur, it was encountered in the process of fixing a bug created by Go1.12. --- pkg/proc/breakpoints.go | 7 ++++++- pkg/proc/disasm_amd64.go | 2 +- pkg/proc/gdbserial/gdbserver.go | 3 --- pkg/proc/native/proc.go | 3 --- pkg/proc/threads.go | 26 ++++++++++++++++---------- 5 files changed, 23 insertions(+), 18 deletions(-) diff --git a/pkg/proc/breakpoints.go b/pkg/proc/breakpoints.go index 72b8468042..66230725a1 100644 --- a/pkg/proc/breakpoints.go +++ b/pkg/proc/breakpoints.go @@ -252,8 +252,13 @@ func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr, return nil, err } + fnName := "" + if fn != nil { + fnName = fn.Name + } + newBreakpoint := &Breakpoint{ - FunctionName: fn.Name, + FunctionName: fnName, File: f, Line: l, Addr: addr, diff --git a/pkg/proc/disasm_amd64.go b/pkg/proc/disasm_amd64.go index d13ed820ec..bc5ba2cbc7 100644 --- a/pkg/proc/disasm_amd64.go +++ b/pkg/proc/disasm_amd64.go @@ -118,7 +118,7 @@ func resolveCallArg(inst *archInst, currentGoroutine bool, regs Registers, mem M file, line, fn := bininfo.PCToLine(pc) if fn == nil { - return nil + return &Location{PC: pc} } return &Location{PC: pc, File: file, Line: line, Fn: fn} } diff --git a/pkg/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go index becbd58247..7b8f94c6f9 100644 --- a/pkg/proc/gdbserial/gdbserver.go +++ b/pkg/proc/gdbserial/gdbserver.go @@ -1028,9 +1028,6 @@ func (p *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) { func (p *Process) writeBreakpoint(addr uint64) (string, int, *proc.Function, []byte, error) { f, l, fn := p.bi.PCToLine(uint64(addr)) - if fn == nil { - return "", 0, nil, nil, proc.InvalidAddressError{Address: addr} - } if err := p.conn.setBreakpoint(addr); err != nil { return "", 0, nil, nil, err diff --git a/pkg/proc/native/proc.go b/pkg/proc/native/proc.go index 1a0ea012a5..d3fefe507d 100644 --- a/pkg/proc/native/proc.go +++ b/pkg/proc/native/proc.go @@ -211,9 +211,6 @@ func (dbp *Process) CheckAndClearManualStopRequest() bool { func (dbp *Process) writeBreakpoint(addr uint64) (string, int, *proc.Function, []byte, error) { f, l, fn := dbp.bi.PCToLine(uint64(addr)) - if fn == nil { - return "", 0, nil, nil, proc.InvalidAddressError{Address: addr} - } originalData := make([]byte, dbp.bi.Arch.BreakpointSize()) _, err := dbp.currentThread.ReadMemory(originalData, uintptr(addr)) diff --git a/pkg/proc/threads.go b/pkg/proc/threads.go index 153155eccd..c15b85fb18 100644 --- a/pkg/proc/threads.go +++ b/pkg/proc/threads.go @@ -379,21 +379,16 @@ func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) er instr := text[0] - if instr.DestLoc == nil || instr.DestLoc.Fn == nil { + if instr.DestLoc == nil { + // Call destination couldn't be resolved because this is was not the + // current instruction, therefore the step-into breakpoint can not be set. return nil } fn := instr.DestLoc.Fn - // Ensure PC and Entry match, otherwise StepInto is likely to set - // its breakpoint before DestLoc.PC and hence run too far ahead. - // Calls to runtime.duffzero and duffcopy have this problem. - if fn.Entry != instr.DestLoc.PC { - return nil - } - // Skip unexported runtime functions - if strings.HasPrefix(fn.Name, "runtime.") && !isExportedRuntime(fn.Name) { + if fn != nil && strings.HasPrefix(fn.Name, "runtime.") && !isExportedRuntime(fn.Name) { return nil } @@ -401,8 +396,19 @@ func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) er // or entire packages from being stepped into with 'step' // those extra checks should be done here. + pc := instr.DestLoc.PC + + // We want to skip the function prologue but we should only do it if the + // destination address of the CALL instruction is the entry point of the + // function. + // Calls to runtime.duffzero and duffcopy inserted by the compiler can + // sometimes point inside the body of those functions, well after the + // prologue. + if fn != nil && fn.Entry == instr.DestLoc.PC { + pc, _ = FirstPCAfterPrologue(dbp, fn, false) + } + // Set a breakpoint after the function's prologue - pc, _ := FirstPCAfterPrologue(dbp, fn, false) if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil { if _, ok := err.(BreakpointExistsError); !ok { return err