github.com/undoio/delve@v1.9.0/pkg/proc/i386_arch.go (about) 1 package proc 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "strings" 7 8 "github.com/undoio/delve/pkg/dwarf/frame" 9 "github.com/undoio/delve/pkg/dwarf/op" 10 "github.com/undoio/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 } 40 } 41 42 func i386FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext { 43 i := bi.Arch 44 if i.sigreturnfn == nil { 45 i.sigreturnfn = bi.LookupFunc["runtime.sigreturn"] 46 } 47 48 if fctxt == nil || (i.sigreturnfn != nil && pc >= i.sigreturnfn.Entry && pc < i.sigreturnfn.End) { 49 // When there's no frame descriptor entry use BP (the frame pointer) instead 50 // - return register is [bp + i.PtrSize()] (i.e. [cfa-i.PtrSize()]) 51 // - cfa is bp + i.PtrSize()*2 52 // - bp is [bp] (i.e. [cfa-i.PtrSize()*2]) 53 // - sp is cfa 54 55 // When the signal handler runs it will move the execution to the signal 56 // handling stack (installed using the sigaltstack system call). 57 // This isn't i proper stack switch: the pointer to g in TLS will still 58 // refer to whatever g was executing on that thread before the signal was 59 // received. 60 // Since go did not execute i stack switch the previous value of sp, pc 61 // and bp is not saved inside g.sched, as it normally would. 62 // The only way to recover is to either read sp/pc from the signal context 63 // parameter (the ucontext_t* parameter) or to unconditionally follow the 64 // frame pointer when we get to runtime.sigreturn (which is what we do 65 // here). 66 67 return &frame.FrameContext{ 68 RetAddrReg: regnum.I386_Eip, 69 Regs: map[uint64]frame.DWRule{ 70 regnum.I386_Eip: frame.DWRule{ 71 Rule: frame.RuleOffset, 72 Offset: int64(-i.PtrSize()), 73 }, 74 regnum.I386_Ebp: frame.DWRule{ 75 Rule: frame.RuleOffset, 76 Offset: int64(-2 * i.PtrSize()), 77 }, 78 regnum.I386_Esp: frame.DWRule{ 79 Rule: frame.RuleValOffset, 80 Offset: 0, 81 }, 82 }, 83 CFA: frame.DWRule{ 84 Rule: frame.RuleCFA, 85 Reg: regnum.I386_Ebp, 86 Offset: int64(2 * i.PtrSize()), 87 }, 88 } 89 } 90 91 if i.crosscall2fn == nil { 92 i.crosscall2fn = bi.LookupFunc["crosscall2"] 93 } 94 95 // TODO(chainhelen), need to check whether there is a bad frame descriptor like amd64. 96 // crosscall2 is defined in $GOROOT/src/runtime/cgo/asm_386.s. 97 if i.crosscall2fn != nil && pc >= i.crosscall2fn.Entry && pc < i.crosscall2fn.End { 98 rule := fctxt.CFA 99 fctxt.CFA = rule 100 } 101 102 // We assume that EBP is the frame pointer and we want to keep it updated, 103 // so that we can use it to unwind the stack even when we encounter frames 104 // without descriptor entries. 105 // If there isn't i rule already we emit one. 106 if fctxt.Regs[regnum.I386_Ebp].Rule == frame.RuleUndefined { 107 fctxt.Regs[regnum.I386_Ebp] = frame.DWRule{ 108 Rule: frame.RuleFramePointer, 109 Reg: regnum.I386_Ebp, 110 Offset: 0, 111 } 112 } 113 114 return fctxt 115 } 116 117 // SwitchStack will use the current frame to determine if it's time to 118 func i386SwitchStack(it *stackIterator, _ *op.DwarfRegisters) bool { 119 if it.frame.Current.Fn == nil { 120 if it.systemstack && it.g != nil && it.top { 121 it.switchToGoroutineStack() 122 return true 123 } 124 return false 125 } 126 switch it.frame.Current.Fn.Name { 127 case "runtime.asmcgocall", "runtime.cgocallback_gofunc": // TODO(chainhelen), need to support cgo stacktraces. 128 return false 129 case "runtime.goexit", "runtime.rt0_go", "runtime.mcall": 130 // Look for "top of stack" functions. 131 it.atend = true 132 return true 133 134 case "runtime.mstart": 135 // Calls to runtime.systemstack will switch to the systemstack then: 136 // 1. alter the goroutine stack so that it looks like systemstack_switch 137 // was called 138 // 2. alter the system stack so that it looks like the bottom-most frame 139 // belongs to runtime.mstart 140 // If we find a runtime.mstart frame on the system stack of a goroutine 141 // parked on runtime.systemstack_switch we assume runtime.systemstack was 142 // called and continue tracing from the parked position. 143 144 if it.top || !it.systemstack || it.g == nil { 145 return false 146 } 147 if fn := it.bi.PCToFunc(it.g.PC); fn == nil || fn.Name != "runtime.systemstack_switch" { 148 return false 149 } 150 151 it.switchToGoroutineStack() 152 return true 153 154 default: 155 if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.throw" && it.frame.Current.Fn.Name != "runtime.fatalthrow" { 156 // The runtime switches to the system stack in multiple places. 157 // This usually happens through a call to runtime.systemstack but there 158 // are functions that switch to the system stack manually (for example 159 // runtime.morestack). 160 // Since we are only interested in printing the system stack for cgo 161 // calls we switch directly to the goroutine stack if we detect that the 162 // function at the top of the stack is a runtime function. 163 // 164 // The function "runtime.throw" is deliberately excluded from this 165 // because it can end up in the stack during a cgo call and switching to 166 // the goroutine stack will exclude all the C functions from the stack 167 // trace. 168 it.switchToGoroutineStack() 169 return true 170 } 171 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(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 }