delve/pkg/proc/disasm.go
aarzilli bc86c662a6 pkg/proc: fix StepBreakpoint handling
StepBreakpoints are set on CALL instructions, when they are hit we
disassemble the current instruction, figure out the destination address
and set a breakpoint after the prologue of the called function.

In order to disassemble the current instruction we disassemble the area
of memory starting from PC and going to PC+15 (because 15 bytes is the
maximum length of one instruction on AMD64). This means that we won't
just disassemble one instruction but also a few instructions following
it ending with one truncated instruction.

This usually works fine but sometimes the disassembler will panic with
an array out of bounds error when trying to disassemble a truncated
instruction. To avoid this problem this commit changes the funciton
disassemble to take one extra parameter, singleInstr, when singleInstr
is set disassemble will quit after disassembling a single instruction.
2017-11-21 00:40:26 -08:00

86 lines
2.3 KiB
Go

package proc
type AsmInstruction struct {
Loc Location
DestLoc *Location
Bytes []byte
Breakpoint bool
AtPC bool
Inst *ArchInst
}
type AssemblyFlavour int
const (
GNUFlavour = AssemblyFlavour(iota)
IntelFlavour
)
// Disassemble disassembles target memory between startPC and endPC, 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 dbp.Exited() {
return nil, &ProcessExitedError{Pid: dbp.Pid()}
}
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) {
mem := make([]byte, int(endPC-startPC))
_, err := memrw.ReadMemory(mem, uintptr(startPC))
if err != nil {
return nil, err
}
r := make([]AsmInstruction, 0, len(mem)/15)
pc := startPC
var curpc uint64
if regs != nil {
curpc = regs.PC()
}
for len(mem) > 0 {
bp, atbp := breakpoints.M[pc]
if atbp {
for i := range bp.OriginalData {
mem[i] = bp.OriginalData[i]
}
}
file, line, fn := bi.PCToLine(pc)
loc := Location{PC: pc, File: file, Line: line, Fn: fn}
inst, err := asmDecode(mem, pc)
if err == nil {
atpc := (regs != nil) && (curpc == pc)
destloc := resolveCallArg(inst, atpc, regs, memrw, bi)
r = append(r, AsmInstruction{Loc: loc, DestLoc: destloc, Bytes: mem[:inst.Len], Breakpoint: atbp, AtPC: atpc, Inst: inst})
pc += uint64(inst.Size())
mem = mem[inst.Size():]
} else {
r = append(r, AsmInstruction{Loc: loc, Bytes: mem[:1], Breakpoint: atbp, Inst: nil})
pc++
mem = mem[1:]
}
if singleInstr {
break
}
}
return r, nil
}