gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/proc/ppc64le_arch.go (about) 1 package proc 2 3 import ( 4 "encoding/binary" 5 "fmt" 6 "strings" 7 8 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/frame" 9 10 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/op" 11 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/regnum" 12 ) 13 14 // This is the unconditional trap, the same mnemonic that both clang and gcc use 15 // It's documented in Section C.6 Trap Mnemonics in the Power ISA Book 3 16 var ppc64leBreakInstruction = []byte{0x08, 0x00, 0xe0, 0x7f} 17 18 func PPC64LEArch(goos string) *Arch { 19 return &Arch{ 20 Name: "ppc64le", 21 ptrSize: 8, 22 maxInstructionLength: 4, 23 breakpointInstruction: ppc64leBreakInstruction, 24 breakInstrMovesPC: false, 25 derefTLS: false, // Chapter 3.7 of the ELF V2 ABI Specification 26 prologues: prologuesPPC64LE, 27 fixFrameUnwindContext: ppc64leFixFrameUnwindContext, 28 switchStack: ppc64leSwitchStack, 29 regSize: ppc64leRegSize, 30 RegistersToDwarfRegisters: ppc64leRegistersToDwarfRegisters, 31 addrAndStackRegsToDwarfRegisters: ppc64leAddrAndStackRegsToDwarfRegisters, 32 DwarfRegisterToString: ppc64leDwarfRegisterToString, 33 inhibitStepInto: func(*BinaryInfo, uint64) bool { return false }, 34 asmDecode: ppc64leAsmDecode, 35 usesLR: true, 36 PCRegNum: regnum.PPC64LE_PC, 37 SPRegNum: regnum.PPC64LE_SP, 38 ContextRegNum: regnum.PPC64LE_R0 + 11, 39 LRRegNum: regnum.PPC64LE_LR, 40 asmRegisters: ppc64leAsmRegisters, 41 RegisterNameToDwarf: nameToDwarfFunc(regnum.PPC64LENameToDwarf), 42 debugCallMinStackSize: 320, 43 maxRegArgBytes: 13*8 + 13*8, 44 argumentRegs: []int{regnum.PPC64LE_R0 + 3, regnum.PPC64LE_R0 + 4, regnum.PPC64LE_R0 + 5}, 45 } 46 } 47 48 func ppc64leFixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext { 49 a := bi.Arch 50 if a.sigreturnfn == nil { 51 a.sigreturnfn = bi.lookupOneFunc("runtime.sigreturn") 52 } 53 if fctxt == nil || (a.sigreturnfn != nil && pc >= a.sigreturnfn.Entry && pc < a.sigreturnfn.End) { 54 return &frame.FrameContext{ 55 RetAddrReg: regnum.PPC64LE_LR, 56 Regs: map[uint64]frame.DWRule{ 57 regnum.PPC64LE_PC: { 58 Rule: frame.RuleOffset, 59 Offset: int64(-a.PtrSize()), 60 }, 61 regnum.PPC64LE_LR: { 62 Rule: frame.RuleOffset, 63 Offset: int64(-2 * a.PtrSize()), 64 }, 65 regnum.PPC64LE_SP: { 66 Rule: frame.RuleValOffset, 67 Offset: 0, 68 }, 69 }, 70 CFA: frame.DWRule{ 71 Rule: frame.RuleCFA, 72 Reg: regnum.PPC64LE_SP, 73 Offset: int64(2 * a.PtrSize()), 74 }, 75 } 76 } 77 if a.crosscall2fn == nil { 78 // This is used to fix issues with the c calling frames 79 a.crosscall2fn = bi.lookupOneFunc("crosscall2") 80 } 81 82 // Checks if we marked the function as a crosscall and if we are currently in it 83 if a.crosscall2fn != nil && pc >= a.crosscall2fn.Entry && pc < a.crosscall2fn.End { 84 rule := fctxt.CFA 85 if rule.Offset == crosscall2SPOffsetBad { 86 // Linux support only 87 rule.Offset += crosscall2SPOffsetLinuxPPC64LE 88 } 89 fctxt.CFA = rule 90 } 91 if fctxt.Regs[regnum.PPC64LE_LR].Rule == frame.RuleUndefined { 92 fctxt.Regs[regnum.PPC64LE_LR] = frame.DWRule{ 93 Rule: frame.RuleFramePointer, 94 Reg: regnum.PPC64LE_LR, 95 Offset: 0, 96 } 97 } 98 return fctxt 99 } 100 101 const ppc64cgocallSPOffsetSaveSlot = 32 102 const ppc64prevG0schedSPOffsetSaveSlot = 40 103 104 func ppc64leSwitchStack(it *stackIterator, callFrameRegs *op.DwarfRegisters) bool { 105 if it.frame.Current.Fn == nil && it.systemstack && it.g != nil && it.top { 106 it.switchToGoroutineStack() 107 return true 108 } 109 if it.frame.Current.Fn != nil { 110 switch it.frame.Current.Fn.Name { 111 case "runtime.asmcgocall", "runtime.cgocallback_gofunc", "runtime.sigpanic", "runtime.cgocallback": 112 //do nothing 113 case "runtime.goexit", "runtime.rt0_go": 114 // Look for "top of stack" functions. 115 it.atend = true 116 return true 117 case "runtime.mcall": 118 if it.systemstack && it.g != nil { 119 it.switchToGoroutineStack() 120 return true 121 } 122 it.atend = true 123 return true 124 case "crosscall2": 125 //The offsets get from runtime/cgo/asm_ppc64x.s:10 126 newsp, _ := readUintRaw(it.mem, it.regs.SP()+8*24, int64(it.bi.Arch.PtrSize())) 127 newbp, _ := readUintRaw(it.mem, it.regs.SP()+8*14, int64(it.bi.Arch.PtrSize())) 128 newlr, _ := readUintRaw(it.mem, it.regs.SP()+16, int64(it.bi.Arch.PtrSize())) 129 if it.regs.Reg(it.regs.BPRegNum) != nil { 130 it.regs.Reg(it.regs.BPRegNum).Uint64Val = newbp 131 } else { 132 reg, _ := it.readRegisterAt(it.regs.BPRegNum, it.regs.SP()+8*14) 133 it.regs.AddReg(it.regs.BPRegNum, reg) 134 } 135 it.regs.Reg(it.regs.LRRegNum).Uint64Val = newlr 136 it.regs.Reg(it.regs.SPRegNum).Uint64Val = newsp 137 it.pc = newlr 138 return true 139 default: 140 if it.systemstack && it.top && it.g != nil && strings.HasPrefix(it.frame.Current.Fn.Name, "runtime.") && it.frame.Current.Fn.Name != "runtime.fatalthrow" { 141 // The runtime switches to the system stack in multiple places. 142 // This usually happens through a call to runtime.systemstack but there 143 // are functions that switch to the system stack manually (for example 144 // runtime.morestack). 145 // Since we are only interested in printing the system stack for cgo 146 // calls we switch directly to the goroutine stack if we detect that the 147 // function at the top of the stack is a runtime function. 148 it.switchToGoroutineStack() 149 return true 150 } 151 } 152 } 153 fn := it.bi.PCToFunc(it.frame.Ret) 154 if fn == nil { 155 return false 156 } 157 switch fn.Name { 158 case "runtime.asmcgocall": 159 if !it.systemstack { 160 return false 161 } 162 163 // This function is called by a goroutine to execute a C function and 164 // switches from the goroutine stack to the system stack. 165 // Since we are unwinding the stack from callee to caller we have to switch 166 // from the system stack to the goroutine stack. 167 off, _ := readIntRaw(it.mem, 168 callFrameRegs.SP()+ppc64cgocallSPOffsetSaveSlot, 169 int64(it.bi.Arch.PtrSize())) 170 oldsp := callFrameRegs.SP() 171 newsp := uint64(int64(it.stackhi) - off) 172 173 // runtime.asmcgocall can also be called from inside the system stack, 174 // in that case no stack switch actually happens 175 if newsp == oldsp { 176 return false 177 } 178 it.systemstack = false 179 callFrameRegs.Reg(callFrameRegs.SPRegNum).Uint64Val = uint64(int64(newsp)) 180 return false 181 182 case "runtime.cgocallback_gofunc", "runtime.cgocallback": 183 // For a detailed description of how this works read the long comment at 184 // the start of $GOROOT/src/runtime/cgocall.go and the source code of 185 // runtime.cgocallback_gofunc in $GOROOT/src/runtime/asm_ppc64.s 186 // 187 // When a C functions calls back into go it will eventually call into 188 // runtime.cgocallback_gofunc which is the function that does the stack 189 // switch from the system stack back into the goroutine stack 190 // Since we are going backwards on the stack here we see the transition 191 // as goroutine stack -> system stack. 192 if it.systemstack { 193 return false 194 } 195 196 it.loadG0SchedSP() 197 if it.g0_sched_sp <= 0 { 198 return false 199 } 200 // entering the system stack 201 callFrameRegs.Reg(callFrameRegs.SPRegNum).Uint64Val = it.g0_sched_sp 202 // reads the previous value of g0.sched.sp that runtime.cgocallback_gofunc saved on the stack 203 204 // TODO: is this save slot correct? 205 it.g0_sched_sp, _ = readUintRaw(it.mem, callFrameRegs.SP()+ppc64prevG0schedSPOffsetSaveSlot, int64(it.bi.Arch.PtrSize())) 206 it.systemstack = true 207 return false 208 } 209 210 return false 211 } 212 213 // ppc64leRegSize returns the size (in bytes) of register regnum. 214 func ppc64leRegSize(regnum uint64) int { 215 return 8 // each register is a 64-bit register 216 } 217 218 func ppc64leRegistersToDwarfRegisters(staticBase uint64, regs Registers) *op.DwarfRegisters { 219 dregs := initDwarfRegistersFromSlice(int(regnum.PPC64LEMaxRegNum()), regs, regnum.PPC64LENameToDwarf) 220 dr := op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.PPC64LE_PC, regnum.PPC64LE_SP, regnum.PPC64LE_SP, regnum.PPC64LE_LR) 221 dr.SetLoadMoreCallback(loadMoreDwarfRegistersFromSliceFunc(dr, regs, regnum.PPC64LENameToDwarf)) 222 return dr 223 } 224 225 func ppc64leAddrAndStackRegsToDwarfRegisters(staticBase, pc, sp, bp, lr uint64) op.DwarfRegisters { 226 dregs := make([]*op.DwarfRegister, regnum.PPC64LE_LR+1) 227 dregs[regnum.PPC64LE_PC] = op.DwarfRegisterFromUint64(pc) 228 dregs[regnum.PPC64LE_SP] = op.DwarfRegisterFromUint64(sp) 229 dregs[regnum.PPC64LE_LR] = op.DwarfRegisterFromUint64(lr) 230 231 return *op.NewDwarfRegisters(staticBase, dregs, binary.LittleEndian, regnum.PPC64LE_PC, regnum.PPC64LE_SP, 0, regnum.PPC64LE_LR) 232 } 233 234 func ppc64leDwarfRegisterToString(i int, reg *op.DwarfRegister) (name string, floatingPoint bool, repr string) { 235 name = regnum.PPC64LEToName(uint64(i)) 236 237 if reg == nil { 238 return name, false, "" 239 } 240 241 if reg.Bytes == nil || (reg.Bytes != nil && len(reg.Bytes) < 16) { 242 return name, false, fmt.Sprintf("%#016x", reg.Uint64Val) 243 } 244 return name, true, fmt.Sprintf("%#x", reg.Bytes) 245 }