Skip to content

Commit

Permalink
proc/*: allow stepping into functions without debug_info symbols
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
aarzilli committed Nov 16, 2018
1 parent d3a952c commit 4634cf8
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 18 deletions.
7 changes: 6 additions & 1 deletion pkg/proc/breakpoints.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion pkg/proc/disasm_amd64.go
Original file line number Diff line number Diff line change
Expand Up @@ -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}
}
Expand Down
3 changes: 0 additions & 3 deletions pkg/proc/gdbserial/gdbserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 0 additions & 3 deletions pkg/proc/native/proc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
26 changes: 16 additions & 10 deletions pkg/proc/threads.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,30 +379,36 @@ 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
}

//TODO(aarzilli): if we want to let users hide functions
// 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
Expand Down

0 comments on commit 4634cf8

Please sign in to comment.