github.com/undoio/delve@v1.9.0/pkg/proc/disasm.go (about)

     1  package proc
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/undoio/delve/pkg/dwarf/op"
     7  )
     8  
     9  // AsmInstruction represents one assembly instruction.
    10  type AsmInstruction struct {
    11  	Loc        Location
    12  	DestLoc    *Location
    13  	Bytes      []byte
    14  	Breakpoint bool
    15  	AtPC       bool
    16  
    17  	Size int
    18  	Kind AsmInstructionKind
    19  
    20  	Inst archInst
    21  }
    22  
    23  type AsmInstructionKind uint8
    24  
    25  const (
    26  	OtherInstruction AsmInstructionKind = iota
    27  	CallInstruction
    28  	RetInstruction
    29  	JmpInstruction
    30  	HardBreakInstruction
    31  )
    32  
    33  // IsCall is true if instr is a call instruction.
    34  func (instr *AsmInstruction) IsCall() bool {
    35  	return instr.Kind == CallInstruction
    36  }
    37  
    38  // IsRet is true if instr is a return instruction.
    39  func (instr *AsmInstruction) IsRet() bool {
    40  	return instr.Kind == RetInstruction
    41  }
    42  
    43  // IsJmp is true if instr is an unconditional jump instruction.
    44  func (instr *AsmInstruction) IsJmp() bool {
    45  	return instr.Kind == JmpInstruction
    46  }
    47  
    48  // IsHardBreak is true if instr is a hardcoded breakpoint instruction.
    49  func (instr *AsmInstruction) IsHardBreak() bool {
    50  	return instr.Kind == HardBreakInstruction
    51  }
    52  
    53  type archInst interface {
    54  	Text(flavour AssemblyFlavour, pc uint64, symLookup func(uint64) (string, uint64)) string
    55  	OpcodeEquals(op uint64) bool
    56  }
    57  
    58  // AssemblyFlavour is the assembly syntax to display.
    59  type AssemblyFlavour int
    60  
    61  const (
    62  	// GNUFlavour will display GNU assembly syntax.
    63  	GNUFlavour AssemblyFlavour = iota
    64  	// IntelFlavour will display Intel assembly syntax.
    65  	IntelFlavour
    66  	// GoFlavour will display Go assembly syntax.
    67  	GoFlavour
    68  )
    69  
    70  type opcodeSeq []uint64
    71  
    72  // firstPCAfterPrologueDisassembly returns the address of the first
    73  // instruction after the prologue for function fn by disassembling fn and
    74  // matching the instructions against known split-stack prologue patterns.
    75  // If sameline is set firstPCAfterPrologueDisassembly will always return an
    76  // address associated with the same line as fn.Entry
    77  func firstPCAfterPrologueDisassembly(p Process, fn *Function, sameline bool) (uint64, error) {
    78  	mem := p.Memory()
    79  	breakpoints := p.Breakpoints()
    80  	bi := p.BinInfo()
    81  	text, err := disassemble(mem, nil, breakpoints, bi, fn.Entry, fn.End, false)
    82  	if err != nil {
    83  		return fn.Entry, err
    84  	}
    85  
    86  	if len(text) <= 0 {
    87  		return fn.Entry, nil
    88  	}
    89  
    90  	for _, prologue := range p.BinInfo().Arch.prologues {
    91  		if len(prologue) >= len(text) {
    92  			continue
    93  		}
    94  		if checkPrologue(text, prologue) {
    95  			r := &text[len(prologue)]
    96  			if sameline {
    97  				if r.Loc.Line != text[0].Loc.Line {
    98  					return fn.Entry, nil
    99  				}
   100  			}
   101  			return r.Loc.PC, nil
   102  		}
   103  	}
   104  
   105  	return fn.Entry, nil
   106  }
   107  
   108  func checkPrologue(s []AsmInstruction, prologuePattern opcodeSeq) bool {
   109  	line := s[0].Loc.Line
   110  	for i, op := range prologuePattern {
   111  		if !s[i].Inst.OpcodeEquals(op) || s[i].Loc.Line != line {
   112  			return false
   113  		}
   114  	}
   115  	return true
   116  }
   117  
   118  // Disassemble disassembles target memory between startAddr and endAddr, marking
   119  // the current instruction being executed in goroutine g.
   120  // If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble
   121  // will evaluate the argument of the CALL instruction using the thread's registers.
   122  // Be aware that the Bytes field of each returned instruction is a slice of a larger array of size startAddr - endAddr.
   123  func Disassemble(mem MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startAddr, endAddr uint64) ([]AsmInstruction, error) {
   124  	if startAddr > endAddr {
   125  		return nil, fmt.Errorf("start address(%x) should be less than end address(%x)", startAddr, endAddr)
   126  	}
   127  	return disassemble(mem, regs, breakpoints, bi, startAddr, endAddr, false)
   128  }
   129  
   130  func disassemble(memrw MemoryReadWriter, regs Registers, breakpoints *BreakpointMap, bi *BinaryInfo, startAddr, endAddr uint64, singleInstr bool) ([]AsmInstruction, error) {
   131  	var dregs *op.DwarfRegisters
   132  	if regs != nil {
   133  		dregs = bi.Arch.RegistersToDwarfRegisters(0, regs)
   134  	}
   135  
   136  	mem := make([]byte, int(endAddr-startAddr))
   137  	_, err := memrw.ReadMemory(mem, startAddr)
   138  	if err != nil {
   139  		return nil, err
   140  	}
   141  
   142  	r := make([]AsmInstruction, 0, len(mem)/int(bi.Arch.MaxInstructionLength()))
   143  	pc := startAddr
   144  
   145  	var curpc uint64
   146  	if regs != nil {
   147  		curpc = regs.PC()
   148  	}
   149  
   150  	for len(mem) > 0 {
   151  		bp, atbp := breakpoints.M[pc]
   152  		if atbp {
   153  			for i := range bp.OriginalData {
   154  				mem[i] = bp.OriginalData[i]
   155  			}
   156  		}
   157  
   158  		file, line, fn := bi.PCToLine(pc)
   159  
   160  		var inst AsmInstruction
   161  		inst.Loc = Location{PC: pc, File: file, Line: line, Fn: fn}
   162  		inst.Breakpoint = atbp
   163  		inst.AtPC = (regs != nil) && (curpc == pc)
   164  
   165  		bi.Arch.asmDecode(&inst, mem, dregs, memrw, bi)
   166  
   167  		r = append(r, inst)
   168  
   169  		pc += uint64(inst.Size)
   170  		mem = mem[inst.Size:]
   171  
   172  		if singleInstr {
   173  			break
   174  		}
   175  	}
   176  	return r, nil
   177  }
   178  
   179  // Text will return the assembly instructions in human readable format according to
   180  // the flavour specified.
   181  func (inst *AsmInstruction) Text(flavour AssemblyFlavour, bi *BinaryInfo) string {
   182  	return inst.Inst.Text(flavour, inst.Loc.PC, bi.symLookup)
   183  }