runtime: move Windows types and consts to internal/runtime/syscall/windows

This CL doesn't change any behavior, it just moves code around to reduce
the size of the runtime package and remove some duplicated symbols.

Updates #51087.

Cq-Include-Trybots: luci.golang.try:gotip-windows-arm64
Change-Id: I3d3e5f214f045c24fb5d4050d56e7b0822a6e4b5
Reviewed-on: https://go-review.googlesource.com/c/go/+/698098
Reviewed-by: Cherry Mui <cherryyz@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
qmuntal 2025-08-21 21:23:03 +02:00 committed by Quim Muntal
parent cc8a6780ac
commit 889e71c2ac
19 changed files with 681 additions and 658 deletions

View file

@ -0,0 +1,159 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Architecture-independent definitions.
package windows
// Pseudo handles.
const (
CurrentProcess = ^uintptr(0) // -1 = current process
CurrentThread = ^uintptr(1) // -2 = current thread
)
const INVALID_HANDLE_VALUE = ^uintptr(0)
const DWORD_MAX = 0xffffffff
const (
PROT_NONE = 0
PROT_READ = 1
PROT_WRITE = 2
PROT_EXEC = 4
)
const (
MAP_ANON = 1
MAP_PRIVATE = 2
)
const DUPLICATE_SAME_ACCESS = 0x2
const THREAD_PRIORITY_HIGHEST = 0x2
const (
SIGINT = 0x2
SIGTERM = 0xF
)
const (
CTRL_C_EVENT = 0x0
CTRL_BREAK_EVENT = 0x1
CTRL_CLOSE_EVENT = 0x2
CTRL_LOGOFF_EVENT = 0x5
CTRL_SHUTDOWN_EVENT = 0x6
)
const (
EXCEPTION_ACCESS_VIOLATION = 0xc0000005
EXCEPTION_IN_PAGE_ERROR = 0xc0000006
EXCEPTION_BREAKPOINT = 0x80000003
EXCEPTION_ILLEGAL_INSTRUCTION = 0xc000001d
EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d
EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e
EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f
EXCEPTION_FLT_OVERFLOW = 0xc0000091
EXCEPTION_FLT_UNDERFLOW = 0xc0000093
EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094
EXCEPTION_INT_OVERFLOW = 0xc0000095
)
const (
SEM_FAILCRITICALERRORS = 0x0001
SEM_NOGPFAULTERRORBOX = 0x0002
SEM_NOOPENFILEERRORBOX = 0x8000
)
const WER_FAULT_REPORTING_NO_UI = 0x0020
const INFINITE = 0xffffffff
const WAIT_TIMEOUT = 258
const FAIL_FAST_GENERATE_EXCEPTION_ADDRESS = 0x1
const (
EXCEPTION_CONTINUE_EXECUTION = -0x1
EXCEPTION_CONTINUE_SEARCH = 0x0
EXCEPTION_CONTINUE_SEARCH_SEH = 0x1
)
const CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002
const (
SYNCHRONIZE = 0x00100000
TIMER_QUERY_STATE = 0x0001
TIMER_MODIFY_STATE = 0x0002
)
// https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55
const (
STATUS_SUCCESS = 0x00000000
STATUS_PENDING = 0x00000103
STATUS_CANCELLED = 0xC0000120
)
// https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
type SystemInfo struct {
ProcessorArchitecture uint16
Reserved uint16
PageSize uint32
MinimumApplicationAddress *byte
MaximumApplicationAddress *byte
ActiveProcessorMask uintptr
NumberOfProcessors uint32
ProcessorType uint32
AllocationGranularity uint32
ProcessorLevel uint16
ProcessorRevision uint16
}
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_pointers
type ExceptionPointers struct {
Record *ExceptionRecord
Context *Context
}
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-exception_record
type ExceptionRecord struct {
ExceptionCode uint32
ExceptionFlags uint32
ExceptionRecord *ExceptionRecord
ExceptionAddress uintptr
NumberParameters uint32
ExceptionInformation [15]uintptr
}
type Handle uintptr
// https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-overlapped
type Overlapped struct {
Internal uintptr
InternalHigh uintptr
Offset uint32
OffsetHigh uint32
HEvent Handle
}
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-memory_basic_information
type MemoryBasicInformation struct {
BaseAddress uintptr
AllocationBase uintptr
AllocationProtect uint32
PartitionId uint16
RegionSize uintptr
State uint32
Protect uint32
Type uint32
}
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfow
type OSVERSIONINFOW struct {
OSVersionInfoSize uint32
MajorVersion uint32
MinorVersion uint32
BuildNumber uint32
PlatformID uint32
CSDVersion [128]uint16
}

View file

@ -0,0 +1,79 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package windows
import (
"internal/goarch"
"unsafe"
)
const CONTEXT_CONTROL = 0x10001
type FloatingSaveArea struct {
ControlWord uint32
StatusWord uint32
TagWord uint32
ErrorOffset uint32
ErrorSelector uint32
DataOffset uint32
DataSelector uint32
RegisterArea [80]uint8
Cr0NpxState uint32
}
type Context struct {
ContextFlags uint32
Dr0 uint32
Dr1 uint32
Dr2 uint32
Dr3 uint32
Dr6 uint32
Dr7 uint32
FloatingSaveArea FloatingSaveArea
SegGs uint32
SegFs uint32
SegEs uint32
SegDs uint32
Edi uint32
Esi uint32
Ebx uint32
Edx uint32
Ecx uint32
Eax uint32
Ebp uint32
Eip uint32
SegCs uint32
EFlags uint32
Esp uint32
SegSs uint32
ExtendedRegisters [512]uint8
}
func (c *Context) PC() uintptr { return uintptr(c.Eip) }
func (c *Context) SP() uintptr { return uintptr(c.Esp) }
// 386 does not have link register, so this returns 0.
func (c *Context) LR() uintptr { return 0 }
func (c *Context) SetLR(x uintptr) {}
func (c *Context) SetPC(x uintptr) { c.Eip = uint32(x) }
func (c *Context) SetSP(x uintptr) { c.Esp = uint32(x) }
// 386 does not have frame pointer register.
func (c *Context) SetFP(x uintptr) {}
func (c *Context) PushCall(targetPC, resumePC uintptr) {
sp := c.SP() - goarch.StackAlign
*(*uintptr)(unsafe.Pointer(sp)) = resumePC
c.SetSP(sp)
c.SetPC(targetPC)
}
// DISPATCHER_CONTEXT is not defined on 386.
type DISPATCHER_CONTEXT struct{}
func (c *DISPATCHER_CONTEXT) Ctx() *Context {
return nil
}

View file

@ -0,0 +1,99 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package windows
import (
"internal/goarch"
"unsafe"
)
const CONTEXT_CONTROL = 0x100001
type M128 struct {
Low uint64
High int64
}
type Context struct {
P1Home uint64
P2Home uint64
P3Home uint64
P4Home uint64
P5Home uint64
P6Home uint64
ContextFlags uint32
MxCsr uint32
SegCs uint16
SegDs uint16
SegEs uint16
SegFs uint16
SegGs uint16
SegSs uint16
EFlags uint32
DR0 uint64
DR1 uint64
DR2 uint64
DR3 uint64
DR6 uint64
DR7 uint64
Rax uint64
Rcx uint64
Rdx uint64
Rbx uint64
Rsp uint64
Rbp uint64
Rsi uint64
Rdi uint64
R8 uint64
R9 uint64
R10 uint64
R11 uint64
R12 uint64
R13 uint64
R14 uint64
R15 uint64
Rip uint64
_ [512]byte
VectorRegister [26]M128
VectorControl uint64
DebugControl uint64
LastBranchToRip uint64
LastBranchFromRip uint64
LastExceptionToRip uint64
LastExceptionFromRip uint64
}
func (c *Context) PC() uintptr { return uintptr(c.Rip) }
func (c *Context) SP() uintptr { return uintptr(c.Rsp) }
// AMD64 does not have link register, so this returns 0.
func (c *Context) LR() uintptr { return 0 }
func (c *Context) SetLR(x uintptr) {}
func (c *Context) SetPC(x uintptr) { c.Rip = uint64(x) }
func (c *Context) SetSP(x uintptr) { c.Rsp = uint64(x) }
func (c *Context) SetFP(x uintptr) { c.Rbp = uint64(x) }
func (c *Context) PushCall(targetPC, resumePC uintptr) {
sp := c.SP() - goarch.StackAlign
*(*uintptr)(unsafe.Pointer(sp)) = resumePC
c.SetSP(sp)
c.SetPC(targetPC)
}
type DISPATCHER_CONTEXT struct {
ControlPc uint64
ImageBase uint64
FunctionEntry uintptr
EstablisherFrame uint64
TargetIp uint64
Context *Context
LanguageHandler uintptr
HandlerData uintptr
}
func (c *DISPATCHER_CONTEXT) Ctx() *Context {
return c.Context
}

View file

@ -0,0 +1,75 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package windows
import (
"internal/goarch"
"unsafe"
)
// NOTE(rsc): CONTEXT_CONTROL is actually 0x400001 and should include PC, SP, and LR.
// However, empirically, LR doesn't come along on Windows 10
// unless you also set CONTEXT_INTEGER (0x400002).
// Without LR, we skip over the next-to-bottom function in profiles
// when the bottom function is frameless.
// So we set both here, to make a working CONTEXT_CONTROL.
const CONTEXT_CONTROL = 0x400003
type Neon128 struct {
Low uint64
High int64
}
// See https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-arm64_nt_context
type Context struct {
ContextFlags uint32
Cpsr uint32
X [31]uint64 // fp is x[29], lr is x[30]
XSp uint64
Pc uint64
V [32]Neon128
Fpcr uint32
Fpsr uint32
Bcr [8]uint32
Bvr [8]uint64
Wcr [2]uint32
Wvr [2]uint64
}
func (c *Context) PC() uintptr { return uintptr(c.Pc) }
func (c *Context) SP() uintptr { return uintptr(c.XSp) }
func (c *Context) LR() uintptr { return uintptr(c.X[30]) }
func (c *Context) SetPC(x uintptr) { c.Pc = uint64(x) }
func (c *Context) SetSP(x uintptr) { c.XSp = uint64(x) }
func (c *Context) SetLR(x uintptr) { c.X[30] = uint64(x) }
func (c *Context) SetFP(x uintptr) { c.X[29] = uint64(x) }
func (c *Context) PushCall(targetPC, resumePC uintptr) {
// Push LR. The injected call is responsible
// for restoring LR. gentraceback is aware of
// this extra slot. See sigctxt.pushCall in
// signal_arm64.go.
sp := c.SP() - goarch.StackAlign
c.SetSP(sp)
*(*uint64)(unsafe.Pointer(sp)) = uint64(c.LR())
c.SetLR(resumePC)
c.SetPC(targetPC)
}
type DISPATCHER_CONTEXT struct {
ControlPc uint64
ImageBase uint64
FunctionEntry uintptr
EstablisherFrame uint64
TargetIp uint64
Context *Context
LanguageHandler uintptr
HandlerData uintptr
}
func (c *DISPATCHER_CONTEXT) Ctx() *Context {
return c.Context
}

View file

@ -1,24 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package windows
type MemoryBasicInformation struct {
// A pointer to the base address of the region of pages.
BaseAddress uintptr
// A pointer to the base address of a range of pages allocated by the VirtualAlloc function.
// The page pointed to by the BaseAddress member is contained within this allocation range.
AllocationBase uintptr
// The memory protection option when the region was initially allocated
AllocationProtect uint32
PartitionId uint16
// The size of the region beginning at the base address in which all pages have identical attributes, in bytes.
RegionSize uintptr
// The state of the pages in the region.
State uint32
// The access protection of the pages in the region.
Protect uint32
// The type of pages in the region.
Type uint32
}

View file

@ -5,6 +5,7 @@
package windows package windows
import ( import (
"internal/runtime/syscall/windows"
"syscall" "syscall"
"unsafe" "unsafe"
) )
@ -276,3 +277,7 @@ type FILE_COMPLETION_INFORMATION struct {
// https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa // https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexa
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw // https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw
const VER_NT_WORKSTATION = 0x0000001 const VER_NT_WORKSTATION = 0x0000001
type MemoryBasicInformation = windows.MemoryBasicInformation
type Context = windows.Context

View file

@ -1,101 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Windows architecture-independent definitions.
package runtime
const (
_PROT_NONE = 0
_PROT_READ = 1
_PROT_WRITE = 2
_PROT_EXEC = 4
_MAP_ANON = 1
_MAP_PRIVATE = 2
_DUPLICATE_SAME_ACCESS = 0x2
_THREAD_PRIORITY_HIGHEST = 0x2
_SIGINT = 0x2
_SIGTERM = 0xF
_CTRL_C_EVENT = 0x0
_CTRL_BREAK_EVENT = 0x1
_CTRL_CLOSE_EVENT = 0x2
_CTRL_LOGOFF_EVENT = 0x5
_CTRL_SHUTDOWN_EVENT = 0x6
_EXCEPTION_ACCESS_VIOLATION = 0xc0000005
_EXCEPTION_IN_PAGE_ERROR = 0xc0000006
_EXCEPTION_BREAKPOINT = 0x80000003
_EXCEPTION_ILLEGAL_INSTRUCTION = 0xc000001d
_EXCEPTION_FLT_DENORMAL_OPERAND = 0xc000008d
_EXCEPTION_FLT_DIVIDE_BY_ZERO = 0xc000008e
_EXCEPTION_FLT_INEXACT_RESULT = 0xc000008f
_EXCEPTION_FLT_OVERFLOW = 0xc0000091
_EXCEPTION_FLT_UNDERFLOW = 0xc0000093
_EXCEPTION_INT_DIVIDE_BY_ZERO = 0xc0000094
_EXCEPTION_INT_OVERFLOW = 0xc0000095
_INFINITE = 0xffffffff
_WAIT_TIMEOUT = 0x102
_EXCEPTION_CONTINUE_EXECUTION = -0x1
_EXCEPTION_CONTINUE_SEARCH = 0x0
_EXCEPTION_CONTINUE_SEARCH_SEH = 0x1
)
type systeminfo struct {
anon0 [4]byte
dwpagesize uint32
lpminimumapplicationaddress *byte
lpmaximumapplicationaddress *byte
dwactiveprocessormask uintptr
dwnumberofprocessors uint32
dwprocessortype uint32
dwallocationgranularity uint32
wprocessorlevel uint16
wprocessorrevision uint16
}
type exceptionpointers struct {
record *exceptionrecord
context *context
}
type exceptionrecord struct {
exceptioncode uint32
exceptionflags uint32
exceptionrecord *exceptionrecord
exceptionaddress uintptr
numberparameters uint32
exceptioninformation [15]uintptr
}
type overlapped struct {
internal uintptr
internalhigh uintptr
anon0 [8]byte
hevent *byte
}
type memoryBasicInformation struct {
baseAddress uintptr
allocationBase uintptr
allocationProtect uint32
regionSize uintptr
state uint32
protect uint32
type_ uint32
}
// https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfow
type _OSVERSIONINFOW struct {
osVersionInfoSize uint32
majorVersion uint32
minorVersion uint32
buildNumber uint32
platformId uint32
csdVersion [128]uint16
}

View file

@ -1,100 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import (
"internal/goarch"
"unsafe"
)
const _CONTEXT_CONTROL = 0x10001
type floatingsavearea struct {
controlword uint32
statusword uint32
tagword uint32
erroroffset uint32
errorselector uint32
dataoffset uint32
dataselector uint32
registerarea [80]uint8
cr0npxstate uint32
}
type context struct {
contextflags uint32
dr0 uint32
dr1 uint32
dr2 uint32
dr3 uint32
dr6 uint32
dr7 uint32
floatsave floatingsavearea
seggs uint32
segfs uint32
seges uint32
segds uint32
edi uint32
esi uint32
ebx uint32
edx uint32
ecx uint32
eax uint32
ebp uint32
eip uint32
segcs uint32
eflags uint32
esp uint32
segss uint32
extendedregisters [512]uint8
}
func (c *context) ip() uintptr { return uintptr(c.eip) }
func (c *context) sp() uintptr { return uintptr(c.esp) }
// 386 does not have link register, so this returns 0.
func (c *context) lr() uintptr { return 0 }
func (c *context) set_lr(x uintptr) {}
func (c *context) set_ip(x uintptr) { c.eip = uint32(x) }
func (c *context) set_sp(x uintptr) { c.esp = uint32(x) }
// 386 does not have frame pointer register.
func (c *context) set_fp(x uintptr) {}
func (c *context) pushCall(targetPC, resumePC uintptr) {
sp := c.sp() - goarch.StackAlign
*(*uintptr)(unsafe.Pointer(sp)) = resumePC
c.set_sp(sp)
c.set_ip(targetPC)
}
func prepareContextForSigResume(c *context) {
c.edx = c.esp
c.ecx = c.eip
}
func dumpregs(r *context) {
print("eax ", hex(r.eax), "\n")
print("ebx ", hex(r.ebx), "\n")
print("ecx ", hex(r.ecx), "\n")
print("edx ", hex(r.edx), "\n")
print("edi ", hex(r.edi), "\n")
print("esi ", hex(r.esi), "\n")
print("ebp ", hex(r.ebp), "\n")
print("esp ", hex(r.esp), "\n")
print("eip ", hex(r.eip), "\n")
print("eflags ", hex(r.eflags), "\n")
print("cs ", hex(r.segcs), "\n")
print("fs ", hex(r.segfs), "\n")
print("gs ", hex(r.seggs), "\n")
}
// _DISPATCHER_CONTEXT is not defined on 386.
type _DISPATCHER_CONTEXT struct{}
func (c *_DISPATCHER_CONTEXT) ctx() *context {
return nil
}

View file

@ -1,128 +0,0 @@
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import (
"internal/goarch"
"unsafe"
)
const _CONTEXT_CONTROL = 0x100001
type m128a struct {
low uint64
high int64
}
type context struct {
p1home uint64
p2home uint64
p3home uint64
p4home uint64
p5home uint64
p6home uint64
contextflags uint32
mxcsr uint32
segcs uint16
segds uint16
seges uint16
segfs uint16
seggs uint16
segss uint16
eflags uint32
dr0 uint64
dr1 uint64
dr2 uint64
dr3 uint64
dr6 uint64
dr7 uint64
rax uint64
rcx uint64
rdx uint64
rbx uint64
rsp uint64
rbp uint64
rsi uint64
rdi uint64
r8 uint64
r9 uint64
r10 uint64
r11 uint64
r12 uint64
r13 uint64
r14 uint64
r15 uint64
rip uint64
anon0 [512]byte
vectorregister [26]m128a
vectorcontrol uint64
debugcontrol uint64
lastbranchtorip uint64
lastbranchfromrip uint64
lastexceptiontorip uint64
lastexceptionfromrip uint64
}
func (c *context) ip() uintptr { return uintptr(c.rip) }
func (c *context) sp() uintptr { return uintptr(c.rsp) }
// AMD64 does not have link register, so this returns 0.
func (c *context) lr() uintptr { return 0 }
func (c *context) set_lr(x uintptr) {}
func (c *context) set_ip(x uintptr) { c.rip = uint64(x) }
func (c *context) set_sp(x uintptr) { c.rsp = uint64(x) }
func (c *context) set_fp(x uintptr) { c.rbp = uint64(x) }
func (c *context) pushCall(targetPC, resumePC uintptr) {
sp := c.sp() - goarch.StackAlign
*(*uintptr)(unsafe.Pointer(sp)) = resumePC
c.set_sp(sp)
c.set_ip(targetPC)
}
func prepareContextForSigResume(c *context) {
c.r8 = c.rsp
c.r9 = c.rip
}
func dumpregs(r *context) {
print("rax ", hex(r.rax), "\n")
print("rbx ", hex(r.rbx), "\n")
print("rcx ", hex(r.rcx), "\n")
print("rdx ", hex(r.rdx), "\n")
print("rdi ", hex(r.rdi), "\n")
print("rsi ", hex(r.rsi), "\n")
print("rbp ", hex(r.rbp), "\n")
print("rsp ", hex(r.rsp), "\n")
print("r8 ", hex(r.r8), "\n")
print("r9 ", hex(r.r9), "\n")
print("r10 ", hex(r.r10), "\n")
print("r11 ", hex(r.r11), "\n")
print("r12 ", hex(r.r12), "\n")
print("r13 ", hex(r.r13), "\n")
print("r14 ", hex(r.r14), "\n")
print("r15 ", hex(r.r15), "\n")
print("rip ", hex(r.rip), "\n")
print("rflags ", hex(r.eflags), "\n")
print("cs ", hex(r.segcs), "\n")
print("fs ", hex(r.segfs), "\n")
print("gs ", hex(r.seggs), "\n")
}
type _DISPATCHER_CONTEXT struct {
controlPc uint64
imageBase uint64
functionEntry uintptr
establisherFrame uint64
targetIp uint64
context *context
languageHandler uintptr
handlerData uintptr
}
func (c *_DISPATCHER_CONTEXT) ctx() *context {
return c.context
}

View file

@ -1,121 +0,0 @@
// Copyright 2018 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import (
"internal/goarch"
"unsafe"
)
// NOTE(rsc): _CONTEXT_CONTROL is actually 0x400001 and should include PC, SP, and LR.
// However, empirically, LR doesn't come along on Windows 10
// unless you also set _CONTEXT_INTEGER (0x400002).
// Without LR, we skip over the next-to-bottom function in profiles
// when the bottom function is frameless.
// So we set both here, to make a working _CONTEXT_CONTROL.
const _CONTEXT_CONTROL = 0x400003
type neon128 struct {
low uint64
high int64
}
// See https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-arm64_nt_context
type context struct {
contextflags uint32
cpsr uint32
x [31]uint64 // fp is x[29], lr is x[30]
xsp uint64
pc uint64
v [32]neon128
fpcr uint32
fpsr uint32
bcr [8]uint32
bvr [8]uint64
wcr [2]uint32
wvr [2]uint64
}
func (c *context) ip() uintptr { return uintptr(c.pc) }
func (c *context) sp() uintptr { return uintptr(c.xsp) }
func (c *context) lr() uintptr { return uintptr(c.x[30]) }
func (c *context) set_ip(x uintptr) { c.pc = uint64(x) }
func (c *context) set_sp(x uintptr) { c.xsp = uint64(x) }
func (c *context) set_lr(x uintptr) { c.x[30] = uint64(x) }
func (c *context) set_fp(x uintptr) { c.x[29] = uint64(x) }
func (c *context) pushCall(targetPC, resumePC uintptr) {
// Push LR. The injected call is responsible
// for restoring LR. gentraceback is aware of
// this extra slot. See sigctxt.pushCall in
// signal_arm64.go.
sp := c.sp() - goarch.StackAlign
c.set_sp(sp)
*(*uint64)(unsafe.Pointer(sp)) = uint64(c.lr())
c.set_lr(resumePC)
c.set_ip(targetPC)
}
func prepareContextForSigResume(c *context) {
c.x[0] = c.xsp
c.x[1] = c.pc
}
func dumpregs(r *context) {
print("r0 ", hex(r.x[0]), "\n")
print("r1 ", hex(r.x[1]), "\n")
print("r2 ", hex(r.x[2]), "\n")
print("r3 ", hex(r.x[3]), "\n")
print("r4 ", hex(r.x[4]), "\n")
print("r5 ", hex(r.x[5]), "\n")
print("r6 ", hex(r.x[6]), "\n")
print("r7 ", hex(r.x[7]), "\n")
print("r8 ", hex(r.x[8]), "\n")
print("r9 ", hex(r.x[9]), "\n")
print("r10 ", hex(r.x[10]), "\n")
print("r11 ", hex(r.x[11]), "\n")
print("r12 ", hex(r.x[12]), "\n")
print("r13 ", hex(r.x[13]), "\n")
print("r14 ", hex(r.x[14]), "\n")
print("r15 ", hex(r.x[15]), "\n")
print("r16 ", hex(r.x[16]), "\n")
print("r17 ", hex(r.x[17]), "\n")
print("r18 ", hex(r.x[18]), "\n")
print("r19 ", hex(r.x[19]), "\n")
print("r20 ", hex(r.x[20]), "\n")
print("r21 ", hex(r.x[21]), "\n")
print("r22 ", hex(r.x[22]), "\n")
print("r23 ", hex(r.x[23]), "\n")
print("r24 ", hex(r.x[24]), "\n")
print("r25 ", hex(r.x[25]), "\n")
print("r26 ", hex(r.x[26]), "\n")
print("r27 ", hex(r.x[27]), "\n")
print("r28 ", hex(r.x[28]), "\n")
print("r29 ", hex(r.x[29]), "\n")
print("lr ", hex(r.x[30]), "\n")
print("sp ", hex(r.xsp), "\n")
print("pc ", hex(r.pc), "\n")
print("cpsr ", hex(r.cpsr), "\n")
}
func stackcheck() {
// TODO: not implemented on ARM
}
type _DISPATCHER_CONTEXT struct {
controlPc uint64
imageBase uint64
functionEntry uintptr
establisherFrame uint64
targetIp uint64
context *context
languageHandler uintptr
handlerData uintptr
}
func (c *_DISPATCHER_CONTEXT) ctx() *context {
return c.context
}

View file

@ -7,7 +7,7 @@
package runtime package runtime
import ( import (
"internal/runtime/sys" "internal/runtime/syscall/windows"
"unsafe" "unsafe"
) )
@ -17,23 +17,11 @@ var (
) )
func NumberOfProcessors() int32 { func NumberOfProcessors() int32 {
var info systeminfo var info windows.SystemInfo
stdcall(_GetSystemInfo, uintptr(unsafe.Pointer(&info))) stdcall(_GetSystemInfo, uintptr(unsafe.Pointer(&info)))
return int32(info.dwnumberofprocessors) return int32(info.NumberOfProcessors)
} }
type ContextStub struct { func GetCallerFp() uintptr {
context return getcallerfp()
}
func (c ContextStub) GetPC() uintptr {
return c.ip()
}
func NewContextStub() *ContextStub {
var ctx context
ctx.set_ip(sys.GetCallerPC())
ctx.set_sp(sys.GetCallerSP())
ctx.set_fp(getcallerfp())
return &ContextStub{ctx}
} }

View file

@ -7,13 +7,10 @@ package runtime
import ( import (
"internal/goarch" "internal/goarch"
"internal/runtime/atomic" "internal/runtime/atomic"
"internal/runtime/syscall/windows"
"unsafe" "unsafe"
) )
const _DWORD_MAX = 0xffffffff
const _INVALID_HANDLE_VALUE = ^uintptr(0)
// Sources are used to identify the event that created an overlapped entry. // Sources are used to identify the event that created an overlapped entry.
// The source values are arbitrary. There is no risk of collision with user // The source values are arbitrary. There is no risk of collision with user
// defined values because the only way to set the key of an overlapped entry // defined values because the only way to set the key of an overlapped entry
@ -59,7 +56,7 @@ func unpackNetpollSource(key uintptr) uint8 {
// Keep these in sync. // Keep these in sync.
type pollOperation struct { type pollOperation struct {
// used by windows // used by windows
_ overlapped _ windows.Overlapped
// used by netpoll // used by netpoll
pd *pollDesc pd *pollDesc
mode int32 mode int32
@ -90,19 +87,19 @@ func pollOperationFromOverlappedEntry(e *overlappedEntry) *pollOperation {
// https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-overlapped_entry // https://learn.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-overlapped_entry
type overlappedEntry struct { type overlappedEntry struct {
key uintptr key uintptr
ov *overlapped ov *windows.Overlapped
internal uintptr internal uintptr
qty uint32 qty uint32
} }
var ( var (
iocphandle uintptr = _INVALID_HANDLE_VALUE // completion port io handle iocphandle uintptr = windows.INVALID_HANDLE_VALUE // completion port io handle
netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak netpollWakeSig atomic.Uint32 // used to avoid duplicate calls of netpollBreak
) )
func netpollinit() { func netpollinit() {
iocphandle = stdcall(_CreateIoCompletionPort, _INVALID_HANDLE_VALUE, 0, 0, _DWORD_MAX) iocphandle = stdcall(_CreateIoCompletionPort, windows.INVALID_HANDLE_VALUE, 0, 0, windows.DWORD_MAX)
if iocphandle == 0 { if iocphandle == 0 {
println("runtime: CreateIoCompletionPort failed (errno=", getlasterror(), ")") println("runtime: CreateIoCompletionPort failed (errno=", getlasterror(), ")")
throw("runtime: netpollinit failed") throw("runtime: netpollinit failed")
@ -152,7 +149,7 @@ func netpollBreak() {
// delay == 0: does not block, just polls // delay == 0: does not block, just polls
// delay > 0: block for up to that many nanoseconds // delay > 0: block for up to that many nanoseconds
func netpoll(delay int64) (gList, int32) { func netpoll(delay int64) (gList, int32) {
if iocphandle == _INVALID_HANDLE_VALUE { if iocphandle == windows.INVALID_HANDLE_VALUE {
return gList{}, 0 return gList{}, 0
} }
@ -182,7 +179,7 @@ func netpoll(delay int64) (gList, int32) {
} }
} }
if delay < 0 { if delay < 0 {
wait = _INFINITE wait = windows.INFINITE
} else if delay == 0 { } else if delay == 0 {
wait = 0 wait = 0
} else if delay < 1e6 { } else if delay < 1e6 {
@ -200,7 +197,7 @@ func netpoll(delay int64) (gList, int32) {
if stdcall(_GetQueuedCompletionStatusEx, iocphandle, uintptr(unsafe.Pointer(&entries[0])), uintptr(n), uintptr(unsafe.Pointer(&n)), uintptr(wait), 0) == 0 { if stdcall(_GetQueuedCompletionStatusEx, iocphandle, uintptr(unsafe.Pointer(&entries[0])), uintptr(n), uintptr(unsafe.Pointer(&n)), uintptr(wait), 0) == 0 {
mp.blocked = false mp.blocked = false
errno := getlasterror() errno := getlasterror()
if errno == _WAIT_TIMEOUT { if errno == windows.WAIT_TIMEOUT {
return gList{}, 0 return gList{}, 0
} }
println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")") println("runtime: GetQueuedCompletionStatusEx failed (errno=", errno, ")")
@ -243,11 +240,6 @@ func netpoll(delay int64) (gList, int32) {
// netpollQueueTimer queues a timer to wake up the poller after the given delay. // netpollQueueTimer queues a timer to wake up the poller after the given delay.
// It returns true if the timer expired during this call. // It returns true if the timer expired during this call.
func netpollQueueTimer(delay int64) (signaled bool) { func netpollQueueTimer(delay int64) (signaled bool) {
const (
STATUS_SUCCESS = 0x00000000
STATUS_PENDING = 0x00000103
STATUS_CANCELLED = 0xC0000120
)
mp := getg().m mp := getg().m
// A wait completion packet can only be associated with one timer at a time, // A wait completion packet can only be associated with one timer at a time,
// so we need to cancel the previous one if it exists. This wouldn't be necessary // so we need to cancel the previous one if it exists. This wouldn't be necessary
@ -258,11 +250,11 @@ func netpollQueueTimer(delay int64) (signaled bool) {
// another thread, so defer the cancellation until it is really necessary. // another thread, so defer the cancellation until it is really necessary.
errno := stdcall(_NtCancelWaitCompletionPacket, mp.waitIocpHandle, 1) errno := stdcall(_NtCancelWaitCompletionPacket, mp.waitIocpHandle, 1)
switch errno { switch errno {
case STATUS_CANCELLED: case windows.STATUS_CANCELLED:
// STATUS_CANCELLED is returned when the associated timer has already expired, // STATUS_CANCELLED is returned when the associated timer has already expired,
// in which automatically cancels the wait completion packet. // in which automatically cancels the wait completion packet.
fallthrough fallthrough
case STATUS_SUCCESS: case windows.STATUS_SUCCESS:
dt := -delay / 100 // relative sleep (negative), 100ns units dt := -delay / 100 // relative sleep (negative), 100ns units
if stdcall(_SetWaitableTimer, mp.waitIocpTimer, uintptr(unsafe.Pointer(&dt)), 0, 0, 0, 0) == 0 { if stdcall(_SetWaitableTimer, mp.waitIocpTimer, uintptr(unsafe.Pointer(&dt)), 0, 0, 0, 0) == 0 {
println("runtime: SetWaitableTimer failed; errno=", getlasterror()) println("runtime: SetWaitableTimer failed; errno=", getlasterror())
@ -273,7 +265,7 @@ func netpollQueueTimer(delay int64) (signaled bool) {
println("runtime: NtAssociateWaitCompletionPacket failed; errno=", errno) println("runtime: NtAssociateWaitCompletionPacket failed; errno=", errno)
throw("runtime: netpoll failed") throw("runtime: netpoll failed")
} }
case STATUS_PENDING: case windows.STATUS_PENDING:
// STATUS_PENDING is returned if the wait operation can't be canceled yet. // STATUS_PENDING is returned if the wait operation can't be canceled yet.
// This can happen if this thread was woken up by another event, such as a netpollBreak, // This can happen if this thread was woken up by another event, such as a netpollBreak,
// and the timer expired just while calling NtCancelWaitCompletionPacket, in which case // and the timer expired just while calling NtCancelWaitCompletionPacket, in which case

View file

@ -323,7 +323,7 @@ func monitorSuspendResume() {
func getCPUCount() int32 { func getCPUCount() int32 {
var mask, sysmask uintptr var mask, sysmask uintptr
ret := stdcall(_GetProcessAffinityMask, currentProcess, uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask))) ret := stdcall(_GetProcessAffinityMask, windows.CurrentProcess, uintptr(unsafe.Pointer(&mask)), uintptr(unsafe.Pointer(&sysmask)))
if ret != 0 { if ret != 0 {
n := 0 n := 0
maskbits := int(unsafe.Sizeof(mask) * 8) maskbits := int(unsafe.Sizeof(mask) * 8)
@ -337,22 +337,17 @@ func getCPUCount() int32 {
} }
} }
// use GetSystemInfo if GetProcessAffinityMask fails // use GetSystemInfo if GetProcessAffinityMask fails
var info systeminfo var info windows.SystemInfo
stdcall(_GetSystemInfo, uintptr(unsafe.Pointer(&info))) stdcall(_GetSystemInfo, uintptr(unsafe.Pointer(&info)))
return int32(info.dwnumberofprocessors) return int32(info.NumberOfProcessors)
} }
func getPageSize() uintptr { func getPageSize() uintptr {
var info systeminfo var info windows.SystemInfo
stdcall(_GetSystemInfo, uintptr(unsafe.Pointer(&info))) stdcall(_GetSystemInfo, uintptr(unsafe.Pointer(&info)))
return uintptr(info.dwpagesize) return uintptr(info.PageSize)
} }
const (
currentProcess = ^uintptr(0) // -1 = current process
currentThread = ^uintptr(1) // -2 = current thread
)
// in sys_windows_386.s and sys_windows_amd64.s: // in sys_windows_386.s and sys_windows_amd64.s:
func getlasterror() uint32 func getlasterror() uint32
@ -405,18 +400,11 @@ var haveHighResSleep = false
// resolution timer. createHighResTimer returns new timer // resolution timer. createHighResTimer returns new timer
// handle or 0, if CreateWaitableTimerEx failed. // handle or 0, if CreateWaitableTimerEx failed.
func createHighResTimer() uintptr { func createHighResTimer() uintptr {
const ( // As per @jstarks, see
// As per @jstarks, see // https://github.com/golang/go/issues/8687#issuecomment-656259353
// https://github.com/golang/go/issues/8687#issuecomment-656259353
_CREATE_WAITABLE_TIMER_HIGH_RESOLUTION = 0x00000002
_SYNCHRONIZE = 0x00100000
_TIMER_QUERY_STATE = 0x0001
_TIMER_MODIFY_STATE = 0x0002
)
return stdcall(_CreateWaitableTimerExW, 0, 0, return stdcall(_CreateWaitableTimerExW, 0, 0,
_CREATE_WAITABLE_TIMER_HIGH_RESOLUTION, windows.CREATE_WAITABLE_TIMER_HIGH_RESOLUTION,
_SYNCHRONIZE|_TIMER_QUERY_STATE|_TIMER_MODIFY_STATE) windows.SYNCHRONIZE|windows.TIMER_QUERY_STATE|windows.TIMER_MODIFY_STATE)
} }
func initHighResTimer() { func initHighResTimer() {
@ -454,10 +442,10 @@ func initLongPathSupport() {
) )
// Check that we're ≥ 10.0.15063. // Check that we're ≥ 10.0.15063.
info := _OSVERSIONINFOW{} info := windows.OSVERSIONINFOW{}
info.osVersionInfoSize = uint32(unsafe.Sizeof(info)) info.OSVersionInfoSize = uint32(unsafe.Sizeof(info))
stdcall(_RtlGetVersion, uintptr(unsafe.Pointer(&info))) stdcall(_RtlGetVersion, uintptr(unsafe.Pointer(&info)))
if info.majorVersion < 10 || (info.majorVersion == 10 && info.minorVersion == 0 && info.buildNumber < 15063) { if info.MajorVersion < 10 || (info.MajorVersion == 10 && info.MinorVersion == 0 && info.BuildNumber < 15063) {
return return
} }
@ -493,7 +481,7 @@ func osinit() {
// of dedicated threads -- GUI, IO, computational, etc. Go processes use // of dedicated threads -- GUI, IO, computational, etc. Go processes use
// equivalent threads that all do a mix of GUI, IO, computations, etc. // equivalent threads that all do a mix of GUI, IO, computations, etc.
// In such context dynamic priority boosting does nothing but harm, so we turn it off. // In such context dynamic priority boosting does nothing but harm, so we turn it off.
stdcall(_SetProcessPriorityBoost, currentProcess, 1) stdcall(_SetProcessPriorityBoost, windows.CurrentProcess, 1)
} }
//go:nosplit //go:nosplit
@ -671,7 +659,7 @@ func semasleep(ns int64) int32 {
var result uintptr var result uintptr
if ns < 0 { if ns < 0 {
result = stdcall(_WaitForSingleObject, getg().m.waitsema, uintptr(_INFINITE)) result = stdcall(_WaitForSingleObject, getg().m.waitsema, uintptr(windows.INFINITE))
} else { } else {
start := nanotime() start := nanotime()
elapsed := int64(0) elapsed := int64(0)
@ -828,7 +816,7 @@ func sigblock(exiting bool) {
// Called on the new thread, cannot allocate Go memory. // Called on the new thread, cannot allocate Go memory.
func minit() { func minit() {
var thandle uintptr var thandle uintptr
if stdcall(_DuplicateHandle, currentProcess, currentThread, currentProcess, uintptr(unsafe.Pointer(&thandle)), 0, 0, _DUPLICATE_SAME_ACCESS) == 0 { if stdcall(_DuplicateHandle, windows.CurrentProcess, windows.CurrentThread, windows.CurrentProcess, uintptr(unsafe.Pointer(&thandle)), 0, 0, windows.DUPLICATE_SAME_ACCESS) == 0 {
print("runtime.minit: duplicatehandle failed; errno=", getlasterror(), "\n") print("runtime.minit: duplicatehandle failed; errno=", getlasterror(), "\n")
throw("runtime.minit: duplicatehandle failed") throw("runtime.minit: duplicatehandle failed")
} }
@ -863,7 +851,7 @@ func minit() {
// Query the true stack base from the OS. Currently we're // Query the true stack base from the OS. Currently we're
// running on a small assumed stack. // running on a small assumed stack.
var mbi memoryBasicInformation var mbi windows.MemoryBasicInformation
res := stdcall(_VirtualQuery, uintptr(unsafe.Pointer(&mbi)), uintptr(unsafe.Pointer(&mbi)), unsafe.Sizeof(mbi)) res := stdcall(_VirtualQuery, uintptr(unsafe.Pointer(&mbi)), uintptr(unsafe.Pointer(&mbi)), unsafe.Sizeof(mbi))
if res == 0 { if res == 0 {
print("runtime: VirtualQuery failed; errno=", getlasterror(), "\n") print("runtime: VirtualQuery failed; errno=", getlasterror(), "\n")
@ -875,7 +863,7 @@ func minit() {
// calling C functions that don't have stack checks and for // calling C functions that don't have stack checks and for
// lastcontinuehandler. We shouldn't be anywhere near this // lastcontinuehandler. We shouldn't be anywhere near this
// bound anyway. // bound anyway.
base := mbi.allocationBase + 16<<10 base := mbi.AllocationBase + 16<<10
// Sanity check the stack bounds. // Sanity check the stack bounds.
g0 := getg() g0 := getg()
if base > g0.stack.hi || g0.stack.hi-base > 64<<20 { if base > g0.stack.hi || g0.stack.hi-base > 64<<20 {
@ -1000,7 +988,7 @@ func osyield() {
//go:nosplit //go:nosplit
func usleep_no_g(us uint32) { func usleep_no_g(us uint32) {
timeout := uintptr(us) / 1000 // ms units timeout := uintptr(us) / 1000 // ms units
stdcall_no_g(_WaitForSingleObject, _INVALID_HANDLE_VALUE, timeout) stdcall_no_g(_WaitForSingleObject, windows.INVALID_HANDLE_VALUE, timeout)
} }
//go:nosplit //go:nosplit
@ -1013,9 +1001,9 @@ func usleep(us uint32) {
h = getg().m.highResTimer h = getg().m.highResTimer
dt := -10 * int64(us) // relative sleep (negative), 100ns units dt := -10 * int64(us) // relative sleep (negative), 100ns units
stdcall(_SetWaitableTimer, h, uintptr(unsafe.Pointer(&dt)), 0, 0, 0, 0) stdcall(_SetWaitableTimer, h, uintptr(unsafe.Pointer(&dt)), 0, 0, 0, 0)
timeout = _INFINITE timeout = windows.INFINITE
} else { } else {
h = _INVALID_HANDLE_VALUE h = windows.INVALID_HANDLE_VALUE
timeout = uintptr(us) / 1000 // ms units timeout = uintptr(us) / 1000 // ms units
} }
stdcall(_WaitForSingleObject, h, timeout) stdcall(_WaitForSingleObject, h, timeout)
@ -1026,16 +1014,16 @@ func ctrlHandler(_type uint32) uintptr {
var s uint32 var s uint32
switch _type { switch _type {
case _CTRL_C_EVENT, _CTRL_BREAK_EVENT: case windows.CTRL_C_EVENT, windows.CTRL_BREAK_EVENT:
s = _SIGINT s = windows.SIGINT
case _CTRL_CLOSE_EVENT, _CTRL_LOGOFF_EVENT, _CTRL_SHUTDOWN_EVENT: case windows.CTRL_CLOSE_EVENT, windows.CTRL_LOGOFF_EVENT, windows.CTRL_SHUTDOWN_EVENT:
s = _SIGTERM s = windows.SIGTERM
default: default:
return 0 return 0
} }
if sigsend(s) { if sigsend(s) {
if s == _SIGTERM { if s == windows.SIGTERM {
// Windows terminates the process after this handler returns. // Windows terminates the process after this handler returns.
// Block indefinitely to give signal handlers a chance to clean up, // Block indefinitely to give signal handlers a chance to clean up,
// but make sure to be properly parked first, so the rest of the // but make sure to be properly parked first, so the rest of the
@ -1054,16 +1042,16 @@ var profiletimer uintptr
func profilem(mp *m, thread uintptr) { func profilem(mp *m, thread uintptr) {
// Align Context to 16 bytes. // Align Context to 16 bytes.
var c *context var c *windows.Context
var cbuf [unsafe.Sizeof(*c) + 15]byte var cbuf [unsafe.Sizeof(*c) + 15]byte
c = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&cbuf[15]))) &^ 15)) c = (*windows.Context)(unsafe.Pointer((uintptr(unsafe.Pointer(&cbuf[15]))) &^ 15))
c.contextflags = _CONTEXT_CONTROL c.ContextFlags = windows.CONTEXT_CONTROL
stdcall(_GetThreadContext, thread, uintptr(unsafe.Pointer(c))) stdcall(_GetThreadContext, thread, uintptr(unsafe.Pointer(c)))
gp := gFromSP(mp, c.sp()) gp := gFromSP(mp, c.SP())
sigprof(c.ip(), c.sp(), c.lr(), gp, mp) sigprof(c.PC(), c.SP(), c.LR(), gp, mp)
} }
func gFromSP(mp *m, sp uintptr) *g { func gFromSP(mp *m, sp uintptr) *g {
@ -1080,10 +1068,10 @@ func gFromSP(mp *m, sp uintptr) *g {
} }
func profileLoop() { func profileLoop() {
stdcall(_SetThreadPriority, currentThread, _THREAD_PRIORITY_HIGHEST) stdcall(_SetThreadPriority, windows.CurrentThread, windows.THREAD_PRIORITY_HIGHEST)
for { for {
stdcall(_WaitForSingleObject, profiletimer, _INFINITE) stdcall(_WaitForSingleObject, profiletimer, windows.INFINITE)
first := (*m)(atomic.Loadp(unsafe.Pointer(&allm))) first := (*m)(atomic.Loadp(unsafe.Pointer(&allm)))
for mp := first; mp != nil; mp = mp.alllink { for mp := first; mp != nil; mp = mp.alllink {
if mp == getg().m { if mp == getg().m {
@ -1101,7 +1089,7 @@ func profileLoop() {
} }
// Acquire our own handle to the thread. // Acquire our own handle to the thread.
var thread uintptr var thread uintptr
if stdcall(_DuplicateHandle, currentProcess, mp.thread, currentProcess, uintptr(unsafe.Pointer(&thread)), 0, 0, _DUPLICATE_SAME_ACCESS) == 0 { if stdcall(_DuplicateHandle, windows.CurrentProcess, mp.thread, windows.CurrentProcess, uintptr(unsafe.Pointer(&thread)), 0, 0, windows.DUPLICATE_SAME_ACCESS) == 0 {
print("runtime: duplicatehandle failed; errno=", getlasterror(), "\n") print("runtime: duplicatehandle failed; errno=", getlasterror(), "\n")
throw("duplicatehandle failed") throw("duplicatehandle failed")
} }
@ -1183,17 +1171,17 @@ func preemptM(mp *m) {
return return
} }
var thread uintptr var thread uintptr
if stdcall(_DuplicateHandle, currentProcess, mp.thread, currentProcess, uintptr(unsafe.Pointer(&thread)), 0, 0, _DUPLICATE_SAME_ACCESS) == 0 { if stdcall(_DuplicateHandle, windows.CurrentProcess, mp.thread, windows.CurrentProcess, uintptr(unsafe.Pointer(&thread)), 0, 0, windows.DUPLICATE_SAME_ACCESS) == 0 {
print("runtime.preemptM: duplicatehandle failed; errno=", getlasterror(), "\n") print("runtime.preemptM: duplicatehandle failed; errno=", getlasterror(), "\n")
throw("runtime.preemptM: duplicatehandle failed") throw("runtime.preemptM: duplicatehandle failed")
} }
unlock(&mp.threadLock) unlock(&mp.threadLock)
// Prepare thread context buffer. This must be aligned to 16 bytes. // Prepare thread context buffer. This must be aligned to 16 bytes.
var c *context var c *windows.Context
var cbuf [unsafe.Sizeof(*c) + 15]byte var cbuf [unsafe.Sizeof(*c) + 15]byte
c = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&cbuf[15]))) &^ 15)) c = (*windows.Context)(unsafe.Pointer((uintptr(unsafe.Pointer(&cbuf[15]))) &^ 15))
c.contextflags = _CONTEXT_CONTROL c.ContextFlags = windows.CONTEXT_CONTROL
// Serialize thread suspension. SuspendThread is asynchronous, // Serialize thread suspension. SuspendThread is asynchronous,
// so it's otherwise possible for two threads to suspend each // so it's otherwise possible for two threads to suspend each
@ -1227,12 +1215,12 @@ func preemptM(mp *m) {
unlock(&suspendLock) unlock(&suspendLock)
// Does it want a preemption and is it safe to preempt? // Does it want a preemption and is it safe to preempt?
gp := gFromSP(mp, c.sp()) gp := gFromSP(mp, c.SP())
if gp != nil && wantAsyncPreempt(gp) { if gp != nil && wantAsyncPreempt(gp) {
if ok, resumePC := isAsyncSafePoint(gp, c.ip(), c.sp(), c.lr()); ok { if ok, resumePC := isAsyncSafePoint(gp, c.PC(), c.SP(), c.LR()); ok {
// Inject call to asyncPreempt // Inject call to asyncPreempt
targetPC := abi.FuncPCABI0(asyncPreempt) targetPC := abi.FuncPCABI0(asyncPreempt)
c.pushCall(targetPC, resumePC) c.PushCall(targetPC, resumePC)
stdcall(_SetThreadContext, thread, uintptr(unsafe.Pointer(c))) stdcall(_SetThreadContext, thread, uintptr(unsafe.Pointer(c)))
} }
} }

View file

@ -12,3 +12,7 @@ func cputicks() int64 {
stdcall(_QueryPerformanceCounter, uintptr(unsafe.Pointer(&counter))) stdcall(_QueryPerformanceCounter, uintptr(unsafe.Pointer(&counter)))
return counter return counter
} }
func stackcheck() {
// TODO: not implemented
}

View file

@ -6,6 +6,7 @@ package runtime_test
import ( import (
"internal/abi" "internal/abi"
"internal/runtime/sys"
"internal/syscall/windows" "internal/syscall/windows"
"runtime" "runtime"
"slices" "slices"
@ -47,7 +48,7 @@ func TestSehLookupFunctionEntry(t *testing.T) {
{"func in prologue", sehf1pc + 1, true}, {"func in prologue", sehf1pc + 1, true},
{"anonymous func with frame", abi.FuncPCABIInternal(fnwithframe), true}, {"anonymous func with frame", abi.FuncPCABIInternal(fnwithframe), true},
{"anonymous func without frame", abi.FuncPCABIInternal(fnwithoutframe), false}, {"anonymous func without frame", abi.FuncPCABIInternal(fnwithoutframe), false},
{"pc at func body", runtime.NewContextStub().GetPC(), true}, {"pc at func body", sys.GetCallerPC(), true},
} }
for _, tt := range tests { for _, tt := range tests {
var base uintptr var base uintptr
@ -68,19 +69,22 @@ func sehCallers() []uintptr {
// We don't need a real context, // We don't need a real context,
// RtlVirtualUnwind just needs a context with // RtlVirtualUnwind just needs a context with
// valid a pc, sp and fp (aka bp). // valid a pc, sp and fp (aka bp).
ctx := runtime.NewContextStub() var ctx windows.Context
ctx.SetPC(sys.GetCallerPC())
ctx.SetSP(sys.GetCallerSP())
ctx.SetFP(runtime.GetCallerFp())
pcs := make([]uintptr, 15) pcs := make([]uintptr, 15)
var base, frame uintptr var base, frame uintptr
var n int var n int
for i := 0; i < len(pcs); i++ { for i := 0; i < len(pcs); i++ {
fn := windows.RtlLookupFunctionEntry(ctx.GetPC(), &base, nil) fn := windows.RtlLookupFunctionEntry(ctx.PC(), &base, nil)
if fn == nil { if fn == nil {
break break
} }
pcs[i] = ctx.GetPC() pcs[i] = ctx.PC()
n++ n++
windows.RtlVirtualUnwind(0, base, ctx.GetPC(), fn, unsafe.Pointer(ctx), nil, &frame, nil) windows.RtlVirtualUnwind(0, base, ctx.PC(), fn, unsafe.Pointer(&ctx), nil, &frame, nil)
} }
return pcs[:n] return pcs[:n]
} }
@ -129,15 +133,14 @@ func TestSehUnwind(t *testing.T) {
t.Skip("skipping amd64-only test") t.Skip("skipping amd64-only test")
} }
pcs := sehf3(false) pcs := sehf3(false)
testSehCallersEqual(t, pcs, []string{"runtime_test.sehCallers", "runtime_test.sehf4", testSehCallersEqual(t, pcs, []string{"runtime_test.sehf4", "runtime_test.sehf3", "runtime_test.TestSehUnwind"})
"runtime_test.sehf3", "runtime_test.TestSehUnwind"})
} }
func TestSehUnwindPanic(t *testing.T) { func TestSehUnwindPanic(t *testing.T) {
if runtime.GOARCH != "amd64" { if runtime.GOARCH != "amd64" {
t.Skip("skipping amd64-only test") t.Skip("skipping amd64-only test")
} }
want := []string{"runtime_test.sehCallers", "runtime_test.TestSehUnwindPanic.func1", "runtime.gopanic", want := []string{"runtime_test.TestSehUnwindPanic.func1", "runtime.gopanic",
"runtime_test.sehf4", "runtime_test.sehf3", "runtime_test.TestSehUnwindPanic"} "runtime_test.sehf4", "runtime_test.sehf3", "runtime_test.TestSehUnwindPanic"}
defer func() { defer func() {
if r := recover(); r == nil { if r := recover(); r == nil {
@ -153,7 +156,7 @@ func TestSehUnwindDoublePanic(t *testing.T) {
if runtime.GOARCH != "amd64" { if runtime.GOARCH != "amd64" {
t.Skip("skipping amd64-only test") t.Skip("skipping amd64-only test")
} }
want := []string{"runtime_test.sehCallers", "runtime_test.TestSehUnwindDoublePanic.func1.1", "runtime.gopanic", want := []string{"runtime_test.TestSehUnwindDoublePanic.func1.1", "runtime.gopanic",
"runtime_test.TestSehUnwindDoublePanic.func1", "runtime.gopanic", "runtime_test.TestSehUnwindDoublePanic"} "runtime_test.TestSehUnwindDoublePanic.func1", "runtime.gopanic", "runtime_test.TestSehUnwindDoublePanic"}
defer func() { defer func() {
defer func() { defer func() {
@ -175,7 +178,7 @@ func TestSehUnwindNilPointerPanic(t *testing.T) {
if runtime.GOARCH != "amd64" { if runtime.GOARCH != "amd64" {
t.Skip("skipping amd64-only test") t.Skip("skipping amd64-only test")
} }
want := []string{"runtime_test.sehCallers", "runtime_test.TestSehUnwindNilPointerPanic.func1", "runtime.gopanic", want := []string{"runtime_test.TestSehUnwindNilPointerPanic.func1", "runtime.gopanic",
"runtime.sigpanic", "runtime_test.TestSehUnwindNilPointerPanic"} "runtime.sigpanic", "runtime_test.TestSehUnwindNilPointerPanic"}
defer func() { defer func() {
if r := recover(); r == nil { if r := recover(); r == nil {

View file

@ -6,36 +6,29 @@ package runtime
import ( import (
"internal/abi" "internal/abi"
"internal/runtime/syscall/windows"
"unsafe" "unsafe"
) )
const (
_SEM_FAILCRITICALERRORS = 0x0001
_SEM_NOGPFAULTERRORBOX = 0x0002
_SEM_NOOPENFILEERRORBOX = 0x8000
_WER_FAULT_REPORTING_NO_UI = 0x0020
)
func preventErrorDialogs() { func preventErrorDialogs() {
errormode := stdcall(_GetErrorMode) errormode := stdcall(_GetErrorMode)
stdcall(_SetErrorMode, errormode|_SEM_FAILCRITICALERRORS|_SEM_NOGPFAULTERRORBOX|_SEM_NOOPENFILEERRORBOX) stdcall(_SetErrorMode, errormode|windows.SEM_FAILCRITICALERRORS|windows.SEM_NOGPFAULTERRORBOX|windows.SEM_NOOPENFILEERRORBOX)
// Disable WER fault reporting UI. // Disable WER fault reporting UI.
// Do this even if WER is disabled as a whole, // Do this even if WER is disabled as a whole,
// as WER might be enabled later with setTraceback("wer") // as WER might be enabled later with setTraceback("wer")
// and we still want the fault reporting UI to be disabled if this happens. // and we still want the fault reporting UI to be disabled if this happens.
var werflags uintptr var werflags uintptr
stdcall(_WerGetFlags, currentProcess, uintptr(unsafe.Pointer(&werflags))) stdcall(_WerGetFlags, windows.CurrentProcess, uintptr(unsafe.Pointer(&werflags)))
stdcall(_WerSetFlags, werflags|_WER_FAULT_REPORTING_NO_UI) stdcall(_WerSetFlags, werflags|windows.WER_FAULT_REPORTING_NO_UI)
} }
// enableWER re-enables Windows error reporting without fault reporting UI. // enableWER re-enables Windows error reporting without fault reporting UI.
func enableWER() { func enableWER() {
// re-enable Windows Error Reporting // re-enable Windows Error Reporting
errormode := stdcall(_GetErrorMode) errormode := stdcall(_GetErrorMode)
if errormode&_SEM_NOGPFAULTERRORBOX != 0 { if errormode&windows.SEM_NOGPFAULTERRORBOX != 0 {
stdcall(_SetErrorMode, errormode^_SEM_NOGPFAULTERRORBOX) stdcall(_SetErrorMode, errormode^windows.SEM_NOGPFAULTERRORBOX)
} }
} }
@ -62,8 +55,8 @@ func initExceptionHandler() {
// by calling runtime.abort function. // by calling runtime.abort function.
// //
//go:nosplit //go:nosplit
func isAbort(r *context) bool { func isAbort(r *windows.Context) bool {
pc := r.ip() pc := r.PC()
if GOARCH == "386" || GOARCH == "amd64" { if GOARCH == "386" || GOARCH == "amd64" {
// In the case of an abort, the exception IP is one byte after // In the case of an abort, the exception IP is one byte after
// the INT3 (this differs from UNIX OSes). // the INT3 (this differs from UNIX OSes).
@ -79,29 +72,29 @@ func isAbort(r *context) bool {
// because of a stack overflow. // because of a stack overflow.
// //
//go:nosplit //go:nosplit
func isgoexception(info *exceptionrecord, r *context) bool { func isgoexception(info *windows.ExceptionRecord, r *windows.Context) bool {
// Only handle exception if executing instructions in Go binary // Only handle exception if executing instructions in Go binary
// (not Windows library code). // (not Windows library code).
// TODO(mwhudson): needs to loop to support shared libs // TODO(mwhudson): needs to loop to support shared libs
if r.ip() < firstmoduledata.text || firstmoduledata.etext < r.ip() { if r.PC() < firstmoduledata.text || firstmoduledata.etext < r.PC() {
return false return false
} }
// Go will only handle some exceptions. // Go will only handle some exceptions.
switch info.exceptioncode { switch info.ExceptionCode {
default: default:
return false return false
case _EXCEPTION_ACCESS_VIOLATION: case windows.EXCEPTION_ACCESS_VIOLATION:
case _EXCEPTION_IN_PAGE_ERROR: case windows.EXCEPTION_IN_PAGE_ERROR:
case _EXCEPTION_INT_DIVIDE_BY_ZERO: case windows.EXCEPTION_INT_DIVIDE_BY_ZERO:
case _EXCEPTION_INT_OVERFLOW: case windows.EXCEPTION_INT_OVERFLOW:
case _EXCEPTION_FLT_DENORMAL_OPERAND: case windows.EXCEPTION_FLT_DENORMAL_OPERAND:
case _EXCEPTION_FLT_DIVIDE_BY_ZERO: case windows.EXCEPTION_FLT_DIVIDE_BY_ZERO:
case _EXCEPTION_FLT_INEXACT_RESULT: case windows.EXCEPTION_FLT_INEXACT_RESULT:
case _EXCEPTION_FLT_OVERFLOW: case windows.EXCEPTION_FLT_OVERFLOW:
case _EXCEPTION_FLT_UNDERFLOW: case windows.EXCEPTION_FLT_UNDERFLOW:
case _EXCEPTION_BREAKPOINT: case windows.EXCEPTION_BREAKPOINT:
case _EXCEPTION_ILLEGAL_INSTRUCTION: // breakpoint arrives this way on arm64 case windows.EXCEPTION_ILLEGAL_INSTRUCTION: // breakpoint arrives this way on arm64
} }
return true return true
} }
@ -134,13 +127,13 @@ func sigFetchG() *g {
// It is nosplit for the same reason as exceptionhandler. // It is nosplit for the same reason as exceptionhandler.
// //
//go:nosplit //go:nosplit
func sigtrampgo(ep *exceptionpointers, kind int) int32 { func sigtrampgo(ep *windows.ExceptionPointers, kind int) int32 {
gp := sigFetchG() gp := sigFetchG()
if gp == nil { if gp == nil {
return _EXCEPTION_CONTINUE_SEARCH return windows.EXCEPTION_CONTINUE_SEARCH
} }
var fn func(info *exceptionrecord, r *context, gp *g) int32 var fn func(info *windows.ExceptionRecord, r *windows.Context, gp *g) int32
switch kind { switch kind {
case callbackVEH: case callbackVEH:
fn = exceptionhandler fn = exceptionhandler
@ -169,12 +162,12 @@ func sigtrampgo(ep *exceptionpointers, kind int) int32 {
var ret int32 var ret int32
if gp != gp.m.g0 { if gp != gp.m.g0 {
systemstack(func() { systemstack(func() {
ret = fn(ep.record, ep.context, gp) ret = fn(ep.Record, ep.Context, gp)
}) })
} else { } else {
ret = fn(ep.record, ep.context, gp) ret = fn(ep.Record, ep.Context, gp)
} }
if ret == _EXCEPTION_CONTINUE_SEARCH { if ret == windows.EXCEPTION_CONTINUE_SEARCH {
return ret return ret
} }
@ -189,13 +182,13 @@ func sigtrampgo(ep *exceptionpointers, kind int) int32 {
// will not actually return to the original frame, so the registers // will not actually return to the original frame, so the registers
// are effectively dead. But this does mean we can't use the // are effectively dead. But this does mean we can't use the
// same mechanism for async preemption. // same mechanism for async preemption.
if ep.context.ip() == abi.FuncPCABI0(sigresume) { if ep.Context.PC() == abi.FuncPCABI0(sigresume) {
// sigresume has already been set up by a previous exception. // sigresume has already been set up by a previous exception.
return ret return ret
} }
prepareContextForSigResume(ep.context) prepareContextForSigResume(ep.Context)
ep.context.set_sp(gp.m.g0.sched.sp) ep.Context.SetSP(gp.m.g0.sched.sp)
ep.context.set_ip(abi.FuncPCABI0(sigresume)) ep.Context.SetPC(abi.FuncPCABI0(sigresume))
return ret return ret
} }
@ -207,9 +200,9 @@ func sigtrampgo(ep *exceptionpointers, kind int) int32 {
// _EXCEPTION_BREAKPOINT, which is raised by abort() if we overflow the g0 stack. // _EXCEPTION_BREAKPOINT, which is raised by abort() if we overflow the g0 stack.
// //
//go:nosplit //go:nosplit
func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 { func exceptionhandler(info *windows.ExceptionRecord, r *windows.Context, gp *g) int32 {
if !isgoexception(info, r) { if !isgoexception(info, r) {
return _EXCEPTION_CONTINUE_SEARCH return windows.EXCEPTION_CONTINUE_SEARCH
} }
if gp.throwsplit || isAbort(r) { if gp.throwsplit || isAbort(r) {
@ -226,10 +219,10 @@ func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
// Have to pass arguments out of band since // Have to pass arguments out of band since
// augmenting the stack frame would break // augmenting the stack frame would break
// the unwinding code. // the unwinding code.
gp.sig = info.exceptioncode gp.sig = info.ExceptionCode
gp.sigcode0 = info.exceptioninformation[0] gp.sigcode0 = info.ExceptionInformation[0]
gp.sigcode1 = info.exceptioninformation[1] gp.sigcode1 = info.ExceptionInformation[1]
gp.sigpc = r.ip() gp.sigpc = r.PC()
// Only push runtime·sigpanic if r.ip() != 0. // Only push runtime·sigpanic if r.ip() != 0.
// If r.ip() == 0, probably panicked because of a // If r.ip() == 0, probably panicked because of a
@ -244,13 +237,13 @@ func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
// The exception is not from asyncPreempt, so not to push a // The exception is not from asyncPreempt, so not to push a
// sigpanic call to make it look like that. Instead, just // sigpanic call to make it look like that. Instead, just
// overwrite the PC. (See issue #35773) // overwrite the PC. (See issue #35773)
if r.ip() != 0 && r.ip() != abi.FuncPCABI0(asyncPreempt) { if r.PC() != 0 && r.PC() != abi.FuncPCABI0(asyncPreempt) {
r.pushCall(abi.FuncPCABI0(sigpanic0), r.ip()) r.PushCall(abi.FuncPCABI0(sigpanic0), r.PC())
} else { } else {
// Not safe to push the call. Just clobber the frame. // Not safe to push the call. Just clobber the frame.
r.set_ip(abi.FuncPCABI0(sigpanic0)) r.SetPC(abi.FuncPCABI0(sigpanic0))
} }
return _EXCEPTION_CONTINUE_EXECUTION return windows.EXCEPTION_CONTINUE_EXECUTION
} }
// sehhandler is reached as part of the SEH chain. // sehhandler is reached as part of the SEH chain.
@ -258,11 +251,11 @@ func exceptionhandler(info *exceptionrecord, r *context, gp *g) int32 {
// It is nosplit for the same reason as exceptionhandler. // It is nosplit for the same reason as exceptionhandler.
// //
//go:nosplit //go:nosplit
func sehhandler(_ *exceptionrecord, _ uint64, _ *context, dctxt *_DISPATCHER_CONTEXT) int32 { func sehhandler(_ *windows.ExceptionRecord, _ uint64, _ *windows.Context, dctxt *windows.DISPATCHER_CONTEXT) int32 {
g0 := getg() g0 := getg()
if g0 == nil || g0.m.curg == nil { if g0 == nil || g0.m.curg == nil {
// No g available, nothing to do here. // No g available, nothing to do here.
return _EXCEPTION_CONTINUE_SEARCH_SEH return windows.EXCEPTION_CONTINUE_SEARCH_SEH
} }
// The Windows SEH machinery will unwind the stack until it finds // The Windows SEH machinery will unwind the stack until it finds
// a frame with a handler for the exception or until the frame is // a frame with a handler for the exception or until the frame is
@ -275,19 +268,19 @@ func sehhandler(_ *exceptionrecord, _ uint64, _ *context, dctxt *_DISPATCHER_CON
// To work around this, manually unwind the stack until the top of the goroutine // To work around this, manually unwind the stack until the top of the goroutine
// stack is reached, and then pass the control back to Windows. // stack is reached, and then pass the control back to Windows.
gp := g0.m.curg gp := g0.m.curg
ctxt := dctxt.ctx() ctxt := dctxt.Ctx()
var base, sp uintptr var base, sp uintptr
for { for {
entry := stdcall(_RtlLookupFunctionEntry, ctxt.ip(), uintptr(unsafe.Pointer(&base)), 0) entry := stdcall(_RtlLookupFunctionEntry, ctxt.PC(), uintptr(unsafe.Pointer(&base)), 0)
if entry == 0 { if entry == 0 {
break break
} }
stdcall(_RtlVirtualUnwind, 0, base, ctxt.ip(), entry, uintptr(unsafe.Pointer(ctxt)), 0, uintptr(unsafe.Pointer(&sp)), 0) stdcall(_RtlVirtualUnwind, 0, base, ctxt.PC(), entry, uintptr(unsafe.Pointer(ctxt)), 0, uintptr(unsafe.Pointer(&sp)), 0)
if sp < gp.stack.lo || gp.stack.hi <= sp { if sp < gp.stack.lo || gp.stack.hi <= sp {
break break
} }
} }
return _EXCEPTION_CONTINUE_SEARCH_SEH return windows.EXCEPTION_CONTINUE_SEARCH_SEH
} }
// It seems Windows searches ContinueHandler's list even // It seems Windows searches ContinueHandler's list even
@ -298,11 +291,11 @@ func sehhandler(_ *exceptionrecord, _ uint64, _ *context, dctxt *_DISPATCHER_CON
// It is nosplit for the same reason as exceptionhandler. // It is nosplit for the same reason as exceptionhandler.
// //
//go:nosplit //go:nosplit
func firstcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 { func firstcontinuehandler(info *windows.ExceptionRecord, r *windows.Context, gp *g) int32 {
if !isgoexception(info, r) { if !isgoexception(info, r) {
return _EXCEPTION_CONTINUE_SEARCH return windows.EXCEPTION_CONTINUE_SEARCH
} }
return _EXCEPTION_CONTINUE_EXECUTION return windows.EXCEPTION_CONTINUE_EXECUTION
} }
// lastcontinuehandler is reached, because runtime cannot handle // lastcontinuehandler is reached, because runtime cannot handle
@ -311,12 +304,12 @@ func firstcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
// It is nosplit for the same reason as exceptionhandler. // It is nosplit for the same reason as exceptionhandler.
// //
//go:nosplit //go:nosplit
func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 { func lastcontinuehandler(info *windows.ExceptionRecord, r *windows.Context, gp *g) int32 {
if islibrary || isarchive { if islibrary || isarchive {
// Go DLL/archive has been loaded in a non-go program. // Go DLL/archive has been loaded in a non-go program.
// If the exception does not originate from go, the go runtime // If the exception does not originate from go, the go runtime
// should not take responsibility of crashing the process. // should not take responsibility of crashing the process.
return _EXCEPTION_CONTINUE_SEARCH return windows.EXCEPTION_CONTINUE_SEARCH
} }
// VEH is called before SEH, but arm64 MSVC DLLs use SEH to trap // VEH is called before SEH, but arm64 MSVC DLLs use SEH to trap
@ -325,9 +318,9 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
// arm64 and it's an illegal instruction and this is coming from // arm64 and it's an illegal instruction and this is coming from
// non-Go code, then assume it's this runtime probing happen, and // non-Go code, then assume it's this runtime probing happen, and
// pass that onward to SEH. // pass that onward to SEH.
if GOARCH == "arm64" && info.exceptioncode == _EXCEPTION_ILLEGAL_INSTRUCTION && if GOARCH == "arm64" && info.ExceptionCode == windows.EXCEPTION_ILLEGAL_INSTRUCTION &&
(r.ip() < firstmoduledata.text || firstmoduledata.etext < r.ip()) { (r.PC() < firstmoduledata.text || firstmoduledata.etext < r.PC()) {
return _EXCEPTION_CONTINUE_SEARCH return windows.EXCEPTION_CONTINUE_SEARCH
} }
winthrow(info, r, gp) winthrow(info, r, gp)
@ -337,7 +330,7 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
// Always called on g0. gp is the G where the exception occurred. // Always called on g0. gp is the G where the exception occurred.
// //
//go:nosplit //go:nosplit
func winthrow(info *exceptionrecord, r *context, gp *g) { func winthrow(info *windows.ExceptionRecord, r *windows.Context, gp *g) {
g0 := getg() g0 := getg()
if panicking.Load() != 0 { // traceback already printed if panicking.Load() != 0 { // traceback already printed
@ -352,9 +345,9 @@ func winthrow(info *exceptionrecord, r *context, gp *g) {
g0.stackguard0 = g0.stack.lo + stackGuard g0.stackguard0 = g0.stack.lo + stackGuard
g0.stackguard1 = g0.stackguard0 g0.stackguard1 = g0.stackguard0
print("Exception ", hex(info.exceptioncode), " ", hex(info.exceptioninformation[0]), " ", hex(info.exceptioninformation[1]), " ", hex(r.ip()), "\n") print("Exception ", hex(info.ExceptionCode), " ", hex(info.ExceptionInformation[0]), " ", hex(info.ExceptionInformation[1]), " ", hex(r.PC()), "\n")
print("PC=", hex(r.ip()), "\n") print("PC=", hex(r.PC()), "\n")
if g0.m.incgo && gp == g0.m.g0 && g0.m.curg != nil { if g0.m.incgo && gp == g0.m.g0 && g0.m.curg != nil {
if iscgo { if iscgo {
print("signal arrived during external code execution\n") print("signal arrived during external code execution\n")
@ -368,7 +361,7 @@ func winthrow(info *exceptionrecord, r *context, gp *g) {
level, _, docrash := gotraceback() level, _, docrash := gotraceback()
if level > 0 { if level > 0 {
tracebacktrap(r.ip(), r.sp(), r.lr(), gp) tracebacktrap(r.PC(), r.SP(), r.LR(), gp)
tracebackothers(gp) tracebackothers(gp)
dumpregs(r) dumpregs(r)
} }
@ -387,7 +380,7 @@ func sigpanic() {
} }
switch gp.sig { switch gp.sig {
case _EXCEPTION_ACCESS_VIOLATION, _EXCEPTION_IN_PAGE_ERROR: case windows.EXCEPTION_ACCESS_VIOLATION, windows.EXCEPTION_IN_PAGE_ERROR:
if gp.sigcode1 < 0x1000 { if gp.sigcode1 < 0x1000 {
panicmem() panicmem()
} }
@ -403,15 +396,15 @@ func sigpanic() {
print("unexpected fault address ", hex(gp.sigcode1), "\n") print("unexpected fault address ", hex(gp.sigcode1), "\n")
} }
throw("fault") throw("fault")
case _EXCEPTION_INT_DIVIDE_BY_ZERO: case windows.EXCEPTION_INT_DIVIDE_BY_ZERO:
panicdivide() panicdivide()
case _EXCEPTION_INT_OVERFLOW: case windows.EXCEPTION_INT_OVERFLOW:
panicoverflow() panicoverflow()
case _EXCEPTION_FLT_DENORMAL_OPERAND, case windows.EXCEPTION_FLT_DENORMAL_OPERAND,
_EXCEPTION_FLT_DIVIDE_BY_ZERO, windows.EXCEPTION_FLT_DIVIDE_BY_ZERO,
_EXCEPTION_FLT_INEXACT_RESULT, windows.EXCEPTION_FLT_INEXACT_RESULT,
_EXCEPTION_FLT_OVERFLOW, windows.EXCEPTION_FLT_OVERFLOW,
_EXCEPTION_FLT_UNDERFLOW: windows.EXCEPTION_FLT_UNDERFLOW:
panicfloat() panicfloat()
} }
throw("fault") throw("fault")
@ -444,29 +437,28 @@ func crash() {
// This provides the expected exit status for the shell. // This provides the expected exit status for the shell.
// //
//go:nosplit //go:nosplit
func dieFromException(info *exceptionrecord, r *context) { func dieFromException(info *windows.ExceptionRecord, r *windows.Context) {
if info == nil { if info == nil {
gp := getg() gp := getg()
if gp.sig != 0 { if gp.sig != 0 {
// Try to reconstruct an exception record from // Try to reconstruct an exception record from
// the exception information stored in gp. // the exception information stored in gp.
info = &exceptionrecord{ info = &windows.ExceptionRecord{
exceptionaddress: gp.sigpc, ExceptionAddress: gp.sigpc,
exceptioncode: gp.sig, ExceptionCode: gp.sig,
numberparameters: 2, NumberParameters: 2,
} }
info.exceptioninformation[0] = gp.sigcode0 info.ExceptionInformation[0] = gp.sigcode0
info.exceptioninformation[1] = gp.sigcode1 info.ExceptionInformation[1] = gp.sigcode1
} else { } else {
// By default, a failing Go application exits with exit code 2. // By default, a failing Go application exits with exit code 2.
// Use this value when gp does not contain exception info. // Use this value when gp does not contain exception info.
info = &exceptionrecord{ info = &windows.ExceptionRecord{
exceptioncode: 2, ExceptionCode: 2,
} }
} }
} }
const FAIL_FAST_GENERATE_EXCEPTION_ADDRESS = 0x1 stdcall(_RaiseFailFastException, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(r)), windows.FAIL_FAST_GENERATE_EXCEPTION_ADDRESS)
stdcall(_RaiseFailFastException, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(r)), FAIL_FAST_GENERATE_EXCEPTION_ADDRESS)
} }
// gsignalStack is unused on Windows. // gsignalStack is unused on Windows.

View file

@ -0,0 +1,28 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import "internal/runtime/syscall/windows"
func prepareContextForSigResume(c *windows.Context) {
c.Edx = c.Esp
c.Ecx = c.Eip
}
func dumpregs(r *windows.Context) {
print("eax ", hex(r.Eax), "\n")
print("ebx ", hex(r.Ebx), "\n")
print("ecx ", hex(r.Ecx), "\n")
print("edx ", hex(r.Edx), "\n")
print("edi ", hex(r.Edi), "\n")
print("esi ", hex(r.Esi), "\n")
print("ebp ", hex(r.Ebp), "\n")
print("esp ", hex(r.Esp), "\n")
print("eip ", hex(r.Eip), "\n")
print("eflags ", hex(r.EFlags), "\n")
print("cs ", hex(r.SegCs), "\n")
print("fs ", hex(r.SegFs), "\n")
print("gs ", hex(r.SegGs), "\n")
}

View file

@ -0,0 +1,36 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import "internal/runtime/syscall/windows"
func prepareContextForSigResume(c *windows.Context) {
c.R8 = c.Rsp
c.R9 = c.Rip
}
func dumpregs(r *windows.Context) {
print("rax ", hex(r.Rax), "\n")
print("rbx ", hex(r.Rbx), "\n")
print("rcx ", hex(r.Rcx), "\n")
print("rdx ", hex(r.Rdx), "\n")
print("rdi ", hex(r.Rdi), "\n")
print("rsi ", hex(r.Rsi), "\n")
print("rbp ", hex(r.Rbp), "\n")
print("rsp ", hex(r.Rsp), "\n")
print("r8 ", hex(r.R8), "\n")
print("r9 ", hex(r.R9), "\n")
print("r10 ", hex(r.R10), "\n")
print("r11 ", hex(r.R11), "\n")
print("r12 ", hex(r.R12), "\n")
print("r13 ", hex(r.R13), "\n")
print("r14 ", hex(r.R14), "\n")
print("r15 ", hex(r.R15), "\n")
print("rip ", hex(r.Rip), "\n")
print("rflags ", hex(r.EFlags), "\n")
print("cs ", hex(r.SegCs), "\n")
print("fs ", hex(r.SegFs), "\n")
print("gs ", hex(r.SegGs), "\n")
}

View file

@ -0,0 +1,49 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package runtime
import "internal/runtime/syscall/windows"
func prepareContextForSigResume(c *windows.Context) {
c.X[0] = c.XSp
c.X[1] = c.Pc
}
func dumpregs(r *windows.Context) {
print("r0 ", hex(r.X[0]), "\n")
print("r1 ", hex(r.X[1]), "\n")
print("r2 ", hex(r.X[2]), "\n")
print("r3 ", hex(r.X[3]), "\n")
print("r4 ", hex(r.X[4]), "\n")
print("r5 ", hex(r.X[5]), "\n")
print("r6 ", hex(r.X[6]), "\n")
print("r7 ", hex(r.X[7]), "\n")
print("r8 ", hex(r.X[8]), "\n")
print("r9 ", hex(r.X[9]), "\n")
print("r10 ", hex(r.X[10]), "\n")
print("r11 ", hex(r.X[11]), "\n")
print("r12 ", hex(r.X[12]), "\n")
print("r13 ", hex(r.X[13]), "\n")
print("r14 ", hex(r.X[14]), "\n")
print("r15 ", hex(r.X[15]), "\n")
print("r16 ", hex(r.X[16]), "\n")
print("r17 ", hex(r.X[17]), "\n")
print("r18 ", hex(r.X[18]), "\n")
print("r19 ", hex(r.X[19]), "\n")
print("r20 ", hex(r.X[20]), "\n")
print("r21 ", hex(r.X[21]), "\n")
print("r22 ", hex(r.X[22]), "\n")
print("r23 ", hex(r.X[23]), "\n")
print("r24 ", hex(r.X[24]), "\n")
print("r25 ", hex(r.X[25]), "\n")
print("r26 ", hex(r.X[26]), "\n")
print("r27 ", hex(r.X[27]), "\n")
print("r28 ", hex(r.X[28]), "\n")
print("r29 ", hex(r.X[29]), "\n")
print("lr ", hex(r.X[30]), "\n")
print("sp ", hex(r.XSp), "\n")
print("pc ", hex(r.Pc), "\n")
print("cpsr ", hex(r.Cpsr), "\n")
}