github.com/cnboonhan/delve@v0.0.0-20230908061759-363f2388c2fb/pkg/proc/i386_arch.go (about) 1 package proc 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "strings" 7 8 "github.com/go-delve/delve/pkg/dwarf/frame" 9 "github.com/go-delve/delve/pkg/dwarf/op" 10 "github.com/go-delve/delve/pkg/dwarf/regnum" 11 ) 12 13 var i386BreakInstruction = []byte{0xCC} 14 15 // I386Arch returns an initialized I386Arch 16 // struct. 17 func I386Arch(goos string) *Arch { 18 return &Arch{ 19 Name: "386", 20 ptrSize: 4, 21 maxInstructionLength: 15, 22 breakpointInstruction: i386BreakInstruction, 23 altBreakpointInstruction: []byte{0xcd, 0x03}, 24 breakInstrMovesPC: true, 25 derefTLS: false, 26 prologues: prologuesI386, 27 fixFrameUnwindContext: i386FixFrameUnwindContext, 28 switchStack: i386SwitchStack, 29 regSize: i386RegSize, 30 RegistersToDwarfRegisters: i386RegistersToDwarfRegisters, 31 addrAndStackRegsToDwarfRegisters: i386AddrAndStackRegsToDwarfRegisters, 32 DwarfRegisterToString: i386DwarfRegisterToString, 33 inhibitStepInto: i386InhibitStepInto, 34 asmDecode: i386AsmDecode, 35 PCRegNum: regnum.I386_Eip, 36 SPRegNum: regnum.I386_Esp, 37 asmRegisters: i386AsmRegisters, 38 RegisterNameToDwarf: nameToDwarfFunc(regnum.I386NameToDwarf), 39 RegnumToString: regnum.I386ToName, 40 } 41 } 42 43 func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext { 44 i := bi.Arch 45 if i.sigreturnfn == nil { 46 i.sigreturnfn = bi.lookupOneFunc("runtime.sigreturn") 47 } 48 49 if fctxt == nil || (i.sigreturnfn != nil && pc >= i.sigreturnfn.Entry && pc < i.sigreturnfn.End) { 50 // When there's no frame descriptor entry use BP (the frame pointer) instead 51 // - return register is [bp + i.PtrSize()] (i.e. [cfa-i.PtrSize()]) 52 // - cfa is bp + i.PtrSize()*2 53 // - bp is [bp] (i.e. [cfa-i.PtrSize()*2]) 54 // - sp is cfa 55 56 // When the signal handler runs it will move the execution to the signal 57 // handling stack (installed using the sigaltstack system call). 58 // This isn't i proper stack switch: the pointer to g in TLS will still 59 // refer to whatever g was executing on that thread before the signal was 60 // received. 61 // Since go did not execute i stack switch the previous value of sp, pc 62 // and bp is not saved inside g.sched, as it normally would. 63 // The only way to recover is to either read sp/pc from the signal context 64 // parameter (the ucontext_t* parameter) or to unconditionally follow the 65 // frame pointer when we get to runtime.sigreturn (which is what we do 66 // here). 67 68 return &frame.FrameContext{ 69 RetAddrReg: regnum.I386_Eip, 70 Regs: map[uint64]frame.DWRule{ 71 regnum.I386_Eip: { 72 Rule: frame.RuleOffset, 73 Offset: int64(-i.PtrSize()), 74 }, 75 regnum.I386_Ebp: { 76 Rule: frame.RuleOffset, 77 Offset: int64(-2 * i.PtrSize()), 78 }, 79 regnum.I386_Esp: { 80 Rule: frame.RuleValOffset, 81 Offset: 0, 82 }, 83 }, 84 CFA: frame.DWRule{ 85 Rule: frame.RuleCFA, 86 Reg: regnum.I386_Ebp, 87 Offset: int64(2 * i.PtrSize()), 88 }, 89 } 90 } 91 92 if i.crosscall2fn == nil { 93 i.crosscall2fn = bi.lookupOneFunc("crosscall2") 94 } 95 96 // TODO(chainhelen), need to check whether there is a bad frame descriptor like amd64. 97 // crosscall2 is defined in $GOROOT/src/runtime/cgo/asm_386.s. 98 if i.crosscall2fn != nil && pc >= i.crosscall2fn.Entry && pc < i.crosscall2fn.End { 99 rule := fctxt.CFA 100 fctxt.CFA = rule 101 } 102 103 // We assume that EBP is the frame pointer and we want to keep it updated, 104 // so that we can use it to unwind the stack even when we encounter frames 105 // without descriptor entries. 106 // If there isn't i rule already we emit one. 107 if fctxt.Regs[regnum.I386_Ebp].Rule == frame.RuleUndefined { 108 fctxt.Regs[regnum.I386_Ebp] = frame.DWRule{ 109 Rule: frame.RuleFramePointer, 110 Reg: regnum.I386_Ebp, 111 Offset: 0, 112 } 113 } 114 115 return fctxt 116 } 117 118 // SwitchStack will use the current frame to determine if it's time to 119 func i386SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool { 120 if it.frame.Current.Fn == nil { 121 if it.systemstack && it.g != nil && it.top { 122 it.switchToGoroutineStack() 123 return true 124 } 125 return false 126 } 127 switch it.frame.Current.Fn.Name { 128 case "runtime.asmcgocall", "runtime.cgocallback_gofunc": // TODO(chainhelen), need to support cgo stacktraces. 129 return false 130 case "runtime.goexit", "runtime.rt0_go": 131 // Look for "top of stack" functions. 132 it.atend = true 133 return true 134 135 case "runtime.mcall": 136 if it.systemstack && it.g != nil { 137 it.switchToGoroutineStack() 138 return true 139 } 140 it.atend = true 141 return true 142 143 case "runtime.mstart": 144 // Calls to runtime.systemstack will switch to the systemstack then: 145 // 1. alter the goroutine stack so that it looks like systemstack_switch 146 // was called 147 // 2. alter the system stack so that it looks like the bottom-most frame 148 // belongs to runtime.mstart 149 // If we find a runtime.mstart frame on the system stack of a goroutine 150 // parked on runtime.systemstack_switch we assume runtime.systemstack was 151 // called and continue tracing from the parked position. 152 153 if it.top || !it.systemstack || it.g == nil { 154 return false 155 } 156 if fn := it.bi.PCToFunc(it.g.PC); fn == nil || fn.Name != "runtime.systemstack_switch" { 157 return false 158 } 159 160 it.switchToGoroutineStack() 161 return true 162 163 case "runtime.newstack", "runtime.systemstack": 164 if it.systemstack && it.g != nil { 165 it.switchToGoroutineStack() 166 return true 167 } 168 169 return false 170 171 default: 172 return false 173 } 174 } 175 176 // RegSize returns the size (in bytes) of register regnum. 177 // The mapping between hardware registers and DWARF registers is specified 178 // in the System V ABI Intel386 Architecture Processor Supplement page 25, 179 // table 2.14 180 // https://www.uclibc.org/docs/psABI-i386.pdf 181 func i386RegSize(regnum uint64) int { 182 // XMM registers 183 if regnum >= 21 && regnum <= 36 { 184 return 16 185 } 186 // x87 registers 187 if regnum >= 11 && regnum <= 18 { 188 return 10 189 } 190 return 4 191 } 192 193 func i386RegistersToDwarfRegisters(staticBase uint64, regs Registers) *op.DwarfRegisters { 194 dregs := initDwarfRegistersFromSlice(regnum.I386MaxRegNum(), regs, regnum.I386NameToDwarf) 195 dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.I386_Eip, regnum.I386_Esp, regnum.I386_Ebp, 0) 196 dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.I386NameToDwarf)) 197 198 return dr 199 } 200 201 func i386AddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { 202 dregs := make([]*op.DwarfRegister, regnum.I386_Eip+1) 203 dregs[regnum.I386_Eip] = op.DwarfRegisterFromUint64(pc) 204 dregs[regnum.I386_Esp] = op.DwarfRegisterFromUint64(sp) 205 dregs[regnum.I386_Ebp] = op.DwarfRegisterFromUint64(bp) 206 207 return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.I386_Eip, regnum.I386_Esp, regnum.I386_Ebp, 0) 208 } 209 210 func i386DwarfRegisterToString(j int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { 211 name = regnum.I386ToName(uint64(j)) 212 213 if reg == nil { 214 return name, false, "" 215 } 216 217 switch n := strings.ToLower(name); n { 218 case "eflags": 219 return name, false, eflagsDescription.Describe(reg.Uint64Val, 32) 220 221 case "tw", "fop": 222 return name, true, fmt.Sprintf("%#04x", reg.Uint64Val) 223 224 default: 225 if reg.Bytes != nil && strings.HasPrefix(n, "xmm") { 226 return name, true, formatSSEReg(name, reg.Bytes) 227 } else if reg.Bytes != nil && strings.HasPrefix(n, "st(") { 228 return name, true, formatX87Reg(reg.Bytes) 229 } else if reg.Bytes == nil || (reg.Bytes != nil && len(reg.Bytes) <= 8) { 230 return name, false, fmt.Sprintf("%#016x", reg.Uint64Val) 231 } else { 232 return name, false, fmt.Sprintf("%#x", reg.Bytes) 233 } 234 } 235 } 236 237 // i386InhibitStepInto returns whether StepBreakpoint can be set at pc. 238 // When cgo or pie on 386 linux, compiler will insert more instructions (ex: call __x86.get_pc_thunk.). 239 // StepBreakpoint shouldn't be set on __x86.get_pc_thunk and skip it. 240 // See comments on stacksplit in $GOROOT/src/cmd/internal/obj/x86/obj6.go for generated instructions details. 241 func i386InhibitStepInto(bi *BinaryInfo, pc uint64) bool { 242 if bi.SymNames != nil && bi.SymNames[pc] != nil && 243 strings.HasPrefix(bi.SymNames[pc].Name, "__x86.get_pc_thunk.") { 244 return true 245 } 246 return false 247 }