170 lines
5.3 KiB
Go
170 lines
5.3 KiB
Go
package proc
|
|
|
|
import (
|
|
"encoding/binary"
|
|
|
|
"github.com/go-delve/delve/pkg/dwarf/op"
|
|
"github.com/go-delve/delve/pkg/dwarf/regnum"
|
|
"golang.org/x/arch/ppc64/ppc64asm"
|
|
)
|
|
|
|
// Possible stacksplit prologues are inserted by stacksplit in
|
|
// $GOROOT/src/cmd/internal/obj/ppc64/obj9.go.
|
|
var prologuesPPC64LE []opcodeSeq
|
|
|
|
func init() {
|
|
// Note: these will be the gnu opcodes and not the Go opcodes. Verify the sequences are as expected.
|
|
var tinyStacksplit = opcodeSeq{uint64(ppc64asm.ADDI), uint64(ppc64asm.CMPLD), uint64(ppc64asm.BC)}
|
|
var smallStacksplit = opcodeSeq{uint64(ppc64asm.ADDI), uint64(ppc64asm.CMPLD), uint64(ppc64asm.BC)}
|
|
var bigStacksplit = opcodeSeq{uint64(ppc64asm.ADDI), uint64(ppc64asm.CMPLD), uint64(ppc64asm.BC), uint64(ppc64asm.STD), uint64(ppc64asm.STD), uint64(ppc64asm.MFSPR)}
|
|
var adjustTOCPrologueOnPIE = opcodeSeq{uint64(ppc64asm.ADDIS), uint64(ppc64asm.ADDI)}
|
|
|
|
var unixGetG = opcodeSeq{uint64(ppc64asm.LD)}
|
|
var prologue opcodeSeq
|
|
prologuesPPC64LE = make([]opcodeSeq, 0, 3)
|
|
prologue = make(opcodeSeq, 0, 1)
|
|
for _, getG := range []opcodeSeq{unixGetG} {
|
|
for _, stacksplit := range []opcodeSeq{tinyStacksplit, smallStacksplit, bigStacksplit} {
|
|
prologue = append(prologue, getG...)
|
|
prologue = append(prologue, stacksplit...)
|
|
prologuesPPC64LE = append(prologuesPPC64LE, prologue)
|
|
}
|
|
}
|
|
// On PIE mode special prologue is generated two instructions before the function entry point that correlates to call target
|
|
// address, Teach delve to recognize this sequence to appropriately adjust PC address so that the breakpoint is actually hit
|
|
// while doing step
|
|
TOCprologue := make(opcodeSeq, 0, len(adjustTOCPrologueOnPIE))
|
|
TOCprologue = append(TOCprologue, adjustTOCPrologueOnPIE...)
|
|
prologuesPPC64LE = append(prologuesPPC64LE, TOCprologue)
|
|
}
|
|
|
|
func ppc64leAsmDecode(asmInst *AsmInstruction, mem []byte, regs *op.DwarfRegisters, memrw MemoryReadWriter, bi *BinaryInfo) error {
|
|
asmInst.Size = 4
|
|
asmInst.Bytes = mem[:asmInst.Size]
|
|
|
|
inst, err := ppc64asm.Decode(mem, binary.LittleEndian)
|
|
if err != nil {
|
|
asmInst.Inst = (*ppc64ArchInst)(nil)
|
|
return err
|
|
}
|
|
asmInst.Inst = (*ppc64ArchInst)(&inst)
|
|
asmInst.Kind = OtherInstruction
|
|
|
|
switch inst.Op {
|
|
case ppc64asm.BL, ppc64asm.BLA, ppc64asm.BCL, ppc64asm.BCLA, ppc64asm.BCLRL, ppc64asm.BCCTRL, ppc64asm.BCTARL:
|
|
// Pages 38-40 Book I v3.0
|
|
asmInst.Kind = CallInstruction
|
|
case ppc64asm.RFEBB, ppc64asm.RFID, ppc64asm.HRFID, ppc64asm.BCLR:
|
|
asmInst.Kind = RetInstruction
|
|
case ppc64asm.B, ppc64asm.BA, ppc64asm.BC, ppc64asm.BCA, ppc64asm.BCCTR, ppc64asm.BCTAR:
|
|
// Pages 38-40 Book I v3.0
|
|
asmInst.Kind = JmpInstruction
|
|
case ppc64asm.TD, ppc64asm.TDI, ppc64asm.TW, ppc64asm.TWI:
|
|
asmInst.Kind = HardBreakInstruction
|
|
}
|
|
|
|
asmInst.DestLoc = resolveCallArgPPC64LE(&inst, asmInst.Loc.PC, asmInst.AtPC, regs, memrw, bi)
|
|
return nil
|
|
}
|
|
|
|
func resolveCallArgPPC64LE(inst *ppc64asm.Inst, instAddr uint64, currentGoroutine bool, regs *op.DwarfRegisters, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
|
|
switch inst.Op {
|
|
case ppc64asm.BCLRL, ppc64asm.BCLR:
|
|
if regs != nil && regs.PC() == instAddr {
|
|
pc := regs.Reg(bininfo.Arch.LRRegNum).Uint64Val
|
|
file, line, fn := bininfo.PCToLine(pc)
|
|
if fn == nil {
|
|
return &Location{PC: pc}
|
|
}
|
|
return &Location{PC: pc, File: file, Line: line, Fn: fn}
|
|
}
|
|
return nil
|
|
case ppc64asm.B, ppc64asm.BL, ppc64asm.BLA, ppc64asm.BCL, ppc64asm.BCLA, ppc64asm.BCCTRL, ppc64asm.BCTARL:
|
|
// ok
|
|
default:
|
|
return nil
|
|
}
|
|
|
|
var pc uint64
|
|
var err error
|
|
|
|
switch arg := inst.Args[0].(type) {
|
|
case ppc64asm.Imm:
|
|
pc = uint64(arg)
|
|
case ppc64asm.Reg:
|
|
if !currentGoroutine || regs == nil {
|
|
return nil
|
|
}
|
|
pc, err = bininfo.Arch.getAsmRegister(regs, int(arg))
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
case ppc64asm.PCRel:
|
|
pc = instAddr + uint64(arg)
|
|
default:
|
|
return nil
|
|
}
|
|
|
|
file, line, fn := bininfo.PCToLine(pc)
|
|
if fn == nil {
|
|
return &Location{PC: pc}
|
|
}
|
|
return &Location{PC: pc, File: file, Line: line, Fn: fn}
|
|
}
|
|
|
|
type ppc64ArchInst ppc64asm.Inst
|
|
|
|
func (inst *ppc64ArchInst) Text(flavour AssemblyFlavour, pc uint64, symLookup func(uint64) (string, uint64)) string {
|
|
if inst == nil {
|
|
return "?"
|
|
}
|
|
|
|
var text string
|
|
|
|
switch flavour {
|
|
case GNUFlavour:
|
|
text = ppc64asm.GNUSyntax(ppc64asm.Inst(*inst), pc)
|
|
default:
|
|
text = ppc64asm.GoSyntax(ppc64asm.Inst(*inst), pc, symLookup)
|
|
}
|
|
|
|
return text
|
|
}
|
|
|
|
func (inst *ppc64ArchInst) OpcodeEquals(op uint64) bool {
|
|
if inst == nil {
|
|
return false
|
|
}
|
|
return uint64(inst.Op) == op
|
|
}
|
|
|
|
var ppc64leAsmRegisters = func() map[int]asmRegister {
|
|
r := make(map[int]asmRegister)
|
|
|
|
// General Purpose Registers: from R0 to R31
|
|
for i := ppc64asm.R0; i <= ppc64asm.R31; i++ {
|
|
r[int(i)] = asmRegister{regnum.PPC64LE_R0 + uint64(i-ppc64asm.R0), 0, 0}
|
|
}
|
|
|
|
// Floating point registers: from F0 to F31
|
|
for i := ppc64asm.F0; i <= ppc64asm.F31; i++ {
|
|
r[int(i)] = asmRegister{regnum.PPC64LE_F0 + uint64(i-ppc64asm.F0), 0, 0}
|
|
}
|
|
|
|
// Vector (Altivec/VMX) registers: from V0 to V31
|
|
for i := ppc64asm.V0; i <= ppc64asm.V31; i++ {
|
|
r[int(i)] = asmRegister{regnum.PPC64LE_V0 + uint64(i-ppc64asm.V0), 0, 0}
|
|
}
|
|
|
|
// Vector Scalar (VSX) registers: from VS0 to VS63
|
|
for i := ppc64asm.VS0; i <= ppc64asm.VS63; i++ {
|
|
r[int(i)] = asmRegister{regnum.PPC64LE_VS0 + uint64(i-ppc64asm.VS0), 0, 0}
|
|
}
|
|
|
|
// Condition Registers: from CR0 to CR7
|
|
for i := ppc64asm.CR0; i <= ppc64asm.CR7; i++ {
|
|
r[int(i)] = asmRegister{regnum.PPC64LE_CR0 + uint64(i-ppc64asm.CR0), 0, 0}
|
|
}
|
|
return r
|
|
}()
|