132 lines
3.5 KiB
Go
132 lines
3.5 KiB
Go
// TODO: disassembler support should be compiled in unconditionally,
|
|
// instead of being decided by the build-target architecture, and be
|
|
// part of the Arch object instead.
|
|
|
|
package proc
|
|
|
|
import (
|
|
"github.com/go-delve/delve/pkg/dwarf/op"
|
|
"github.com/go-delve/delve/pkg/dwarf/regnum"
|
|
|
|
"golang.org/x/arch/arm64/arm64asm"
|
|
)
|
|
|
|
func arm64AsmDecode(asmInst *AsmInstruction, mem []byte, regs *op.DwarfRegisters, memrw MemoryReadWriter, bi *BinaryInfo) error {
|
|
asmInst.Size = 4
|
|
asmInst.Bytes = mem[:asmInst.Size]
|
|
|
|
inst, err := arm64asm.Decode(mem)
|
|
if err != nil {
|
|
asmInst.Inst = (*arm64ArchInst)(nil)
|
|
return err
|
|
}
|
|
|
|
asmInst.Inst = (*arm64ArchInst)(&inst)
|
|
asmInst.Kind = OtherInstruction
|
|
|
|
switch inst.Op {
|
|
case arm64asm.BL, arm64asm.BLR:
|
|
asmInst.Kind = CallInstruction
|
|
case arm64asm.RET, arm64asm.ERET:
|
|
asmInst.Kind = RetInstruction
|
|
case arm64asm.B, arm64asm.BR:
|
|
asmInst.Kind = JmpInstruction
|
|
case arm64asm.BRK:
|
|
asmInst.Kind = HardBreakInstruction
|
|
}
|
|
|
|
asmInst.DestLoc = resolveCallArgARM64(&inst, asmInst.Loc.PC, asmInst.AtPC, regs, memrw, bi)
|
|
|
|
return nil
|
|
}
|
|
|
|
func resolveCallArgARM64(inst *arm64asm.Inst, instAddr uint64, currentGoroutine bool, regs *op.DwarfRegisters, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
|
|
switch inst.Op {
|
|
case arm64asm.BL, arm64asm.BLR, arm64asm.B, arm64asm.BR:
|
|
// ok
|
|
default:
|
|
return nil
|
|
}
|
|
|
|
var pc uint64
|
|
var err error
|
|
|
|
switch arg := inst.Args[0].(type) {
|
|
case arm64asm.Imm:
|
|
pc = uint64(arg.Imm)
|
|
case arm64asm.Reg:
|
|
if !currentGoroutine || regs == nil {
|
|
return nil
|
|
}
|
|
pc, err = bininfo.Arch.getAsmRegister(regs, int(arg))
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
case arm64asm.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}
|
|
}
|
|
|
|
// Possible stacksplit prologues are inserted by stacksplit in
|
|
// $GOROOT/src/cmd/internal/obj/arm64/obj7.go.
|
|
var prologuesARM64 []opcodeSeq
|
|
|
|
func init() {
|
|
var tinyStacksplit = opcodeSeq{uint64(arm64asm.MOV), uint64(arm64asm.CMP), uint64(arm64asm.B)}
|
|
var smallStacksplit = opcodeSeq{uint64(arm64asm.SUB), uint64(arm64asm.CMP), uint64(arm64asm.B)}
|
|
var bigStacksplit = opcodeSeq{uint64(arm64asm.CMP), uint64(arm64asm.B), uint64(arm64asm.ADD), uint64(arm64asm.SUB), uint64(arm64asm.MOV), uint64(arm64asm.CMP), uint64(arm64asm.B)}
|
|
var unixGetG = opcodeSeq{uint64(arm64asm.LDR)}
|
|
|
|
prologuesARM64 = make([]opcodeSeq, 0, 3)
|
|
for _, getG := range []opcodeSeq{unixGetG} {
|
|
for _, stacksplit := range []opcodeSeq{tinyStacksplit, smallStacksplit, bigStacksplit} {
|
|
prologue := make(opcodeSeq, 0, len(getG)+len(stacksplit))
|
|
prologue = append(prologue, getG...)
|
|
prologue = append(prologue, stacksplit...)
|
|
prologuesARM64 = append(prologuesARM64, prologue)
|
|
}
|
|
}
|
|
}
|
|
|
|
type arm64ArchInst arm64asm.Inst
|
|
|
|
func (inst *arm64ArchInst) Text(flavour AssemblyFlavour, pc uint64, symLookup func(uint64) (string, uint64)) string {
|
|
if inst == nil {
|
|
return "?"
|
|
}
|
|
|
|
var text string
|
|
|
|
switch flavour {
|
|
case GNUFlavour:
|
|
text = arm64asm.GNUSyntax(arm64asm.Inst(*inst))
|
|
default:
|
|
text = arm64asm.GoSyntax(arm64asm.Inst(*inst), pc, symLookup, nil)
|
|
}
|
|
|
|
return text
|
|
}
|
|
|
|
func (inst *arm64ArchInst) OpcodeEquals(op uint64) bool {
|
|
if inst == nil {
|
|
return false
|
|
}
|
|
return uint64(inst.Op) == op
|
|
}
|
|
|
|
var arm64AsmRegisters = func() map[int]asmRegister {
|
|
r := make(map[int]asmRegister)
|
|
for i := arm64asm.X0; i <= arm64asm.X30; i++ {
|
|
r[int(i)] = asmRegister{regnum.ARM64_X0 + uint64(i-arm64asm.X0), 0, 0}
|
|
}
|
|
return r
|
|
}()
|