delve/pkg/proc/arm64_disasm.go
2023-11-14 16:36:55 +01:00

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
}()