delve/pkg/proc/winutil/regs_arm64_arch.go
Quim Muntal 4455d6a9ef
Add support for windows/arm64 (#3063)
* Add support for windows/arm64

* split sentinel files and add winarm64 experiment

* update loadBinaryInfoPE to support PIE binaries

* skip TestDump on windows/arm64

* run windows/arm64 compilation on windows/amd64

* add entry point check for pie binaries

* delete unusded code

* document windows/arm64 breakpoint

* implement changing windows/arm64 fp registers

* update crosscall offset names

* fix G load when using CGO

* fix testvariablescgo

* remove DerefGStructOffset

* derefrence gstructoffset in GStructOffset() if necessary
2022-09-21 13:39:44 -07:00

187 lines
4.7 KiB
Go

package winutil
import (
"bytes"
"encoding/binary"
"fmt"
"unsafe"
"github.com/go-delve/delve/pkg/dwarf/op"
"github.com/go-delve/delve/pkg/dwarf/regnum"
"github.com/go-delve/delve/pkg/proc"
)
const (
ARM64_MAX_BREAKPOINTS = 8
ARM64_MAX_WATCHPOINTS = 2
)
// neon128 tracks the neon128 windows struct.
type neon128 struct {
Low uint64
High int64
}
// ARM64Registers represents CPU registers on an ARM64 processor.
type ARM64Registers struct {
iscgo bool
Regs [31]uint64
Sp uint64
Pc uint64
FloatRegisters [32]neon128
Fpcr uint32
Fpsr uint32
Bcr [ARM64_MAX_BREAKPOINTS]uint32
Bvr [ARM64_MAX_BREAKPOINTS]uint64
Wcr [ARM64_MAX_WATCHPOINTS]uint32
Wvr [ARM64_MAX_WATCHPOINTS]uint64
Context *ARM64CONTEXT
}
// NewARM64Registers creates a new ARM64Registers struct from a CONTEXT.
func NewARM64Registers(context *ARM64CONTEXT, iscgo bool) *ARM64Registers {
regs := &ARM64Registers{
iscgo: iscgo,
Regs: context.Regs,
Sp: context.Sp,
Pc: context.Pc,
FloatRegisters: context.FloatRegisters,
Fpcr: context.Fpcr,
Fpsr: context.Fpsr,
Bcr: context.Bcr,
Bvr: context.Bvr,
Wcr: context.Wcr,
Wvr: context.Wvr,
Context: context,
}
return regs
}
// Slice returns the registers as a list of (name, value) pairs.
func (r *ARM64Registers) Slice(floatingPoint bool) ([]proc.Register, error) {
out := make([]proc.Register, 0, len(r.Regs)+len(r.FloatRegisters)+2)
for i, v := range r.Regs {
out = proc.AppendUint64Register(out, fmt.Sprintf("X%d", i), v)
}
out = proc.AppendUint64Register(out, "SP", r.Sp)
out = proc.AppendUint64Register(out, "PC", r.Pc)
if floatingPoint {
for i := range r.FloatRegisters {
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, r.FloatRegisters[i].Low)
binary.Write(&buf, binary.LittleEndian, r.FloatRegisters[i].High)
out = proc.AppendBytesRegister(out, fmt.Sprintf("V%d", i), buf.Bytes())
}
out = proc.AppendUint64Register(out, "Fpcr", uint64(r.Fpcr))
out = proc.AppendUint64Register(out, "Fpsr", uint64(r.Fpsr))
}
return out, nil
}
// PC returns the value of RIP register.
func (r *ARM64Registers) PC() uint64 {
return r.Pc
}
// SP returns the value of RSP register.
func (r *ARM64Registers) SP() uint64 {
return r.Sp
}
func (r *ARM64Registers) BP() uint64 {
return r.Regs[29]
}
// TLS returns the address of the thread local storage memory segment.
func (r *ARM64Registers) TLS() uint64 {
if !r.iscgo {
return 0
}
return r.Regs[18]
}
// GAddr returns the address of the G variable if it is known, 0 and false
// otherwise.
func (r *ARM64Registers) GAddr() (uint64, bool) {
return r.Regs[28], !r.iscgo
}
// LR returns the link register.
func (r *ARM64Registers) LR() uint64 {
return r.Regs[30]
}
// Copy returns a copy of these registers that is guaranteed not to change.
func (r *ARM64Registers) Copy() (proc.Registers, error) {
rr := *r
rr.Context = NewARM64CONTEXT()
*(rr.Context) = *(r.Context)
return &rr, nil
}
// ARM64CONTEXT tracks the _ARM64_NT_CONTEXT of windows.
type ARM64CONTEXT struct {
ContextFlags uint32
Cpsr uint32
Regs [31]uint64
Sp uint64
Pc uint64
FloatRegisters [32]neon128
Fpcr uint32
Fpsr uint32
Bcr [ARM64_MAX_BREAKPOINTS]uint32
Bvr [ARM64_MAX_BREAKPOINTS]uint64
Wcr [ARM64_MAX_WATCHPOINTS]uint32
Wvr [ARM64_MAX_WATCHPOINTS]uint64
}
// NewARM64CONTEXT allocates Windows CONTEXT structure aligned to 16 bytes.
func NewARM64CONTEXT() *ARM64CONTEXT {
var c *ARM64CONTEXT
buf := make([]byte, unsafe.Sizeof(*c)+15)
return (*ARM64CONTEXT)(unsafe.Pointer((uintptr(unsafe.Pointer(&buf[15]))) &^ 15))
}
func (ctx *ARM64CONTEXT) SetFlags(flags uint32) {
ctx.ContextFlags = flags
}
func (ctx *ARM64CONTEXT) SetPC(pc uint64) {
ctx.Pc = pc
}
func (ctx *ARM64CONTEXT) SetTrap(trap bool) {
const v = 0x200000
if trap {
ctx.Cpsr |= v
} else {
ctx.Cpsr &= ^uint32(v)
}
}
func (ctx *ARM64CONTEXT) SetReg(regNum uint64, reg *op.DwarfRegister) error {
switch regNum {
case regnum.ARM64_PC:
ctx.Pc = reg.Uint64Val
return nil
case regnum.ARM64_SP:
ctx.Sp = reg.Uint64Val
return nil
default:
switch {
case regNum >= regnum.ARM64_X0 && regNum <= regnum.ARM64_X0+30:
ctx.Regs[regNum-regnum.ARM64_X0] = reg.Uint64Val
return nil
case regNum >= regnum.ARM64_V0 && regNum <= regnum.ARM64_V0+30:
i := regNum - regnum.ARM64_V0
ctx.FloatRegisters[i].Low = reg.Uint64Val
ctx.FloatRegisters[i].High = 0
return nil
default:
return fmt.Errorf("changing register %d not implemented", regNum)
}
}
}