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  }