github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/proc/disasm.go (about) 1 package proc 2 3 import ( 4 "fmt" 5 6 "github.com/go-delve/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 copy(mem, bp.OriginalData) 154 } 155 156 file, line, fn := bi.PCToLine(pc) 157 158 var inst AsmInstruction 159 inst.Loc = Location{PC: pc, File: file, Line: line, Fn: fn} 160 inst.Breakpoint = atbp 161 inst.AtPC = (regs != nil) && (curpc == pc) 162 163 bi.Arch.asmDecode(&inst, mem, dregs, memrw, bi) 164 165 r = append(r, inst) 166 167 pc += uint64(inst.Size) 168 mem = mem[inst.Size:] 169 170 if singleInstr { 171 break 172 } 173 } 174 return r, nil 175 } 176 177 // Text will return the assembly instructions in human readable format according to 178 // the flavour specified. 179 func (instr *AsmInstruction) Text(flavour AssemblyFlavour, bi *BinaryInfo) string { 180 return instr.Inst.Text(flavour, instr.Loc.PC, bi.symLookup) 181 }