2016-02-06 06:00:48 +00:00
|
|
|
package proc
|
|
|
|
|
2021-04-28 17:00:26 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/go-delve/delve/pkg/dwarf/op"
|
|
|
|
)
|
2020-02-19 16:46:03 +00:00
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
// AsmInstruction represents one assembly instruction.
|
2016-02-06 06:00:48 +00:00
|
|
|
type AsmInstruction struct {
|
|
|
|
Loc Location
|
|
|
|
DestLoc *Location
|
|
|
|
Bytes []byte
|
|
|
|
Breakpoint bool
|
|
|
|
AtPC bool
|
2019-11-28 12:54:39 +00:00
|
|
|
|
|
|
|
Size int
|
|
|
|
Kind AsmInstructionKind
|
|
|
|
|
|
|
|
Inst archInst
|
|
|
|
}
|
|
|
|
|
|
|
|
type AsmInstructionKind uint8
|
|
|
|
|
|
|
|
const (
|
|
|
|
OtherInstruction AsmInstructionKind = iota
|
|
|
|
CallInstruction
|
|
|
|
RetInstruction
|
2020-03-09 09:47:56 +00:00
|
|
|
JmpInstruction
|
2020-07-28 16:19:51 +00:00
|
|
|
HardBreakInstruction
|
2019-11-28 12:54:39 +00:00
|
|
|
)
|
|
|
|
|
2020-03-09 09:47:56 +00:00
|
|
|
// IsCall is true if instr is a call instruction.
|
2019-11-28 12:54:39 +00:00
|
|
|
func (instr *AsmInstruction) IsCall() bool {
|
|
|
|
return instr.Kind == CallInstruction
|
|
|
|
}
|
|
|
|
|
2020-03-09 09:47:56 +00:00
|
|
|
// IsRet is true if instr is a return instruction.
|
2019-11-28 12:54:39 +00:00
|
|
|
func (instr *AsmInstruction) IsRet() bool {
|
|
|
|
return instr.Kind == RetInstruction
|
|
|
|
}
|
|
|
|
|
2020-03-09 09:47:56 +00:00
|
|
|
// IsJmp is true if instr is an unconditional jump instruction.
|
|
|
|
func (instr *AsmInstruction) IsJmp() bool {
|
|
|
|
return instr.Kind == JmpInstruction
|
|
|
|
}
|
|
|
|
|
2020-07-28 16:19:51 +00:00
|
|
|
// IsHardBreak is true if instr is a hardcoded breakpoint instruction.
|
|
|
|
func (instr *AsmInstruction) IsHardBreak() bool {
|
|
|
|
return instr.Kind == HardBreakInstruction
|
|
|
|
}
|
|
|
|
|
2019-11-28 12:54:39 +00:00
|
|
|
type archInst interface {
|
|
|
|
Text(flavour AssemblyFlavour, pc uint64, symLookup func(uint64) (string, uint64)) string
|
|
|
|
OpcodeEquals(op uint64) bool
|
2016-02-06 06:00:48 +00:00
|
|
|
}
|
|
|
|
|
2018-08-31 18:08:18 +00:00
|
|
|
// AssemblyFlavour is the assembly syntax to display.
|
2016-02-06 06:00:48 +00:00
|
|
|
type AssemblyFlavour int
|
|
|
|
|
|
|
|
const (
|
2018-08-31 18:08:18 +00:00
|
|
|
// GNUFlavour will display GNU assembly syntax.
|
2021-02-10 17:31:37 +00:00
|
|
|
GNUFlavour AssemblyFlavour = iota
|
2018-08-31 18:08:18 +00:00
|
|
|
// IntelFlavour will display Intel assembly syntax.
|
2016-02-06 06:00:48 +00:00
|
|
|
IntelFlavour
|
2018-08-31 18:08:18 +00:00
|
|
|
// GoFlavour will display Go assembly syntax.
|
2017-10-26 11:37:19 +00:00
|
|
|
GoFlavour
|
2016-02-06 06:00:48 +00:00
|
|
|
)
|
|
|
|
|
2019-11-28 12:54:39 +00:00
|
|
|
type opcodeSeq []uint64
|
|
|
|
|
2019-11-26 11:03:24 +00:00
|
|
|
// firstPCAfterPrologueDisassembly returns the address of the first
|
|
|
|
// instruction after the prologue for function fn by disassembling fn and
|
|
|
|
// matching the instructions against known split-stack prologue patterns.
|
|
|
|
// If sameline is set firstPCAfterPrologueDisassembly will always return an
|
|
|
|
// address associated with the same line as fn.Entry
|
|
|
|
func firstPCAfterPrologueDisassembly(p Process, fn *Function, sameline bool) (uint64, error) {
|
2020-11-09 19:28:40 +00:00
|
|
|
mem := p.Memory()
|
2019-11-26 11:03:24 +00:00
|
|
|
breakpoints := p.Breakpoints()
|
|
|
|
bi := p.BinInfo()
|
|
|
|
text, err := disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End, false)
|
|
|
|
if err != nil {
|
|
|
|
return fn.Entry, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(text) <= 0 {
|
|
|
|
return fn.Entry, nil
|
|
|
|
}
|
|
|
|
|
2020-10-12 22:05:28 +00:00
|
|
|
for _, prologue := range p.BinInfo().Arch.prologues {
|
2019-11-26 11:03:24 +00:00
|
|
|
if len(prologue) >= len(text) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if checkPrologue(text, prologue) {
|
|
|
|
r := &text[len(prologue)]
|
|
|
|
if sameline {
|
|
|
|
if r.Loc.Line != text[0].Loc.Line {
|
|
|
|
return fn.Entry, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return r.Loc.PC, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fn.Entry, nil
|
|
|
|
}
|
|
|
|
|
2019-11-28 12:54:39 +00:00
|
|
|
func checkPrologue(s []AsmInstruction, prologuePattern opcodeSeq) bool {
|
2019-11-26 11:03:24 +00:00
|
|
|
line := s[0].Loc.Line
|
|
|
|
for i, op := range prologuePattern {
|
2019-11-28 12:54:39 +00:00
|
|
|
if !s[i].Inst.OpcodeEquals(op) || s[i].Loc.Line != line {
|
2019-11-26 11:03:24 +00:00
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2019-08-08 18:54:56 +00:00
|
|
|
// Disassemble disassembles target memory between startAddr and endAddr, marking
|
2017-02-15 13:41:03 +00:00
|
|
|
// the current instruction being executed in goroutine g.
|
2019-08-08 18:54:56 +00:00
|
|
|
// 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) {
|
2020-02-19 16:46:03 +00:00
|
|
|
if startAddr > endAddr {
|
|
|
|
return nil, fmt.Errorf("start address(%x) should be less than end address(%x)", startAddr, endAddr)
|
|
|
|
}
|
2019-08-08 18:54:56 +00:00
|
|
|
return disassemble(mem, regs, breakpoints, bi, startAddr, endAddr, false)
|
2017-04-13 23:19:57 +00:00
|
|
|
}
|
|
|
|
|
2019-08-08 18:54:56 +00:00
|
|
|
func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startAddr, endAddr uint64, singleInstr bool) ([]AsmInstruction, error) {
|
2021-04-28 17:00:26 +00:00
|
|
|
var dregs *op.DwarfRegisters
|
|
|
|
if regs != nil {
|
|
|
|
dregs = bi.Arch.RegistersToDwarfRegisters(0, regs)
|
|
|
|
}
|
|
|
|
|
2019-08-08 18:54:56 +00:00
|
|
|
mem := make([]byte, int(endAddr-startAddr))
|
2020-09-09 17:36:15 +00:00
|
|
|
_, err := memrw.ReadMemory(mem, startAddr)
|
2016-02-06 06:00:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-11-28 12:54:39 +00:00
|
|
|
r := make([]AsmInstruction, 0, len(mem)/int(bi.Arch.MaxInstructionLength()))
|
2019-08-08 18:54:56 +00:00
|
|
|
pc := startAddr
|
2016-02-06 06:00:48 +00:00
|
|
|
|
|
|
|
var curpc uint64
|
2017-04-13 23:19:57 +00:00
|
|
|
if regs != nil {
|
|
|
|
curpc = regs.PC()
|
2016-02-06 06:00:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for len(mem) > 0 {
|
2017-09-24 13:00:55 +00:00
|
|
|
bp, atbp := breakpoints.M[pc]
|
2016-02-06 06:00:48 +00:00
|
|
|
if atbp {
|
2022-07-22 17:39:18 +00:00
|
|
|
copy(mem, bp.OriginalData)
|
2016-02-06 06:00:48 +00:00
|
|
|
}
|
2019-11-28 12:54:39 +00:00
|
|
|
|
2017-04-13 23:19:57 +00:00
|
|
|
file, line, fn := bi.PCToLine(pc)
|
2019-11-28 12:54:39 +00:00
|
|
|
|
|
|
|
var inst AsmInstruction
|
|
|
|
inst.Loc = Location{PC: pc, File: file, Line: line, Fn: fn}
|
|
|
|
inst.Breakpoint = atbp
|
|
|
|
inst.AtPC = (regs != nil) && (curpc == pc)
|
|
|
|
|
2021-04-28 17:00:26 +00:00
|
|
|
bi.Arch.asmDecode(&inst, mem, dregs, memrw, bi)
|
2019-11-28 12:54:39 +00:00
|
|
|
|
|
|
|
r = append(r, inst)
|
|
|
|
|
|
|
|
pc += uint64(inst.Size)
|
|
|
|
mem = mem[inst.Size:]
|
|
|
|
|
2017-11-06 17:34:59 +00:00
|
|
|
if singleInstr {
|
|
|
|
break
|
|
|
|
}
|
2016-02-06 06:00:48 +00:00
|
|
|
}
|
|
|
|
return r, nil
|
|
|
|
}
|
2019-11-28 12:54:39 +00:00
|
|
|
|
|
|
|
// Text will return the assembly instructions in human readable format according to
|
|
|
|
// the flavour specified.
|
|
|
|
func (inst *AsmInstruction) Text(flavour AssemblyFlavour, bi *BinaryInfo) string {
|
|
|
|
return inst.Inst.Text(flavour, inst.Loc.PC, bi.symLookup)
|
|
|
|
}
|