mirror of
https://github.com/go-delve/delve.git
synced 2025-10-30 10:17:03 +08:00
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
This commit is contained in:
@ -7,8 +7,12 @@
|
||||
#elif __i386__
|
||||
#define BREAKPOINT asm("int3;")
|
||||
#elif __aarch64__
|
||||
#ifdef WIN32
|
||||
#define BREAKPOINT asm("brk 0xF000;")
|
||||
#else
|
||||
#define BREAKPOINT asm("brk 0;")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define N 100
|
||||
|
||||
|
||||
@ -77,3 +77,12 @@ $x = $LastExitCode
|
||||
if ($version -ne "gotip") {
|
||||
Exit $x
|
||||
}
|
||||
|
||||
# TODO: Remove once we have a windows/arm64 builder.
|
||||
# Test windows/arm64 compiles.
|
||||
$env:GOARCH = "arm64"
|
||||
go run _scripts/make.go build --tags exp.winarm64
|
||||
$x = $LastExitCode
|
||||
if ($version -ne "gotip") {
|
||||
Exit $x
|
||||
}
|
||||
|
||||
@ -104,9 +104,9 @@ func amd64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
|
||||
if rule.Offset == crosscall2SPOffsetBad {
|
||||
switch bi.GOOS {
|
||||
case "windows":
|
||||
rule.Offset += crosscall2SPOffsetWindows
|
||||
rule.Offset += crosscall2SPOffsetWindowsAMD64
|
||||
default:
|
||||
rule.Offset += crosscall2SPOffsetNonWindows
|
||||
rule.Offset += crosscall2SPOffset
|
||||
}
|
||||
}
|
||||
fctxt.CFA = rule
|
||||
|
||||
@ -150,6 +150,6 @@ func nameToDwarfFunc(n2d map[string]int) func(string) (int, bool) {
|
||||
// crosscall2 is defined in $GOROOT/src/runtime/cgo/asm_amd64.s.
|
||||
const (
|
||||
crosscall2SPOffsetBad = 0x8
|
||||
crosscall2SPOffsetWindows = 0x118
|
||||
crosscall2SPOffsetNonWindows = 0x58
|
||||
crosscall2SPOffsetWindowsAMD64 = 0x118
|
||||
crosscall2SPOffset = 0x58
|
||||
)
|
||||
|
||||
@ -14,15 +14,25 @@ import (
|
||||
|
||||
var arm64BreakInstruction = []byte{0x0, 0x0, 0x20, 0xd4}
|
||||
|
||||
// Windows ARM64 expects a breakpoint to be compiled to the instruction BRK #0xF000.
|
||||
// See go.dev/issues/53837.
|
||||
var arm64WindowsBreakInstruction = []byte{0x0, 0x0, 0x3e, 0xd4}
|
||||
|
||||
// ARM64Arch returns an initialized ARM64
|
||||
// struct.
|
||||
func ARM64Arch(goos string) *Arch {
|
||||
var brk []byte
|
||||
if goos == "windows" {
|
||||
brk = arm64WindowsBreakInstruction
|
||||
} else {
|
||||
brk = arm64BreakInstruction
|
||||
}
|
||||
return &Arch{
|
||||
Name: "arm64",
|
||||
ptrSize: 8,
|
||||
maxInstructionLength: 4,
|
||||
breakpointInstruction: arm64BreakInstruction,
|
||||
breakInstrMovesPC: false,
|
||||
breakpointInstruction: brk,
|
||||
breakInstrMovesPC: goos == "windows",
|
||||
derefTLS: false,
|
||||
prologues: prologuesARM64,
|
||||
fixFrameUnwindContext: arm64FixFrameUnwindContext,
|
||||
@ -102,12 +112,7 @@ func arm64FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *Binary
|
||||
if a.crosscall2fn != nil && pc >= a.crosscall2fn.Entry && pc < a.crosscall2fn.End {
|
||||
rule := fctxt.CFA
|
||||
if rule.Offset == crosscall2SPOffsetBad {
|
||||
switch bi.GOOS {
|
||||
case "windows":
|
||||
rule.Offset += crosscall2SPOffsetWindows
|
||||
default:
|
||||
rule.Offset += crosscall2SPOffsetNonWindows
|
||||
}
|
||||
rule.Offset += crosscall2SPOffset
|
||||
}
|
||||
fctxt.CFA = rule
|
||||
}
|
||||
|
||||
@ -137,6 +137,7 @@ var (
|
||||
|
||||
supportedWindowsArch = map[_PEMachine]bool{
|
||||
_IMAGE_FILE_MACHINE_AMD64: true,
|
||||
_IMAGE_FILE_MACHINE_ARM64: true,
|
||||
}
|
||||
|
||||
supportedDarwinArch = map[macho.Cpu]bool{
|
||||
@ -681,8 +682,18 @@ func loadBinaryInfo(bi *BinaryInfo, image *Image, path string, entryPoint uint64
|
||||
|
||||
// GStructOffset returns the offset of the G
|
||||
// struct in thread local storage.
|
||||
func (bi *BinaryInfo) GStructOffset() uint64 {
|
||||
return bi.gStructOffset
|
||||
func (bi *BinaryInfo) GStructOffset(mem MemoryReadWriter) (uint64, error) {
|
||||
offset := bi.gStructOffset
|
||||
if bi.GOOS == "windows" && bi.Arch.Name == "arm64" {
|
||||
// The G struct offset from the TLS section is a pointer
|
||||
// and the address must be dereferenced to find to actual G struct offset.
|
||||
var err error
|
||||
offset, err = readUintRaw(mem, offset, int64(bi.Arch.PtrSize()))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return offset, nil
|
||||
}
|
||||
|
||||
// LastModified returns the last modified time of the binary.
|
||||
@ -1589,8 +1600,6 @@ func loadBinaryInfoPE(bi *BinaryInfo, image *Image, path string, entryPoint uint
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//TODO(aarzilli): actually test this when Go supports PIE buildmode on Windows.
|
||||
opth := peFile.OptionalHeader.(*pe.OptionalHeader64)
|
||||
if entryPoint != 0 {
|
||||
image.StaticBase = entryPoint - opth.ImageBase
|
||||
@ -1618,13 +1627,38 @@ func loadBinaryInfoPE(bi *BinaryInfo, image *Image, path string, entryPoint uint
|
||||
wg.Add(2)
|
||||
go bi.parseDebugFramePE(image, peFile, debugInfoBytes, wg)
|
||||
go bi.loadDebugInfoMaps(image, debugInfoBytes, debugLineBytes, wg, nil)
|
||||
if image.index == 0 {
|
||||
// determine g struct offset only when loading the executable file
|
||||
wg.Add(1)
|
||||
go bi.setGStructOffsetPE(entryPoint, peFile, wg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bi *BinaryInfo) setGStructOffsetPE(entryPoint uint64, peFile *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
switch _PEMachine(peFile.Machine) {
|
||||
case _IMAGE_FILE_MACHINE_AMD64:
|
||||
// Use ArbitraryUserPointer (0x28) as pointer to pointer
|
||||
// to G struct per:
|
||||
// https://golang.org/src/runtime/cgo/gcc_windows_amd64.c
|
||||
|
||||
bi.gStructOffset = 0x28
|
||||
return nil
|
||||
case _IMAGE_FILE_MACHINE_ARM64:
|
||||
// Use runtime.tls_g as pointer to offset from R18 to G struct:
|
||||
// https://golang.org/src/runtime/sys_windows_arm64.s:runtime·wintls
|
||||
for _, s := range peFile.Symbols {
|
||||
if s.Name == "runtime.tls_g" {
|
||||
i := int(s.SectionNumber) - 1
|
||||
if 0 <= i && i < len(peFile.Sections) {
|
||||
sect := peFile.Sections[i]
|
||||
if s.Value < sect.VirtualSize {
|
||||
bi.gStructOffset = entryPoint + uint64(sect.VirtualAddress) + uint64(s.Value)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
|
||||
|
||||
@ -359,12 +359,14 @@ func (p *gdbProcess) Connect(conn net.Conn, path string, pid int, debugInfoDirs
|
||||
// store the MOV instruction.
|
||||
// If the stub doesn't support memory allocation reloadRegisters will
|
||||
// overwrite some existing memory to store the MOV.
|
||||
if ginstr, err := p.loadGInstr(); err == nil {
|
||||
if addr, err := p.conn.allocMemory(256); err == nil {
|
||||
if _, err := p.conn.writeMemory(addr, p.loadGInstr()); err == nil {
|
||||
if _, err := p.conn.writeMemory(addr, ginstr); err == nil {
|
||||
p.loadGInstrAddr = addr
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tgt, nil
|
||||
}
|
||||
@ -1553,7 +1555,7 @@ func (t *gdbThread) Blocked() bool {
|
||||
// loadGInstr returns the correct MOV instruction for the current
|
||||
// OS/architecture that can be executed to load the address of G from an
|
||||
// inferior's thread.
|
||||
func (p *gdbProcess) loadGInstr() []byte {
|
||||
func (p *gdbProcess) loadGInstr() ([]byte, error) {
|
||||
var op []byte
|
||||
switch p.bi.GOOS {
|
||||
case "windows", "darwin", "freebsd":
|
||||
@ -1565,10 +1567,14 @@ func (p *gdbProcess) loadGInstr() []byte {
|
||||
default:
|
||||
panic("unsupported operating system attempting to find Goroutine on Thread")
|
||||
}
|
||||
offset, err := p.bi.GStructOffset(p.Memory())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := &bytes.Buffer{}
|
||||
buf.Write(op)
|
||||
binary.Write(buf, binary.LittleEndian, uint32(p.bi.GStructOffset()))
|
||||
return buf.Bytes()
|
||||
binary.Write(buf, binary.LittleEndian, uint32(offset))
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (p *gdbProcess) MemoryMap() ([]proc.MemoryMapEntry, error) {
|
||||
@ -1727,7 +1733,10 @@ func (t *gdbThread) readSomeRegisters(regNames ...string) error {
|
||||
// the MOV instruction used to load current G, executes this single
|
||||
// instruction and then puts everything back the way it was.
|
||||
func (t *gdbThread) reloadGAtPC() error {
|
||||
movinstr := t.p.loadGInstr()
|
||||
movinstr, err := t.p.loadGInstr()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if t.Blocked() {
|
||||
t.regs.tls = 0
|
||||
@ -1760,7 +1769,7 @@ func (t *gdbThread) reloadGAtPC() error {
|
||||
}
|
||||
|
||||
savedcode := make([]byte, len(movinstr))
|
||||
_, err := t.p.ReadMemory(savedcode, pc)
|
||||
_, err = t.p.ReadMemory(savedcode, pc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//go:build (freebsd && amd64) || darwin
|
||||
// +build freebsd,amd64 darwin
|
||||
//go:build (freebsd && amd64) || darwin || (windows && arm64)
|
||||
// +build freebsd,amd64 darwin windows,arm64
|
||||
|
||||
package native
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//go:build (linux && 386) || (darwin && arm64)
|
||||
// +build linux,386 darwin,arm64
|
||||
//go:build (linux && 386) || (darwin && arm64) || (windows && arm64)
|
||||
// +build linux,386 darwin,arm64 windows,arm64
|
||||
|
||||
package native
|
||||
|
||||
|
||||
@ -726,8 +726,12 @@ func (dbp *nativeProcess) SetUProbe(fnName string, goidOffset int64, args []ebpf
|
||||
return fmt.Errorf("could not find function: %s", fnName)
|
||||
}
|
||||
|
||||
offset, err := dbp.BinInfo().GStructOffset(dbp.Memory())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
key := fn.Entry
|
||||
err := dbp.os.ebpf.UpdateArgMap(key, goidOffset, args, dbp.BinInfo().GStructOffset(), false)
|
||||
err = dbp.os.ebpf.UpdateArgMap(key, goidOffset, args, offset, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -762,7 +766,7 @@ func (dbp *nativeProcess) SetUProbe(fnName string, goidOffset int64, args []ebpf
|
||||
}
|
||||
addrs = append(addrs, proc.FindDeferReturnCalls(instructions)...)
|
||||
for _, addr := range addrs {
|
||||
err := dbp.os.ebpf.UpdateArgMap(addr, goidOffset, args, dbp.BinInfo().GStructOffset(), true)
|
||||
err := dbp.os.ebpf.UpdateArgMap(addr, goidOffset, args, offset, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-delve/delve/pkg/dwarf/op"
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
)
|
||||
import "github.com/go-delve/delve/pkg/dwarf/op"
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (thread *nativeThread) setPC(pc uint64) error {
|
||||
@ -39,21 +33,3 @@ func (thread *nativeThread) SetReg(regNum uint64, reg *op.DwarfRegister) error {
|
||||
|
||||
return thread.setContext(context)
|
||||
}
|
||||
|
||||
func registers(thread *nativeThread) (proc.Registers, error) {
|
||||
context := newContext()
|
||||
|
||||
context.SetFlags(_CONTEXT_ALL)
|
||||
err := thread.getContext(context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var threadInfo _THREAD_BASIC_INFORMATION
|
||||
status := _NtQueryInformationThread(thread.os.hThread, _ThreadBasicInformation, uintptr(unsafe.Pointer(&threadInfo)), uint32(unsafe.Sizeof(threadInfo)), nil)
|
||||
if !_NT_SUCCESS(status) {
|
||||
return nil, fmt.Errorf("NtQueryInformationThread failed: it returns 0x%x", status)
|
||||
}
|
||||
|
||||
return newRegisters(context, uint64(threadInfo.TebBaseAddress)), nil
|
||||
}
|
||||
|
||||
6
pkg/proc/native/support_sentinel darwin.go
Normal file
6
pkg/proc/native/support_sentinel darwin.go
Normal file
@ -0,0 +1,6 @@
|
||||
// This file is used to detect build on unsupported GOOS/GOARCH combinations.
|
||||
|
||||
//go:build darwin && !amd64 && !arm64
|
||||
// +build darwin,!amd64,!arm64
|
||||
|
||||
package your_darwin_architectur_is_not_supported_by_delve
|
||||
@ -1,6 +1,6 @@
|
||||
// This file is used to detect build on unsupported GOOS/GOARCH combinations.
|
||||
|
||||
//go:build (!linux && !darwin && !windows && !freebsd) || (linux && !amd64 && !arm64 && !386) || (darwin && !amd64 && !arm64) || (windows && !amd64) || (freebsd && !amd64)
|
||||
// +build !linux,!darwin,!windows,!freebsd linux,!amd64,!arm64,!386 darwin,!amd64,!arm64 windows,!amd64 freebsd,!amd64
|
||||
//go:build !linux && !darwin && !windows && !freebsd
|
||||
// +build !linux,!darwin,!windows,!freebsd
|
||||
|
||||
package your_operating_system_and_architecture_combination_is_not_supported_by_delve
|
||||
package your_operating_system_is_not_supported_by_delve
|
||||
|
||||
6
pkg/proc/native/support_sentinel_freebsd.go
Normal file
6
pkg/proc/native/support_sentinel_freebsd.go
Normal file
@ -0,0 +1,6 @@
|
||||
// This file is used to detect build on unsupported GOOS/GOARCH combinations.
|
||||
|
||||
//go:build freebsd && !amd64
|
||||
// +build freebsd,!amd64
|
||||
|
||||
package your_freebsd_architecture_is_not_supported_by_delve
|
||||
6
pkg/proc/native/support_sentinel_linux.go
Normal file
6
pkg/proc/native/support_sentinel_linux.go
Normal file
@ -0,0 +1,6 @@
|
||||
// This file is used to detect build on unsupported GOOS/GOARCH combinations.
|
||||
|
||||
//go:build linux && !amd64 && !arm64 && !386
|
||||
// +build linux,!amd64,!arm64,!386
|
||||
|
||||
package your_linux_architecture_is_not_supported_by_delve
|
||||
8
pkg/proc/native/support_sentinel_windows.go
Normal file
8
pkg/proc/native/support_sentinel_windows.go
Normal file
@ -0,0 +1,8 @@
|
||||
// This file is used to detect build on unsupported GOOS/GOARCH combinations.
|
||||
|
||||
//go:build windows && !amd64 && !(arm64 && exp.winarm64)
|
||||
// +build windows
|
||||
// +build !amd64
|
||||
// +build !arm64 !exp.winarm64
|
||||
|
||||
package your_windows_architecture_is_not_supported_by_delve
|
||||
23
pkg/proc/native/syscall_windows_arm64.go
Normal file
23
pkg/proc/native/syscall_windows_arm64.go
Normal file
@ -0,0 +1,23 @@
|
||||
package native
|
||||
|
||||
import "github.com/go-delve/delve/pkg/proc/winutil"
|
||||
|
||||
const (
|
||||
_CONTEXT_ARM64 = 0x00400000
|
||||
_CONTEXT_CONTROL = (_CONTEXT_ARM64 | 0x1)
|
||||
_CONTEXT_INTEGER = (_CONTEXT_ARM64 | 0x2)
|
||||
_CONTEXT_FLOATING_POINT = (_CONTEXT_ARM64 | 0x4)
|
||||
_CONTEXT_DEBUG_REGISTERS = (_CONTEXT_ARM64 | 0x8)
|
||||
_CONTEXT_ARM64_X18 = (_CONTEXT_ARM64 | 0x10)
|
||||
_CONTEXT_FULL = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_FLOATING_POINT)
|
||||
_CONTEXT_ALL = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_FLOATING_POINT | _CONTEXT_DEBUG_REGISTERS | _CONTEXT_ARM64_X18)
|
||||
_CONTEXT_EXCEPTION_ACTIVE = 0x8000000
|
||||
_CONTEXT_SERVICE_ACTIVE = 0x10000000
|
||||
_CONTEXT_EXCEPTION_REQUEST = 0x40000000
|
||||
_CONTEXT_EXCEPTION_REPORTING = 0x80000000
|
||||
)
|
||||
|
||||
// zsyscall_windows.go, an autogenerated file, wants to refer to the context
|
||||
// structure as _CONTEXT, but we need to have it in pkg/proc/winutil.CONTEXT
|
||||
// because it's also used on non-windows operating systems.
|
||||
type _CONTEXT = winutil.ARM64CONTEXT
|
||||
@ -2,6 +2,8 @@ package native
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
"github.com/go-delve/delve/pkg/proc/amd64util"
|
||||
@ -12,8 +14,22 @@ func newContext() *winutil.AMD64CONTEXT {
|
||||
return winutil.NewAMD64CONTEXT()
|
||||
}
|
||||
|
||||
func newRegisters(context *winutil.AMD64CONTEXT, TebBaseAddress uint64) *winutil.AMD64Registers {
|
||||
return winutil.NewAMD64Registers(context, TebBaseAddress)
|
||||
func registers(t *nativeThread) (proc.Registers, error) {
|
||||
context := newContext()
|
||||
|
||||
context.SetFlags(_CONTEXT_ALL)
|
||||
err := t.getContext(context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var threadInfo _THREAD_BASIC_INFORMATION
|
||||
status := _NtQueryInformationThread(t.os.hThread, _ThreadBasicInformation, uintptr(unsafe.Pointer(&threadInfo)), uint32(unsafe.Sizeof(threadInfo)), nil)
|
||||
if !_NT_SUCCESS(status) {
|
||||
return nil, fmt.Errorf("NtQueryInformationThread failed: it returns 0x%x", status)
|
||||
}
|
||||
|
||||
return winutil.NewAMD64Registers(context, uint64(threadInfo.TebBaseAddress)), nil
|
||||
}
|
||||
|
||||
func (t *nativeThread) setContext(context *winutil.AMD64CONTEXT) error {
|
||||
|
||||
38
pkg/proc/native/threads_windows_arm64.go
Normal file
38
pkg/proc/native/threads_windows_arm64.go
Normal file
@ -0,0 +1,38 @@
|
||||
package native
|
||||
|
||||
import (
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
"github.com/go-delve/delve/pkg/proc/winutil"
|
||||
)
|
||||
|
||||
func newContext() *winutil.ARM64CONTEXT {
|
||||
return winutil.NewARM64CONTEXT()
|
||||
}
|
||||
|
||||
func registers(t *nativeThread) (proc.Registers, error) {
|
||||
context := newContext()
|
||||
|
||||
context.SetFlags(_CONTEXT_ALL)
|
||||
err := t.getContext(context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return winutil.NewARM64Registers(context, t.dbp.iscgo), nil
|
||||
}
|
||||
|
||||
func newRegisters(context *winutil.ARM64CONTEXT, TebBaseAddress uint64, iscgo bool) *winutil.ARM64Registers {
|
||||
return winutil.NewARM64Registers(context, iscgo)
|
||||
}
|
||||
|
||||
func (t *nativeThread) setContext(context *winutil.ARM64CONTEXT) error {
|
||||
return _SetThreadContext(t.os.hThread, context)
|
||||
}
|
||||
|
||||
func (t *nativeThread) getContext(context *winutil.ARM64CONTEXT) error {
|
||||
return _GetThreadContext(t.os.hThread, context)
|
||||
}
|
||||
|
||||
func (t *nativeThread) restoreRegisters(savedRegs proc.Registers) error {
|
||||
return t.setContext(savedRegs.(*winutil.ARM64Registers).Context)
|
||||
}
|
||||
@ -112,7 +112,9 @@ func TestDwarfVersion(t *testing.T) {
|
||||
// Tests that we correctly read the version of compilation units
|
||||
fixture := protest.BuildFixture("math", 0)
|
||||
bi := NewBinaryInfo(runtime.GOOS, runtime.GOARCH)
|
||||
assertNoError(bi.LoadBinaryInfo(fixture.Path, 0, nil), t, "LoadBinaryInfo")
|
||||
// Use a fake entry point so LoadBinaryInfo does not error in case the binary is PIE.
|
||||
const fakeEntryPoint = 1
|
||||
assertNoError(bi.LoadBinaryInfo(fixture.Path, fakeEntryPoint, nil), t, "LoadBinaryInfo")
|
||||
for _, cu := range bi.Images[0].compileUnits {
|
||||
if cu.Version != 4 {
|
||||
t.Errorf("compile unit %q at %#x has bad version %d", cu.name, cu.entry.Offset, cu.Version)
|
||||
@ -127,7 +129,9 @@ func TestRegabiFlagSentinel(t *testing.T) {
|
||||
}
|
||||
fixture := protest.BuildFixture("math", 0)
|
||||
bi := NewBinaryInfo(runtime.GOOS, runtime.GOARCH)
|
||||
assertNoError(bi.LoadBinaryInfo(fixture.Path, 0, nil), t, "LoadBinaryInfo")
|
||||
// Use a fake entry point so LoadBinaryInfo does not error in case the binary is PIE.
|
||||
const fakeEntryPoint = 1
|
||||
assertNoError(bi.LoadBinaryInfo(fixture.Path, fakeEntryPoint, nil), t, "LoadBinaryInfo")
|
||||
if !bi.regabi {
|
||||
t.Errorf("regabi flag not set %s GOEXPERIMENT=%s", runtime.Version(), os.Getenv("GOEXPERIMENT"))
|
||||
}
|
||||
|
||||
@ -5234,7 +5234,7 @@ func TestIssue2319(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDump(t *testing.T) {
|
||||
if runtime.GOOS == "freebsd" || (runtime.GOOS == "darwin" && testBackend == "native") {
|
||||
if runtime.GOOS == "freebsd" || (runtime.GOOS == "darwin" && testBackend == "native") || (runtime.GOOS == "windows" && runtime.GOARCH != "amd64") {
|
||||
t.Skip("not supported")
|
||||
}
|
||||
|
||||
|
||||
@ -438,8 +438,12 @@ func getGVariable(thread Thread) (*Variable, error) {
|
||||
|
||||
gaddr, hasgaddr := regs.GAddr()
|
||||
if !hasgaddr {
|
||||
var err error
|
||||
gaddr, err = readUintRaw(thread.ProcessMemory(), regs.TLS()+thread.BinInfo().GStructOffset(), int64(thread.BinInfo().Arch.PtrSize()))
|
||||
bi := thread.BinInfo()
|
||||
offset, err := bi.GStructOffset(thread.ProcessMemory())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gaddr, err = readUintRaw(thread.ProcessMemory(), regs.TLS()+offset, int64(bi.Arch.PtrSize()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
186
pkg/proc/winutil/regs_arm64_arch.go
Normal file
186
pkg/proc/winutil/regs_arm64_arch.go
Normal file
@ -0,0 +1,186 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user