mirror of https://github.com/golang/go.git
runtime: support windows/arm
Updates #26148 Change-Id: I8f68b2c926c7b11dc86c9664ed7ff2d2f78b64b4 Reviewed-on: https://go-review.googlesource.com/128715 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
parent
37db664c6c
commit
d24ec86e4f
|
|
@ -3,7 +3,6 @@
|
|||
runtime/sys_windows_386.s: [386] profileloop: use of 4(SP) points beyond argument frame
|
||||
runtime/sys_windows_386.s: [386] ctrlhandler: 4(SP) should be _type+0(FP)
|
||||
runtime/sys_windows_386.s: [386] setldt: function setldt missing Go declaration
|
||||
runtime/zcallback_windows.s: [386] callbackasm: function callbackasm missing Go declaration
|
||||
runtime/sys_windows_386.s: [386] callbackasm1+0: function callbackasm1+0 missing Go declaration
|
||||
runtime/sys_windows_386.s: [386] tstart: function tstart missing Go declaration
|
||||
runtime/sys_windows_386.s: [386] tstart_stdcall: RET without writing to 4-byte ret+4(FP)
|
||||
|
|
|
|||
|
|
@ -6,4 +6,3 @@ runtime/sys_windows_amd64.s: [amd64] callbackasm1: function callbackasm1 missing
|
|||
runtime/sys_windows_amd64.s: [amd64] tstart_stdcall: RET without writing to 4-byte ret+8(FP)
|
||||
runtime/sys_windows_amd64.s: [amd64] settls: function settls missing Go declaration
|
||||
runtime/sys_windows_amd64.s: [amd64] cannot check cross-package assembly function: now is in package time
|
||||
runtime/zcallback_windows.s: [amd64] callbackasm: function callbackasm missing Go declaration
|
||||
|
|
|
|||
|
|
@ -784,6 +784,9 @@ TEXT setg<>(SB),NOSPLIT|NOFRAME,$0-0
|
|||
MOVW R0, g
|
||||
|
||||
// Save g to thread-local storage.
|
||||
#ifdef GOOS_windows
|
||||
B runtime·save_g(SB)
|
||||
#else
|
||||
MOVB runtime·iscgo(SB), R0
|
||||
CMP $0, R0
|
||||
B.EQ 2(PC)
|
||||
|
|
@ -791,6 +794,7 @@ TEXT setg<>(SB),NOSPLIT|NOFRAME,$0-0
|
|||
|
||||
MOVW g, R0
|
||||
RET
|
||||
#endif
|
||||
|
||||
TEXT runtime·emptyfunc(SB),0,$0-0
|
||||
RET
|
||||
|
|
|
|||
|
|
@ -104,6 +104,9 @@ type context struct {
|
|||
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) setip(x uintptr) { c.eip = uint32(x) }
|
||||
func (c *context) setsp(x uintptr) { c.esp = uint32(x) }
|
||||
|
||||
|
|
|
|||
|
|
@ -119,6 +119,9 @@ type context struct {
|
|||
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) setip(x uintptr) { c.rip = uint64(x) }
|
||||
func (c *context) setsp(x uintptr) { c.rsp = uint64(x) }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,149 @@
|
|||
// 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
|
||||
|
||||
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
|
||||
_CTRL_C_EVENT = 0x0
|
||||
_CTRL_BREAK_EVENT = 0x1
|
||||
|
||||
_CONTEXT_CONTROL = 0x10001
|
||||
_CONTEXT_FULL = 0x10007
|
||||
|
||||
_EXCEPTION_ACCESS_VIOLATION = 0xc0000005
|
||||
_EXCEPTION_BREAKPOINT = 0x80000003
|
||||
_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
|
||||
)
|
||||
|
||||
type systeminfo struct {
|
||||
anon0 [4]byte
|
||||
dwpagesize uint32
|
||||
lpminimumapplicationaddress *byte
|
||||
lpmaximumapplicationaddress *byte
|
||||
dwactiveprocessormask uint32
|
||||
dwnumberofprocessors uint32
|
||||
dwprocessortype uint32
|
||||
dwallocationgranularity uint32
|
||||
wprocessorlevel uint16
|
||||
wprocessorrevision uint16
|
||||
}
|
||||
|
||||
type exceptionrecord struct {
|
||||
exceptioncode uint32
|
||||
exceptionflags uint32
|
||||
exceptionrecord *exceptionrecord
|
||||
exceptionaddress *byte
|
||||
numberparameters uint32
|
||||
exceptioninformation [15]uint32
|
||||
}
|
||||
|
||||
type neon128 struct {
|
||||
low uint64
|
||||
high int64
|
||||
}
|
||||
|
||||
type context struct {
|
||||
contextflags uint32
|
||||
r0 uint32
|
||||
r1 uint32
|
||||
r2 uint32
|
||||
r3 uint32
|
||||
r4 uint32
|
||||
r5 uint32
|
||||
r6 uint32
|
||||
r7 uint32
|
||||
r8 uint32
|
||||
r9 uint32
|
||||
r10 uint32
|
||||
r11 uint32
|
||||
r12 uint32
|
||||
|
||||
spr uint32
|
||||
lrr uint32
|
||||
pc uint32
|
||||
cpsr uint32
|
||||
|
||||
fpscr uint32
|
||||
padding uint32
|
||||
|
||||
floatNeon [16]neon128
|
||||
|
||||
bvr [8]uint32
|
||||
bcr [8]uint32
|
||||
wvr [1]uint32
|
||||
wcr [1]uint32
|
||||
padding2 [2]uint32
|
||||
}
|
||||
|
||||
func (c *context) ip() uintptr { return uintptr(c.pc) }
|
||||
func (c *context) sp() uintptr { return uintptr(c.spr) }
|
||||
func (c *context) lr() uintptr { return uintptr(c.lrr) }
|
||||
|
||||
func (c *context) setip(x uintptr) { c.pc = uint32(x) }
|
||||
func (c *context) setsp(x uintptr) { c.spr = uint32(x) }
|
||||
|
||||
func dumpregs(r *context) {
|
||||
print("r0 ", hex(r.r0), "\n")
|
||||
print("r1 ", hex(r.r1), "\n")
|
||||
print("r2 ", hex(r.r2), "\n")
|
||||
print("r3 ", hex(r.r3), "\n")
|
||||
print("r4 ", hex(r.r4), "\n")
|
||||
print("r5 ", hex(r.r5), "\n")
|
||||
print("r6 ", hex(r.r6), "\n")
|
||||
print("r7 ", hex(r.r7), "\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("sp ", hex(r.spr), "\n")
|
||||
print("lr ", hex(r.lrr), "\n")
|
||||
print("pc ", hex(r.pc), "\n")
|
||||
print("cpsr ", hex(r.cpsr), "\n")
|
||||
}
|
||||
|
||||
type overlapped struct {
|
||||
internal uint32
|
||||
internalhigh uint32
|
||||
anon0 [8]byte
|
||||
hevent *byte
|
||||
}
|
||||
|
||||
type memoryBasicInformation struct {
|
||||
baseAddress uintptr
|
||||
allocationBase uintptr
|
||||
allocationProtect uint32
|
||||
regionSize uintptr
|
||||
state uint32
|
||||
protect uint32
|
||||
type_ uint32
|
||||
}
|
||||
|
||||
func stackcheck() {
|
||||
// TODO: not implemented on ARM
|
||||
}
|
||||
|
|
@ -43,6 +43,7 @@ const (
|
|||
//go:cgo_import_dynamic runtime._SetWaitableTimer SetWaitableTimer%6 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._SuspendThread SuspendThread%1 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._SwitchToThread SwitchToThread%0 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._TlsAlloc TlsAlloc%0 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._VirtualAlloc VirtualAlloc%4 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._VirtualFree VirtualFree%3 "kernel32.dll"
|
||||
//go:cgo_import_dynamic runtime._VirtualQuery VirtualQuery%3 "kernel32.dll"
|
||||
|
|
@ -91,6 +92,7 @@ var (
|
|||
_SetWaitableTimer,
|
||||
_SuspendThread,
|
||||
_SwitchToThread,
|
||||
_TlsAlloc,
|
||||
_VirtualAlloc,
|
||||
_VirtualFree,
|
||||
_VirtualQuery,
|
||||
|
|
@ -860,14 +862,34 @@ func profilem(mp *m) {
|
|||
var r *context
|
||||
rbuf := make([]byte, unsafe.Sizeof(*r)+15)
|
||||
|
||||
tls := &mp.tls[0]
|
||||
gp := *((**g)(unsafe.Pointer(tls)))
|
||||
|
||||
// align Context to 16 bytes
|
||||
r = (*context)(unsafe.Pointer((uintptr(unsafe.Pointer(&rbuf[15]))) &^ 15))
|
||||
r.contextflags = _CONTEXT_CONTROL
|
||||
stdcall2(_GetThreadContext, mp.thread, uintptr(unsafe.Pointer(r)))
|
||||
sigprof(r.ip(), r.sp(), 0, gp, mp)
|
||||
|
||||
var gp *g
|
||||
switch GOARCH {
|
||||
default:
|
||||
panic("unsupported architecture")
|
||||
case "arm":
|
||||
// TODO(jordanrh1): this is incorrect when Go is executing
|
||||
// on the system or signal stacks because curg returns
|
||||
// the current user g. The true g is stored in thread
|
||||
// local storage, which we cannot access from another CPU.
|
||||
// We cannot pull R10 from the thread context because
|
||||
// it might be executing C code, in which case R10
|
||||
// would not be g.
|
||||
gp = mp.curg
|
||||
case "386", "amd64":
|
||||
tls := &mp.tls[0]
|
||||
gp = *((**g)(unsafe.Pointer(tls)))
|
||||
}
|
||||
|
||||
if gp == nil {
|
||||
sigprofNonGoPC(r.ip())
|
||||
} else {
|
||||
sigprof(r.ip(), r.sp(), 0, gp, mp)
|
||||
}
|
||||
}
|
||||
|
||||
func profileloop1(param uintptr) uint32 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
// 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
|
||||
|
||||
//go:nosplit
|
||||
func cputicks() int64 {
|
||||
return nanotime()
|
||||
}
|
||||
|
||||
func checkgoarm() {
|
||||
if goarm < 7 {
|
||||
print("Need atomic synchronization instructions, coprocessor ",
|
||||
"access instructions. Recompile using GOARM=7.\n")
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
// 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.
|
||||
|
||||
#include "go_asm.h"
|
||||
#include "go_tls.h"
|
||||
#include "textflag.h"
|
||||
|
||||
// This is the entry point for the program from the
|
||||
// kernel for an ordinary -buildmode=exe program.
|
||||
TEXT _rt0_arm_windows(SB),NOSPLIT|NOFRAME,$0
|
||||
B ·rt0_go(SB)
|
||||
|
|
@ -27,7 +27,7 @@ func lastcontinuetramp()
|
|||
|
||||
func initExceptionHandler() {
|
||||
stdcall2(_AddVectoredExceptionHandler, 1, funcPC(exceptiontramp))
|
||||
if _AddVectoredContinueHandler == nil || unsafe.Sizeof(&_AddVectoredContinueHandler) == 4 {
|
||||
if _AddVectoredContinueHandler == nil || GOARCH == "386" {
|
||||
// use SetUnhandledExceptionFilter for windows-386 or
|
||||
// if VectoredContinueHandler is unavailable.
|
||||
// note: SetUnhandledExceptionFilter handler won't be called, if debugging.
|
||||
|
|
@ -177,9 +177,15 @@ func lastcontinuehandler(info *exceptionrecord, r *context, gp *g) int32 {
|
|||
}
|
||||
print("\n")
|
||||
|
||||
// TODO(jordanrh1): This may be needed for 386/AMD64 as well.
|
||||
if GOARCH == "arm" {
|
||||
_g_.m.throwing = 1
|
||||
_g_.m.caughtsig.set(gp)
|
||||
}
|
||||
|
||||
level, _, docrash := gotraceback()
|
||||
if level > 0 {
|
||||
tracebacktrap(r.ip(), r.sp(), 0, gp)
|
||||
tracebacktrap(r.ip(), r.sp(), r.lr(), gp)
|
||||
tracebackothers(gp)
|
||||
dumpregs(r)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,605 @@
|
|||
// 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.
|
||||
|
||||
#include "go_asm.h"
|
||||
#include "go_tls.h"
|
||||
#include "textflag.h"
|
||||
|
||||
// void runtime·asmstdcall(void *c);
|
||||
TEXT runtime·asmstdcall(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVM.DB.W [R4, R5, R14], (R13) // push {r4, r5, lr}
|
||||
MOVW R0, R4 // put libcall * in r4
|
||||
MOVW R13, R5 // save stack pointer in r5
|
||||
|
||||
// SetLastError(0)
|
||||
MOVW $0, R0
|
||||
MRC 15, 0, R1, C13, C0, 2
|
||||
MOVW R0, 0x34(R1)
|
||||
|
||||
MOVW 8(R4), R12 // libcall->args
|
||||
|
||||
// Do we have more than 4 arguments?
|
||||
MOVW 4(R4), R0 // libcall->n
|
||||
SUB.S $4, R0, R2
|
||||
BLE loadregs
|
||||
|
||||
// Reserve stack space for remaining args
|
||||
SUB R2<<2, R13
|
||||
BIC $0x7, R13 // alignment for ABI
|
||||
|
||||
// R0: count of arguments
|
||||
// R1:
|
||||
// R2: loop counter, from 0 to (n-4)
|
||||
// R3: scratch
|
||||
// R4: pointer to libcall struct
|
||||
// R12: libcall->args
|
||||
MOVW $0, R2
|
||||
stackargs:
|
||||
ADD $4, R2, R3 // r3 = args[4 + i]
|
||||
MOVW R3<<2(R12), R3
|
||||
MOVW R3, R2<<2(R13) // stack[i] = r3
|
||||
|
||||
ADD $1, R2 // i++
|
||||
SUB $4, R0, R3 // while (i < (n - 4))
|
||||
CMP R3, R2
|
||||
BLT stackargs
|
||||
|
||||
loadregs:
|
||||
CMP $3, R0
|
||||
MOVW.GT 12(R12), R3
|
||||
|
||||
CMP $2, R0
|
||||
MOVW.GT 8(R12), R2
|
||||
|
||||
CMP $1, R0
|
||||
MOVW.GT 4(R12), R1
|
||||
|
||||
CMP $0, R0
|
||||
MOVW.GT 0(R12), R0
|
||||
|
||||
BIC $0x7, R13 // alignment for ABI
|
||||
MOVW 0(R4), R12 // branch to libcall->fn
|
||||
BL (R12)
|
||||
|
||||
MOVW R5, R13 // free stack space
|
||||
MOVW R0, 12(R4) // save return value to libcall->r1
|
||||
MOVW R1, 16(R4)
|
||||
|
||||
// GetLastError
|
||||
MRC 15, 0, R1, C13, C0, 2
|
||||
MOVW 0x34(R1), R0
|
||||
MOVW R0, 20(R4) // store in libcall->err
|
||||
|
||||
MOVM.IA.W (R13), [R4, R5, R15]
|
||||
|
||||
TEXT runtime·badsignal2(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVM.DB.W [R4, R14], (R13) // push {r4, lr}
|
||||
MOVW R13, R4 // save original stack pointer
|
||||
SUB $8, R13 // space for 2 variables
|
||||
BIC $0x7, R13 // alignment for ABI
|
||||
|
||||
// stderr
|
||||
MOVW runtime·_GetStdHandle(SB), R1
|
||||
MOVW $-12, R0
|
||||
BL (R1)
|
||||
|
||||
MOVW $runtime·badsignalmsg(SB), R1 // lpBuffer
|
||||
MOVW $runtime·badsignallen(SB), R2 // lpNumberOfBytesToWrite
|
||||
MOVW (R2), R2
|
||||
ADD $0x4, R13, R3 // lpNumberOfBytesWritten
|
||||
MOVW $0, R12 // lpOverlapped
|
||||
MOVW R12, (R13)
|
||||
|
||||
MOVW runtime·_WriteFile(SB), R12
|
||||
BL (R12)
|
||||
|
||||
MOVW R4, R13 // restore SP
|
||||
MOVM.IA.W (R13), [R4, R15] // pop {r4, pc}
|
||||
|
||||
TEXT runtime·getlasterror(SB),NOSPLIT,$0
|
||||
MRC 15, 0, R0, C13, C0, 2
|
||||
MOVW 0x34(R0), R0
|
||||
MOVW R0, ret+0(FP)
|
||||
RET
|
||||
|
||||
TEXT runtime·setlasterror(SB),NOSPLIT|NOFRAME,$0
|
||||
MRC 15, 0, R1, C13, C0, 2
|
||||
MOVW R0, 0x34(R1)
|
||||
RET
|
||||
|
||||
// Called by Windows as a Vectored Exception Handler (VEH).
|
||||
// First argument is pointer to struct containing
|
||||
// exception record and context pointers.
|
||||
// Handler function is stored in R1
|
||||
// Return 0 for 'not handled', -1 for handled.
|
||||
// int32_t sigtramp(
|
||||
// PEXCEPTION_POINTERS ExceptionInfo,
|
||||
// func *GoExceptionHandler);
|
||||
TEXT runtime·sigtramp(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVM.DB.W [R0, R4-R11, R14], (R13) // push {r0, r4-r11, lr} (SP-=40)
|
||||
SUB $(8+20), R13 // reserve space for g, sp, and
|
||||
// parameters/retval to go call
|
||||
|
||||
MOVW R0, R6 // Save param0
|
||||
MOVW R1, R7 // Save param1
|
||||
|
||||
BL runtime·load_g(SB)
|
||||
CMP $0, g // is there a current g?
|
||||
BL.EQ runtime·badsignal2(SB)
|
||||
|
||||
// save g and SP in case of stack switch
|
||||
MOVW R13, 24(R13)
|
||||
MOVW g, 20(R13)
|
||||
|
||||
// do we need to switch to the g0 stack?
|
||||
MOVW g, R5 // R5 = g
|
||||
MOVW g_m(R5), R2 // R2 = m
|
||||
MOVW m_g0(R2), R4 // R4 = g0
|
||||
CMP R5, R4 // if curg == g0
|
||||
BEQ g0
|
||||
|
||||
// switch to g0 stack
|
||||
MOVW R4, g // g = g0
|
||||
MOVW (g_sched+gobuf_sp)(g), R3 // R3 = g->gobuf.sp
|
||||
BL runtime·save_g(SB)
|
||||
|
||||
// traceback will think that we've done PUSH and SUB
|
||||
// on this stack, so subtract them here to match.
|
||||
// (we need room for sighandler arguments anyway).
|
||||
// and re-save old SP for restoring later.
|
||||
SUB $(40+8+20), R3
|
||||
MOVW R13, 24(R3) // save old stack pointer
|
||||
MOVW R3, R13 // switch stack
|
||||
|
||||
g0:
|
||||
MOVW 0(R6), R2 // R2 = ExceptionPointers->ExceptionRecord
|
||||
MOVW 4(R6), R3 // R3 = ExceptionPointers->ContextRecord
|
||||
|
||||
// make it look like mstart called us on g0, to stop traceback
|
||||
MOVW $runtime·mstart(SB), R4
|
||||
|
||||
MOVW R4, 0(R13) // Save link register for traceback
|
||||
MOVW R2, 4(R13) // Move arg0 (ExceptionRecord) into position
|
||||
MOVW R3, 8(R13) // Move arg1 (ContextRecord) into position
|
||||
MOVW R5, 12(R13) // Move arg2 (original g) into position
|
||||
BL (R7) // Call the go routine
|
||||
MOVW 16(R13), R4 // Fetch return value from stack
|
||||
|
||||
// Compute the value of the g0 stack pointer after deallocating
|
||||
// this frame, then allocating 8 bytes. We may need to store
|
||||
// the resume SP and PC on the g0 stack to work around
|
||||
// control flow guard when we resume from the exception.
|
||||
ADD $(40+20), R13, R12
|
||||
|
||||
// switch back to original stack and g
|
||||
MOVW 24(R13), R13
|
||||
MOVW 20(R13), g
|
||||
BL runtime·save_g(SB)
|
||||
|
||||
done:
|
||||
MOVW R4, R0 // move retval into position
|
||||
ADD $(8 + 20), R13 // free locals
|
||||
MOVM.IA.W (R13), [R3, R4-R11, R14] // pop {r3, r4-r11, lr}
|
||||
|
||||
// if return value is CONTINUE_SEARCH, do not set up control
|
||||
// flow guard workaround
|
||||
CMP $0, R0
|
||||
BEQ return
|
||||
|
||||
// Check if we need to set up the control flow guard workaround.
|
||||
// On Windows/ARM, the stack pointer must lie within system
|
||||
// stack limits when we resume from exception.
|
||||
// Store the resume SP and PC on the g0 stack,
|
||||
// and return to returntramp on the g0 stack. returntramp
|
||||
// pops the saved PC and SP from the g0 stack, resuming execution
|
||||
// at the desired location.
|
||||
// If returntramp has already been set up by a previous exception
|
||||
// handler, don't clobber the stored SP and PC on the stack.
|
||||
MOVW 4(R3), R3 // PEXCEPTION_POINTERS->Context
|
||||
MOVW 0x40(R3), R2 // load PC from context record
|
||||
MOVW $runtime·returntramp(SB), R1
|
||||
CMP R1, R2
|
||||
B.EQ return // do not clobber saved SP/PC
|
||||
|
||||
// Save resume SP and PC on g0 stack
|
||||
MOVW 0x38(R3), R2 // load SP from context record
|
||||
MOVW R2, 0(R12) // Store resume SP on g0 stack
|
||||
MOVW 0x40(R3), R2 // load PC from context record
|
||||
MOVW R2, 4(R12) // Store resume PC on g0 stack
|
||||
|
||||
// Set up context record to return to returntramp on g0 stack
|
||||
MOVW R12, 0x38(R3) // save g0 stack pointer
|
||||
// in context record
|
||||
MOVW $runtime·returntramp(SB), R2 // save resume address
|
||||
MOVW R2, 0x40(R3) // in context record
|
||||
|
||||
return:
|
||||
B (R14) // return
|
||||
|
||||
//
|
||||
// Trampoline to resume execution from exception handler.
|
||||
// This is part of the control flow guard workaround.
|
||||
// It switches stacks and jumps to the continuation address.
|
||||
//
|
||||
TEXT runtime·returntramp(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVM.IA (R13), [R13, R15] // ldm sp, [sp, pc]
|
||||
|
||||
TEXT runtime·exceptiontramp(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVW $runtime·exceptionhandler(SB), R1
|
||||
B runtime·sigtramp(SB)
|
||||
|
||||
TEXT runtime·firstcontinuetramp(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVW $runtime·firstcontinuehandler(SB), R1
|
||||
B runtime·sigtramp(SB)
|
||||
|
||||
TEXT runtime·lastcontinuetramp(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVW $runtime·lastcontinuehandler(SB), R1
|
||||
B runtime·sigtramp(SB)
|
||||
|
||||
TEXT runtime·ctrlhandler(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVW $runtime·ctrlhandler1(SB), R1
|
||||
B runtime·externalthreadhandler(SB)
|
||||
|
||||
TEXT runtime·profileloop(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVW $runtime·profileloop1(SB), R1
|
||||
B runtime·externalthreadhandler(SB)
|
||||
|
||||
// int32 externalthreadhandler(uint32 arg, int (*func)(uint32))
|
||||
// stack layout:
|
||||
// +----------------+
|
||||
// | callee-save |
|
||||
// | registers |
|
||||
// +----------------+
|
||||
// | m |
|
||||
// +----------------+
|
||||
// 20| g |
|
||||
// +----------------+
|
||||
// 16| func ptr (r1) |
|
||||
// +----------------+
|
||||
// 12| argument (r0) |
|
||||
//---+----------------+
|
||||
// 8 | param1 |
|
||||
// +----------------+
|
||||
// 4 | param0 |
|
||||
// +----------------+
|
||||
// 0 | retval |
|
||||
// +----------------+
|
||||
//
|
||||
TEXT runtime·externalthreadhandler(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVM.DB.W [R4-R11, R14], (R13) // push {r4-r11, lr}
|
||||
SUB $(m__size + g__size + 20), R13 // space for locals
|
||||
MOVW R0, 12(R13)
|
||||
MOVW R1, 16(R13)
|
||||
|
||||
// zero out m and g structures
|
||||
ADD $20, R13, R0 // compute pointer to g
|
||||
MOVW R0, 4(R13)
|
||||
MOVW $(m__size + g__size), R0
|
||||
MOVW R0, 8(R13)
|
||||
BL runtime·memclrNoHeapPointers(SB)
|
||||
|
||||
// initialize m and g structures
|
||||
ADD $20, R13, R2 // R2 = g
|
||||
ADD $(20 + g__size), R13, R3 // R3 = m
|
||||
MOVW R2, m_g0(R3) // m->g0 = g
|
||||
MOVW R3, g_m(R2) // g->m = m
|
||||
MOVW R2, m_curg(R3) // m->curg = g
|
||||
|
||||
MOVW R2, g
|
||||
BL runtime·save_g(SB)
|
||||
|
||||
// set up stackguard stuff
|
||||
MOVW R13, R0
|
||||
MOVW R0, g_stack+stack_hi(g)
|
||||
SUB $(32*1024), R0
|
||||
MOVW R0, (g_stack+stack_lo)(g)
|
||||
MOVW R0, g_stackguard0(g)
|
||||
MOVW R0, g_stackguard1(g)
|
||||
|
||||
// move argument into position and call function
|
||||
MOVW 12(R13), R0
|
||||
MOVW R0, 4(R13)
|
||||
MOVW 16(R13), R1
|
||||
BL (R1)
|
||||
|
||||
// clear g
|
||||
MOVW $0, g
|
||||
BL runtime·save_g(SB)
|
||||
|
||||
MOVW 0(R13), R0 // load return value
|
||||
ADD $(m__size + g__size + 20), R13 // free locals
|
||||
MOVM.IA.W (R13), [R4-R11, R15] // pop {r4-r11, pc}
|
||||
|
||||
GLOBL runtime·cbctxts(SB), NOPTR, $4
|
||||
|
||||
TEXT runtime·callbackasm1(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVM.DB.W [R4-R11, R14], (R13) // push {r4-r11, lr}
|
||||
SUB $36, R13 // space for locals
|
||||
|
||||
// save callback arguments to stack. We currently support up to 4 arguments
|
||||
ADD $16, R13, R4
|
||||
MOVM.IA [R0-R3], (R4)
|
||||
|
||||
// load cbctxts[i]. The trampoline in zcallback_windows.s puts the callback
|
||||
// index in R12
|
||||
MOVW runtime·cbctxts(SB), R4
|
||||
MOVW R12<<2(R4), R4 // R4 holds pointer to wincallbackcontext structure
|
||||
|
||||
// extract callback context
|
||||
MOVW wincallbackcontext_argsize(R4), R5
|
||||
MOVW wincallbackcontext_gobody(R4), R4
|
||||
|
||||
// we currently support up to 4 arguments
|
||||
CMP $(4 * 4), R5
|
||||
BL.GT runtime·abort(SB)
|
||||
|
||||
// extend argsize by size of return value
|
||||
ADD $4, R5
|
||||
|
||||
// Build 'type args struct'
|
||||
MOVW R4, 4(R13) // fn
|
||||
ADD $16, R13, R0 // arg (points to r0-r3, ret on stack)
|
||||
MOVW R0, 8(R13)
|
||||
MOVW R5, 12(R13) // argsize
|
||||
|
||||
BL runtime·load_g(SB)
|
||||
BL runtime·cgocallback_gofunc(SB)
|
||||
|
||||
ADD $16, R13, R0 // load arg
|
||||
MOVW 12(R13), R1 // load argsize
|
||||
SUB $4, R1 // offset to return value
|
||||
MOVW R1<<0(R0), R0 // load return value
|
||||
|
||||
ADD $36, R13 // free locals
|
||||
MOVM.IA.W (R13), [R4-R11, R15] // pop {r4-r11, pc}
|
||||
|
||||
// uint32 tstart_stdcall(M *newm);
|
||||
TEXT runtime·tstart_stdcall(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVM.DB.W [R4-R11, R14], (R13) // push {r4-r11, lr}
|
||||
|
||||
MOVW m_g0(R0), g
|
||||
MOVW R0, g_m(g)
|
||||
BL runtime·save_g(SB)
|
||||
|
||||
// Layout new m scheduler stack on os stack.
|
||||
MOVW R13, R0
|
||||
MOVW R0, g_stack+stack_hi(g)
|
||||
SUB $(64*1024), R0
|
||||
MOVW R0, (g_stack+stack_lo)(g)
|
||||
MOVW R0, g_stackguard0(g)
|
||||
MOVW R0, g_stackguard1(g)
|
||||
|
||||
BL runtime·emptyfunc(SB) // fault if stack check is wrong
|
||||
BL runtime·mstart(SB)
|
||||
|
||||
// Exit the thread.
|
||||
MOVW $0, R0
|
||||
MOVM.IA.W (R13), [R4-R11, R15] // pop {r4-r11, pc}
|
||||
|
||||
// onosstack calls fn on OS stack.
|
||||
// adapted from asm_arm.s : systemstack
|
||||
// func onosstack(fn unsafe.Pointer, arg uint32)
|
||||
TEXT runtime·onosstack(SB),NOSPLIT,$0
|
||||
MOVW fn+0(FP), R5 // R5 = fn
|
||||
MOVW arg+4(FP), R6 // R6 = arg
|
||||
|
||||
// This function can be called when there is no g,
|
||||
// for example, when we are handling a callback on a non-go thread.
|
||||
// In this case we're already on the system stack.
|
||||
CMP $0, g
|
||||
BEQ noswitch
|
||||
|
||||
MOVW g_m(g), R1 // R1 = m
|
||||
|
||||
MOVW m_gsignal(R1), R2 // R2 = gsignal
|
||||
CMP g, R2
|
||||
B.EQ noswitch
|
||||
|
||||
MOVW m_g0(R1), R2 // R2 = g0
|
||||
CMP g, R2
|
||||
B.EQ noswitch
|
||||
|
||||
MOVW m_curg(R1), R3
|
||||
CMP g, R3
|
||||
B.EQ switch
|
||||
|
||||
// Bad: g is not gsignal, not g0, not curg. What is it?
|
||||
// Hide call from linker nosplit analysis.
|
||||
MOVW $runtime·badsystemstack(SB), R0
|
||||
BL (R0)
|
||||
B runtime·abort(SB)
|
||||
|
||||
switch:
|
||||
// save our state in g->sched. Pretend to
|
||||
// be systemstack_switch if the G stack is scanned.
|
||||
MOVW $runtime·systemstack_switch(SB), R3
|
||||
ADD $4, R3, R3 // get past push {lr}
|
||||
MOVW R3, (g_sched+gobuf_pc)(g)
|
||||
MOVW R13, (g_sched+gobuf_sp)(g)
|
||||
MOVW LR, (g_sched+gobuf_lr)(g)
|
||||
MOVW g, (g_sched+gobuf_g)(g)
|
||||
|
||||
// switch to g0
|
||||
MOVW R2, g
|
||||
MOVW (g_sched+gobuf_sp)(R2), R3
|
||||
// make it look like mstart called systemstack on g0, to stop traceback
|
||||
SUB $4, R3, R3
|
||||
MOVW $runtime·mstart(SB), R4
|
||||
MOVW R4, 0(R3)
|
||||
MOVW R3, R13
|
||||
|
||||
// call target function
|
||||
MOVW R6, R0 // arg
|
||||
BL (R5)
|
||||
|
||||
// switch back to g
|
||||
MOVW g_m(g), R1
|
||||
MOVW m_curg(R1), g
|
||||
MOVW (g_sched+gobuf_sp)(g), R13
|
||||
MOVW $0, R3
|
||||
MOVW R3, (g_sched+gobuf_sp)(g)
|
||||
RET
|
||||
|
||||
noswitch:
|
||||
// Using a tail call here cleans up tracebacks since we won't stop
|
||||
// at an intermediate systemstack.
|
||||
MOVW.P 4(R13), R14 // restore LR
|
||||
MOVW R6, R0 // arg
|
||||
B (R5)
|
||||
|
||||
// Runs on OS stack. Duration (in 100ns units) is in R0.
|
||||
TEXT runtime·usleep2(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVM.DB.W [R4, R14], (R13) // push {r4, lr}
|
||||
MOVW R13, R4 // Save SP
|
||||
SUB $8, R13 // R13 = R13 - 8
|
||||
BIC $0x7, R13 // Align SP for ABI
|
||||
RSB $0, R0, R3 // R3 = -R0
|
||||
MOVW $0, R1 // R1 = FALSE (alertable)
|
||||
MOVW $-1, R0 // R0 = handle
|
||||
MOVW R13, R2 // R2 = pTime
|
||||
MOVW R3, 0(R2) // time_lo
|
||||
MOVW R0, 4(R2) // time_hi
|
||||
MOVW runtime·_NtWaitForSingleObject(SB), R3
|
||||
BL (R3)
|
||||
MOVW R4, R13 // Restore SP
|
||||
MOVM.IA.W (R13), [R4, R15] // pop {R4, pc}
|
||||
|
||||
// Runs on OS stack.
|
||||
TEXT runtime·switchtothread(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVM.DB.W [R4, R14], (R13) // push {R4, lr}
|
||||
MOVW R13, R4
|
||||
BIC $0x7, R13 // alignment for ABI
|
||||
MOVW runtime·_SwitchToThread(SB), R0
|
||||
BL (R0)
|
||||
MOVW R4, R13 // restore stack pointer
|
||||
MOVM.IA.W (R13), [R4, R15] // pop {R4, pc}
|
||||
|
||||
TEXT ·publicationBarrier(SB),NOSPLIT|NOFRAME,$0-0
|
||||
B runtime·armPublicationBarrier(SB)
|
||||
|
||||
// never called (cgo not supported)
|
||||
TEXT runtime·read_tls_fallback(SB),NOSPLIT|NOFRAME,$0
|
||||
MOVW $0xabcd, R0
|
||||
MOVW R0, (R0)
|
||||
RET
|
||||
|
||||
// See http://www.dcl.hpi.uni-potsdam.de/research/WRK/2007/08/getting-os-information-the-kuser_shared_data-structure/
|
||||
// Must read hi1, then lo, then hi2. The snapshot is valid if hi1 == hi2.
|
||||
#define _INTERRUPT_TIME 0x7ffe0008
|
||||
#define _SYSTEM_TIME 0x7ffe0014
|
||||
#define time_lo 0
|
||||
#define time_hi1 4
|
||||
#define time_hi2 8
|
||||
|
||||
TEXT runtime·nanotime(SB),NOSPLIT,$0-8
|
||||
MOVW $0, R0
|
||||
MOVB runtime·useQPCTime(SB), R0
|
||||
CMP $0, R0
|
||||
BNE useQPC
|
||||
MOVW $_INTERRUPT_TIME, R3
|
||||
loop:
|
||||
MOVW time_hi1(R3), R1
|
||||
MOVW time_lo(R3), R0
|
||||
MOVW time_hi2(R3), R2
|
||||
CMP R1, R2
|
||||
BNE loop
|
||||
|
||||
// wintime = R1:R0, multiply by 100
|
||||
MOVW $100, R2
|
||||
MULLU R0, R2, (R4, R3) // R4:R3 = R1:R0 * R2
|
||||
MULA R1, R2, R4, R4
|
||||
|
||||
// wintime*100 = R4:R3, subtract startNano and return
|
||||
MOVW runtime·startNano+0(SB), R0
|
||||
MOVW runtime·startNano+4(SB), R1
|
||||
SUB.S R0, R3
|
||||
SBC R1, R4
|
||||
MOVW R3, ret_lo+0(FP)
|
||||
MOVW R4, ret_hi+4(FP)
|
||||
RET
|
||||
useQPC:
|
||||
B runtime·nanotimeQPC(SB) // tail call
|
||||
RET
|
||||
|
||||
TEXT time·now(SB),NOSPLIT,$0-20
|
||||
MOVW $0, R0
|
||||
MOVB runtime·useQPCTime(SB), R0
|
||||
CMP $0, R0
|
||||
BNE useQPC
|
||||
MOVW $_INTERRUPT_TIME, R3
|
||||
loop:
|
||||
MOVW time_hi1(R3), R1
|
||||
MOVW time_lo(R3), R0
|
||||
MOVW time_hi2(R3), R2
|
||||
CMP R1, R2
|
||||
BNE loop
|
||||
|
||||
// wintime = R1:R0, multiply by 100
|
||||
MOVW $100, R2
|
||||
MULLU R0, R2, (R4, R3) // R4:R3 = R1:R0 * R2
|
||||
MULA R1, R2, R4, R4
|
||||
|
||||
// wintime*100 = R4:R3, subtract startNano and return
|
||||
MOVW runtime·startNano+0(SB), R0
|
||||
MOVW runtime·startNano+4(SB), R1
|
||||
SUB.S R0, R3
|
||||
SBC R1, R4
|
||||
MOVW R3, mono+12(FP)
|
||||
MOVW R4, mono+16(FP)
|
||||
|
||||
MOVW $_SYSTEM_TIME, R3
|
||||
wall:
|
||||
MOVW time_hi1(R3), R1
|
||||
MOVW time_lo(R3), R0
|
||||
MOVW time_hi2(R3), R2
|
||||
CMP R1, R2
|
||||
BNE wall
|
||||
|
||||
// w = R1:R0 in 100ns untis
|
||||
// convert to Unix epoch (but still 100ns units)
|
||||
#define delta 116444736000000000
|
||||
SUB.S $(delta & 0xFFFFFFFF), R0
|
||||
SBC $(delta >> 32), R1
|
||||
|
||||
// Convert to nSec
|
||||
MOVW $100, R2
|
||||
MULLU R0, R2, (R4, R3) // R4:R3 = R1:R0 * R2
|
||||
MULA R1, R2, R4, R4
|
||||
// w = R2:R1 in nSec
|
||||
MOVW R3, R1 // R4:R3 -> R2:R1
|
||||
MOVW R4, R2
|
||||
|
||||
// multiply nanoseconds by reciprocal of 10**9 (scaled by 2**61)
|
||||
// to get seconds (96 bit scaled result)
|
||||
MOVW $0x89705f41, R3 // 2**61 * 10**-9
|
||||
MULLU R1,R3,(R6,R5) // R7:R6:R5 = R2:R1 * R3
|
||||
MOVW $0,R7
|
||||
MULALU R2,R3,(R7,R6)
|
||||
|
||||
// unscale by discarding low 32 bits, shifting the rest by 29
|
||||
MOVW R6>>29,R6 // R7:R6 = (R7:R6:R5 >> 61)
|
||||
ORR R7<<3,R6
|
||||
MOVW R7>>29,R7
|
||||
|
||||
// subtract (10**9 * sec) from nsec to get nanosecond remainder
|
||||
MOVW $1000000000, R5 // 10**9
|
||||
MULLU R6,R5,(R9,R8) // R9:R8 = R7:R6 * R5
|
||||
MULA R7,R5,R9,R9
|
||||
SUB.S R8,R1 // R2:R1 -= R9:R8
|
||||
SBC R9,R2
|
||||
|
||||
// because reciprocal was a truncated repeating fraction, quotient
|
||||
// may be slightly too small -- adjust to make remainder < 10**9
|
||||
CMP R5,R1 // if remainder > 10**9
|
||||
SUB.HS R5,R1 // remainder -= 10**9
|
||||
ADD.HS $1,R6 // sec += 1
|
||||
|
||||
MOVW R6,sec_lo+0(FP)
|
||||
MOVW R7,sec_hi+4(FP)
|
||||
MOVW R1,nsec+8(FP)
|
||||
RET
|
||||
useQPC:
|
||||
B runtime·nanotimeQPC(SB) // tail call
|
||||
RET
|
||||
|
||||
|
|
@ -25,18 +25,32 @@ func (c *wincallbackcontext) setCleanstack(cleanstack bool) {
|
|||
var (
|
||||
cbs callbacks
|
||||
cbctxts **wincallbackcontext = &cbs.ctxt[0] // to simplify access to cbs.ctxt in sys_windows_*.s
|
||||
|
||||
callbackasm byte // type isn't really byte, it's code in runtime
|
||||
)
|
||||
|
||||
func callbackasm()
|
||||
|
||||
// callbackasmAddr returns address of runtime.callbackasm
|
||||
// function adjusted by i.
|
||||
// runtime.callbackasm is just a series of CALL instructions
|
||||
// (each is 5 bytes long), and we want callback to arrive at
|
||||
// On x86 and amd64, runtime.callbackasm is a series of CALL instructions,
|
||||
// and we want callback to arrive at
|
||||
// correspondent call instruction instead of start of
|
||||
// runtime.callbackasm.
|
||||
// On ARM, runtime.callbackasm is a series of mov and branch instructions.
|
||||
// R12 is loaded with the callback index. Each entry is two instructions,
|
||||
// hence 8 bytes.
|
||||
func callbackasmAddr(i int) uintptr {
|
||||
return uintptr(add(unsafe.Pointer(&callbackasm), uintptr(i*5)))
|
||||
var entrySize int
|
||||
switch GOARCH {
|
||||
default:
|
||||
panic("unsupported architecture")
|
||||
case "386", "amd64":
|
||||
entrySize = 5
|
||||
case "arm":
|
||||
// On ARM, each entry is a MOV instruction
|
||||
// followed by a branch instruction
|
||||
entrySize = 8
|
||||
}
|
||||
return funcPC(callbackasm) + uintptr(i*entrySize)
|
||||
}
|
||||
|
||||
//go:linkname compileCallback syscall.compileCallback
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@
|
|||
#ifdef GOOS_darwin
|
||||
#define TLSG_IS_VARIABLE
|
||||
#endif
|
||||
#ifdef GOOS_windows
|
||||
#define TLSG_IS_VARIABLE
|
||||
#endif
|
||||
|
||||
// save_g saves the g register into pthread-provided
|
||||
// thread-local memory, so that we can call externally compiled
|
||||
|
|
@ -35,6 +38,17 @@ TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0
|
|||
// nothing to do as nacl/arm does not use TLS at all.
|
||||
MOVW g, R0 // preserve R0 across call to setg<>
|
||||
RET
|
||||
#else
|
||||
#ifdef GOOS_windows
|
||||
// Save the value in the _TEB->TlsSlots array.
|
||||
// Effectively implements TlsSetValue().
|
||||
MRC 15, 0, R0, C13, C0, 2
|
||||
ADD $0xe10, R0
|
||||
MOVW $runtime·tls_g(SB), R11
|
||||
MOVW (R11), R11
|
||||
MOVW g, R11<<2(R0)
|
||||
MOVW g, R0 // preserve R0 accross call to setg<>
|
||||
RET
|
||||
#else
|
||||
// If the host does not support MRC the linker will replace it with
|
||||
// a call to runtime.read_tls_fallback which jumps to __kuser_get_tls.
|
||||
|
|
@ -47,6 +61,7 @@ TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0
|
|||
MOVW g, R0 // preserve R0 across call to setg<>
|
||||
RET
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// load_g loads the g register from pthread-provided
|
||||
// thread-local memory, for use after calling externally compiled
|
||||
|
|
@ -55,6 +70,16 @@ TEXT runtime·load_g(SB),NOSPLIT,$0
|
|||
#ifdef GOOS_nacl
|
||||
// nothing to do as nacl/arm does not use TLS at all.
|
||||
RET
|
||||
#else
|
||||
#ifdef GOOS_windows
|
||||
// Get the value from the _TEB->TlsSlots array.
|
||||
// Effectively implements TlsGetValue().
|
||||
MRC 15, 0, R0, C13, C0, 2
|
||||
ADD $0xe10, R0
|
||||
MOVW $runtime·tls_g(SB), g
|
||||
MOVW (g), g
|
||||
MOVW g<<2(R0), g
|
||||
RET
|
||||
#else
|
||||
// See save_g
|
||||
MRC 15, 0, R0, C13, C0, 3 // fetch TLS base pointer
|
||||
|
|
@ -64,6 +89,7 @@ TEXT runtime·load_g(SB),NOSPLIT,$0
|
|||
MOVW 0(R0), g
|
||||
RET
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// This is called from rt0_go, which runs on the system stack
|
||||
// using the initial stack allocated by the OS.
|
||||
|
|
@ -76,6 +102,20 @@ TEXT runtime·load_g(SB),NOSPLIT,$0
|
|||
// Declare a dummy word ($4, not $0) to make sure the
|
||||
// frame is 8 bytes and stays 8-byte-aligned.
|
||||
TEXT runtime·_initcgo(SB),NOSPLIT,$4
|
||||
#ifdef GOOS_windows
|
||||
MOVW R13, R4
|
||||
BIC $0x7, R13
|
||||
MOVW $runtime·_TlsAlloc(SB), R0
|
||||
MOVW (R0), R0
|
||||
BL (R0)
|
||||
// Assert that slot is less than 64 so we can use _TEB->TlsSlots
|
||||
CMP $64, R0
|
||||
MOVW $runtime·abort(SB), R1
|
||||
BL.GE (R1)
|
||||
MOVW $runtime·tls_g(SB), R1
|
||||
MOVW R0, (R1)
|
||||
MOVW R4, R13
|
||||
#else
|
||||
#ifndef GOOS_nacl
|
||||
// if there is an _cgo_init, call it.
|
||||
MOVW _cgo_init(SB), R4
|
||||
|
|
@ -91,7 +131,8 @@ TEXT runtime·_initcgo(SB),NOSPLIT,$4
|
|||
MOVW $setg_gcc<>(SB), R1 // arg 1: setg
|
||||
MOVW g, R0 // arg 0: G
|
||||
BL (R4) // will clobber R0-R3
|
||||
#endif
|
||||
#endif // GOOS_nacl
|
||||
#endif // GOOS_windows
|
||||
nocgo:
|
||||
RET
|
||||
|
||||
|
|
|
|||
|
|
@ -17,11 +17,12 @@ import (
|
|||
|
||||
const maxCallback = 2000
|
||||
|
||||
func genasm() {
|
||||
func genasm386Amd64() {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString(`// generated by wincallback.go; run go generate
|
||||
buf.WriteString(`// Code generated by wincallback.go using 'go generate'. DO NOT EDIT.
|
||||
|
||||
// +build 386 amd64
|
||||
// runtime·callbackasm is called by external code to
|
||||
// execute Go implemented callback function. It is not
|
||||
// called from the start, instead runtime·compilecallback
|
||||
|
|
@ -29,13 +30,43 @@ func genasm() {
|
|||
// appropriately so different callbacks start with different
|
||||
// CALL instruction in runtime·callbackasm. This determines
|
||||
// which Go callback function is executed later on.
|
||||
|
||||
TEXT runtime·callbackasm(SB),7,$0
|
||||
`)
|
||||
for i := 0; i < maxCallback; i++ {
|
||||
buf.WriteString("\tCALL\truntime·callbackasm1(SB)\n")
|
||||
}
|
||||
|
||||
err := ioutil.WriteFile("zcallback_windows.s", buf.Bytes(), 0666)
|
||||
filename := fmt.Sprintf("zcallback_windows.s")
|
||||
err := ioutil.WriteFile(filename, buf.Bytes(), 0666)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "wincallback: %s\n", err)
|
||||
os.Exit(2)
|
||||
}
|
||||
}
|
||||
|
||||
func genasmArm() {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString(`// Code generated by wincallback.go using 'go generate'. DO NOT EDIT.
|
||||
|
||||
// External code calls into callbackasm at an offset corresponding
|
||||
// to the callback index. Callbackasm is a table of MOV and B instructions.
|
||||
// The MOV instruction loads R12 with the callback index, and the
|
||||
// B instruction branches to callbackasm1.
|
||||
// callbackasm1 takes the callback index from R12 and
|
||||
// indexes into an array that stores information about each callback.
|
||||
// It then calls the Go implementation for that callback.
|
||||
#include "textflag.h"
|
||||
|
||||
TEXT runtime·callbackasm(SB),NOSPLIT|NOFRAME,$0
|
||||
`)
|
||||
for i := 0; i < maxCallback; i++ {
|
||||
buf.WriteString(fmt.Sprintf("\tMOVW\t$%d, R12\n", i))
|
||||
buf.WriteString("\tB\truntime·callbackasm1(SB)\n")
|
||||
}
|
||||
|
||||
err := ioutil.WriteFile("zcallback_windows_arm.s", buf.Bytes(), 0666)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "wincallback: %s\n", err)
|
||||
os.Exit(2)
|
||||
|
|
@ -45,7 +76,7 @@ TEXT runtime·callbackasm(SB),7,$0
|
|||
func gengo() {
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.WriteString(fmt.Sprintf(`// generated by wincallback.go; run go generate
|
||||
buf.WriteString(fmt.Sprintf(`// Code generated by wincallback.go using 'go generate'. DO NOT EDIT.
|
||||
|
||||
package runtime
|
||||
|
||||
|
|
@ -59,6 +90,7 @@ const cb_max = %d // maximum number of windows callbacks allowed
|
|||
}
|
||||
|
||||
func main() {
|
||||
genasm()
|
||||
genasm386Amd64()
|
||||
genasmArm()
|
||||
gengo()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
// generated by wincallback.go; run go generate
|
||||
// Code generated by wincallback.go using 'go generate'. DO NOT EDIT.
|
||||
|
||||
package runtime
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// generated by wincallback.go; run go generate
|
||||
// Code generated by wincallback.go using 'go generate'. DO NOT EDIT.
|
||||
|
||||
// +build 386 amd64
|
||||
// runtime·callbackasm is called by external code to
|
||||
// execute Go implemented callback function. It is not
|
||||
// called from the start, instead runtime·compilecallback
|
||||
|
|
@ -7,6 +8,7 @@
|
|||
// appropriately so different callbacks start with different
|
||||
// CALL instruction in runtime·callbackasm. This determines
|
||||
// which Go callback function is executed later on.
|
||||
|
||||
TEXT runtime·callbackasm(SB),7,$0
|
||||
CALL runtime·callbackasm1(SB)
|
||||
CALL runtime·callbackasm1(SB)
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue