
* 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
187 lines
4.7 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|