mirror of
https://github.com/golang/go
synced 2024-11-22 22:10:03 -07:00
runtime: do not generate code during runtime in windows NewCallback
Update #5494 R=golang-dev, minux.ma, rsc, iant CC=golang-dev https://golang.org/cl/10368043
This commit is contained in:
parent
ecdbcaf449
commit
05a5de30f0
1
src/cmd/dist/a.h
vendored
1
src/cmd/dist/a.h
vendored
@ -94,6 +94,7 @@ void mkenam(char*, char*);
|
||||
|
||||
// buildruntime.c
|
||||
void mkzasm(char*, char*);
|
||||
void mkzsys(char*, char*);
|
||||
void mkzgoarch(char*, char*);
|
||||
void mkzgoos(char*, char*);
|
||||
void mkzruntimedefs(char*, char*);
|
||||
|
2
src/cmd/dist/build.c
vendored
2
src/cmd/dist/build.c
vendored
@ -528,6 +528,7 @@ static struct {
|
||||
}},
|
||||
{"pkg/runtime", {
|
||||
"zasm_$GOOS_$GOARCH.h",
|
||||
"zsys_$GOOS_$GOARCH.s",
|
||||
"zgoarch_$GOARCH.go",
|
||||
"zgoos_$GOOS.go",
|
||||
"zruntime_defs_$GOOS_$GOARCH.go",
|
||||
@ -552,6 +553,7 @@ static struct {
|
||||
{"opnames.h", gcopnames},
|
||||
{"enam.c", mkenam},
|
||||
{"zasm_", mkzasm},
|
||||
{"zsys_", mkzsys},
|
||||
{"zgoarch_", mkzgoarch},
|
||||
{"zgoos_", mkzgoos},
|
||||
{"zruntime_defs_", mkzruntimedefs},
|
||||
|
44
src/cmd/dist/buildruntime.c
vendored
44
src/cmd/dist/buildruntime.c
vendored
@ -178,6 +178,8 @@ static struct {
|
||||
},
|
||||
};
|
||||
|
||||
#define MAXWINCB 2000 /* maximum number of windows callbacks allowed */
|
||||
|
||||
// mkzasm writes zasm_$GOOS_$GOARCH.h,
|
||||
// which contains struct offsets for use by
|
||||
// assembly files. It also writes a copy to the work space
|
||||
@ -249,6 +251,8 @@ ok:
|
||||
aggr = "gobuf";
|
||||
else if(streq(fields.p[1], "WinCall"))
|
||||
aggr = "wincall";
|
||||
else if(streq(fields.p[1], "WinCallbackContext"))
|
||||
aggr = "cbctxt";
|
||||
else if(streq(fields.p[1], "SEH"))
|
||||
aggr = "seh";
|
||||
}
|
||||
@ -262,6 +266,11 @@ ok:
|
||||
bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2]));
|
||||
}
|
||||
}
|
||||
|
||||
// Some #defines that are used for .c files.
|
||||
if(streq(goos, "windows")) {
|
||||
bwritestr(&out, bprintf(&b, "#define cb_max %d\n", MAXWINCB));
|
||||
}
|
||||
|
||||
// Write both to file and to workdir/zasm_GOOS_GOARCH.h.
|
||||
writefile(&out, file, 0);
|
||||
@ -275,6 +284,41 @@ ok:
|
||||
vfree(&fields);
|
||||
}
|
||||
|
||||
// mkzsys writes zsys_$GOOS_$GOARCH.h,
|
||||
// which contains arch or os specific asm code.
|
||||
//
|
||||
void
|
||||
mkzsys(char *dir, char *file)
|
||||
{
|
||||
int i;
|
||||
Buf out;
|
||||
|
||||
USED(dir);
|
||||
|
||||
binit(&out);
|
||||
|
||||
bwritestr(&out, "// auto generated by go tool dist\n\n");
|
||||
if(streq(goos, "windows")) {
|
||||
bwritef(&out,
|
||||
"// runtime·callbackasm is called by external code to\n"
|
||||
"// execute Go implemented callback function. It is not\n"
|
||||
"// called from the start, instead runtime·compilecallback\n"
|
||||
"// always returns address into runtime·callbackasm offset\n"
|
||||
"// appropriately so different callbacks start with different\n"
|
||||
"// CALL instruction in runtime·callbackasm. This determines\n"
|
||||
"// which Go callback function is executed later on.\n"
|
||||
"TEXT runtime·callbackasm(SB),7,$0\n");
|
||||
for(i=0; i<MAXWINCB; i++) {
|
||||
bwritef(&out, "\tCALL\truntime·callbackasm1(SB)\n");
|
||||
}
|
||||
bwritef(&out, "\tRET\n");
|
||||
}
|
||||
|
||||
writefile(&out, file, 0);
|
||||
|
||||
bfree(&out);
|
||||
}
|
||||
|
||||
static char *runtimedefs[] = {
|
||||
"proc.c",
|
||||
"iface.c",
|
||||
|
@ -7,24 +7,19 @@
|
||||
#include "typekind.h"
|
||||
#include "defs_GOOS_GOARCH.h"
|
||||
#include "os_GOOS.h"
|
||||
|
||||
// Will keep all callbacks in a linked list, so they don't get garbage collected.
|
||||
typedef struct Callback Callback;
|
||||
struct Callback {
|
||||
Callback* link;
|
||||
void* gobody;
|
||||
byte asmbody;
|
||||
};
|
||||
#include "zasm_GOOS_GOARCH.h"
|
||||
|
||||
typedef struct Callbacks Callbacks;
|
||||
struct Callbacks {
|
||||
Lock;
|
||||
Callback* link;
|
||||
int32 n;
|
||||
WinCallbackContext* ctxt[cb_max];
|
||||
int32 n;
|
||||
};
|
||||
|
||||
static Callbacks cbs;
|
||||
|
||||
WinCallbackContext** runtime·cbctxts; // to simplify access to cbs.ctxt in sys_windows_*.s
|
||||
|
||||
// Call back from windows dll into go.
|
||||
byte *
|
||||
runtime·compilecallback(Eface fn, bool cleanstack)
|
||||
@ -32,8 +27,7 @@ runtime·compilecallback(Eface fn, bool cleanstack)
|
||||
FuncType *ft;
|
||||
Type *t;
|
||||
int32 argsize, i, n;
|
||||
byte *p;
|
||||
Callback *c;
|
||||
WinCallbackContext *c;
|
||||
|
||||
if(fn.type == nil || fn.type->kind != KindFunc)
|
||||
runtime·panicstring("compilecallback: not a function");
|
||||
@ -50,59 +44,33 @@ runtime·compilecallback(Eface fn, bool cleanstack)
|
||||
argsize += sizeof(uintptr);
|
||||
}
|
||||
|
||||
// compute size of new fn.
|
||||
// must match code laid out below.
|
||||
n = 1+4; // MOVL fn, AX
|
||||
n += 1+4; // MOVL argsize, DX
|
||||
n += 1+4; // MOVL callbackasm, CX
|
||||
n += 2; // CALL CX
|
||||
n += 1; // RET
|
||||
if(cleanstack && argsize!=0)
|
||||
n += 2; // ... argsize
|
||||
|
||||
runtime·lock(&cbs);
|
||||
for(c = cbs.link; c != nil; c = c->link) {
|
||||
if(c->gobody == fn.data) {
|
||||
if(runtime·cbctxts == nil)
|
||||
runtime·cbctxts = &(cbs.ctxt[0]);
|
||||
n = cbs.n;
|
||||
for(i=0; i<n; i++) {
|
||||
if(cbs.ctxt[i]->gobody == fn.data) {
|
||||
runtime·unlock(&cbs);
|
||||
return &c->asmbody;
|
||||
// runtime·callbackasm is just a series of CALL instructions
|
||||
// (each is 5 bytes long), and we want callback to arrive at
|
||||
// correspondent call instruction instead of start of
|
||||
// runtime·callbackasm.
|
||||
return (byte*)runtime·callbackasm + i * 5;
|
||||
}
|
||||
}
|
||||
if(cbs.n >= 2000)
|
||||
if(n >= cb_max)
|
||||
runtime·throw("too many callback functions");
|
||||
c = runtime·mal(sizeof *c + n);
|
||||
c = runtime·mal(sizeof *c);
|
||||
c->gobody = fn.data;
|
||||
c->link = cbs.link;
|
||||
cbs.link = c;
|
||||
c->argsize = argsize;
|
||||
if(cleanstack && argsize!=0)
|
||||
c->restorestack = argsize;
|
||||
else
|
||||
c->restorestack = 0;
|
||||
cbs.ctxt[n] = c;
|
||||
cbs.n++;
|
||||
runtime·unlock(&cbs);
|
||||
|
||||
p = &c->asmbody;
|
||||
|
||||
// MOVL fn, AX
|
||||
*p++ = 0xb8;
|
||||
*(uint32*)p = (uint32)(fn.data);
|
||||
p += 4;
|
||||
|
||||
// MOVL argsize, DX
|
||||
*p++ = 0xba;
|
||||
*(uint32*)p = argsize;
|
||||
p += 4;
|
||||
|
||||
// MOVL callbackasm, CX
|
||||
*p++ = 0xb9;
|
||||
*(uint32*)p = (uint32)runtime·callbackasm;
|
||||
p += 4;
|
||||
|
||||
// CALL CX
|
||||
*p++ = 0xff;
|
||||
*p++ = 0xd1;
|
||||
|
||||
// RET argsize?
|
||||
if(cleanstack && argsize!=0) {
|
||||
*p++ = 0xc2;
|
||||
*(uint16*)p = argsize;
|
||||
} else
|
||||
*p = 0xc3;
|
||||
|
||||
return &c->asmbody;
|
||||
// as before
|
||||
return (byte*)runtime·callbackasm + n * 5;
|
||||
}
|
@ -1,105 +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.
|
||||
|
||||
#include "runtime.h"
|
||||
#include "type.h"
|
||||
#include "typekind.h"
|
||||
#include "defs_GOOS_GOARCH.h"
|
||||
#include "os_GOOS.h"
|
||||
|
||||
// Will keep all callbacks in a linked list, so they don't get garbage collected.
|
||||
typedef struct Callback Callback;
|
||||
struct Callback {
|
||||
Callback* link;
|
||||
void* gobody;
|
||||
byte asmbody;
|
||||
};
|
||||
|
||||
typedef struct Callbacks Callbacks;
|
||||
struct Callbacks {
|
||||
Lock;
|
||||
Callback* link;
|
||||
int32 n;
|
||||
};
|
||||
|
||||
static Callbacks cbs;
|
||||
|
||||
// Call back from windows dll into go.
|
||||
byte *
|
||||
runtime·compilecallback(Eface fn, bool /*cleanstack*/)
|
||||
{
|
||||
FuncType *ft;
|
||||
Type *t;
|
||||
int32 argsize, i, n;
|
||||
byte *p;
|
||||
Callback *c;
|
||||
|
||||
if(fn.type == nil || fn.type->kind != KindFunc)
|
||||
runtime·panicstring("compilecallback: not a function");
|
||||
ft = (FuncType*)fn.type;
|
||||
if(ft->out.len != 1)
|
||||
runtime·panicstring("compilecallback: function must have one output parameter");
|
||||
if(((Type**)ft->out.array)[0]->size != sizeof(uintptr))
|
||||
runtime·panicstring("compilecallback: output parameter size is wrong");
|
||||
argsize = 0;
|
||||
for(i=0; i<ft->in.len; i++) {
|
||||
t = ((Type**)ft->in.array)[i];
|
||||
if(t->size > sizeof(uintptr))
|
||||
runtime·panicstring("compilecallback: input parameter size is wrong");
|
||||
argsize += sizeof(uintptr);
|
||||
}
|
||||
|
||||
// compute size of new fn.
|
||||
// must match code laid out below.
|
||||
n = 2+8+1; // MOVQ fn, AX / PUSHQ AX
|
||||
n += 2+8+1; // MOVQ argsize, AX / PUSHQ AX
|
||||
n += 2+8; // MOVQ callbackasm, AX
|
||||
n += 2; // JMP AX
|
||||
|
||||
runtime·lock(&cbs);
|
||||
for(c = cbs.link; c != nil; c = c->link) {
|
||||
if(c->gobody == fn.data) {
|
||||
runtime·unlock(&cbs);
|
||||
return &c->asmbody;
|
||||
}
|
||||
}
|
||||
if(cbs.n >= 2000)
|
||||
runtime·throw("too many callback functions");
|
||||
c = runtime·mal(sizeof *c + n);
|
||||
c->gobody = fn.data;
|
||||
c->link = cbs.link;
|
||||
cbs.link = c;
|
||||
cbs.n++;
|
||||
runtime·unlock(&cbs);
|
||||
|
||||
p = &c->asmbody;
|
||||
|
||||
// MOVQ fn, AX
|
||||
*p++ = 0x48;
|
||||
*p++ = 0xb8;
|
||||
*(uint64*)p = (uint64)(fn.data);
|
||||
p += 8;
|
||||
// PUSH AX
|
||||
*p++ = 0x50;
|
||||
|
||||
// MOVQ argsize, AX
|
||||
*p++ = 0x48;
|
||||
*p++ = 0xb8;
|
||||
*(uint64*)p = argsize;
|
||||
p += 8;
|
||||
// PUSH AX
|
||||
*p++ = 0x50;
|
||||
|
||||
// MOVQ callbackasm, AX
|
||||
*p++ = 0x48;
|
||||
*p++ = 0xb8;
|
||||
*(uint64*)p = (uint64)runtime·callbackasm;
|
||||
p += 8;
|
||||
|
||||
// JMP AX
|
||||
*p++ = 0xFF;
|
||||
*p = 0xE0;
|
||||
|
||||
return &c->asmbody;
|
||||
}
|
@ -78,6 +78,7 @@ typedef struct Complex64 Complex64;
|
||||
typedef struct Complex128 Complex128;
|
||||
typedef struct WinCall WinCall;
|
||||
typedef struct SEH SEH;
|
||||
typedef struct WinCallbackContext WinCallbackContext;
|
||||
typedef struct Timers Timers;
|
||||
typedef struct Timer Timer;
|
||||
typedef struct GCStats GCStats;
|
||||
@ -444,6 +445,13 @@ struct SEH
|
||||
void* prev;
|
||||
void* handler;
|
||||
};
|
||||
// describes how to handle callback
|
||||
struct WinCallbackContext
|
||||
{
|
||||
void* gobody; // Go function to call
|
||||
uintptr argsize; // callback arguments size (in bytes)
|
||||
uintptr restorestack; // adjust stack on return by (in bytes) (386 only)
|
||||
};
|
||||
|
||||
#ifdef GOOS_windows
|
||||
enum {
|
||||
|
@ -164,19 +164,16 @@ TEXT runtime·externalthreadhandler(SB),7,$0
|
||||
POPL BP
|
||||
RET
|
||||
|
||||
// Called from dynamic function created by ../thread.c compilecallback,
|
||||
// running on Windows stack (not Go stack).
|
||||
// BX, BP, SI, DI registers and DF flag are preserved
|
||||
// as required by windows callback convention.
|
||||
// AX = address of go func we need to call
|
||||
// DX = total size of arguments
|
||||
//
|
||||
TEXT runtime·callbackasm+0(SB),7,$0
|
||||
// preserve whatever's at the memory location that
|
||||
// the callback will use to store the return value
|
||||
LEAL 8(SP), CX
|
||||
PUSHL 0(CX)(DX*1)
|
||||
ADDL $4, DX // extend argsize by size of return value
|
||||
GLOBL runtime·cbctxts(SB), $4
|
||||
|
||||
TEXT runtime·callbackasm1+0(SB),7,$0
|
||||
MOVL 0(SP), AX // will use to find our callback context
|
||||
|
||||
// remove return address from stack, we are not returning there
|
||||
ADDL $4, SP
|
||||
|
||||
// address to callback parameters into CX
|
||||
LEAL 4(SP), CX
|
||||
|
||||
// save registers as required for windows callback
|
||||
PUSHL DI
|
||||
@ -189,19 +186,51 @@ TEXT runtime·callbackasm+0(SB),7,$0
|
||||
PUSHL 0(FS)
|
||||
MOVL SP, 0(FS)
|
||||
|
||||
// callback parameters
|
||||
PUSHL DX
|
||||
PUSHL CX
|
||||
PUSHL AX
|
||||
// determine index into runtime·cbctxts table
|
||||
SUBL $runtime·callbackasm(SB), AX
|
||||
MOVL $0, DX
|
||||
MOVL $5, BX // divide by 5 because each call instruction in runtime·callbacks is 5 bytes long
|
||||
DIVL BX,
|
||||
|
||||
// find correspondent runtime·cbctxts table entry
|
||||
MOVL runtime·cbctxts(SB), BX
|
||||
MOVL -4(BX)(AX*4), BX
|
||||
|
||||
// extract callback context
|
||||
MOVL cbctxt_gobody(BX), AX
|
||||
MOVL cbctxt_argsize(BX), DX
|
||||
|
||||
// preserve whatever's at the memory location that
|
||||
// the callback will use to store the return value
|
||||
PUSHL 0(CX)(DX*1)
|
||||
|
||||
// extend argsize by size of return value
|
||||
ADDL $4, DX
|
||||
|
||||
// remember how to restore stack on return
|
||||
MOVL cbctxt_restorestack(BX), BX
|
||||
PUSHL BX
|
||||
|
||||
// call target Go function
|
||||
PUSHL DX // argsize (including return value)
|
||||
PUSHL CX // callback parameters
|
||||
PUSHL AX // address of target Go function
|
||||
CLD
|
||||
|
||||
CALL runtime·cgocallback_gofunc(SB)
|
||||
|
||||
POPL AX
|
||||
POPL CX
|
||||
POPL DX
|
||||
|
||||
// how to restore stack on return
|
||||
POPL BX
|
||||
|
||||
// return value into AX (as per Windows spec)
|
||||
// and restore previously preserved value
|
||||
MOVL -4(CX)(DX*1), AX
|
||||
POPL -4(CX)(DX*1)
|
||||
|
||||
MOVL BX, CX // cannot use BX anymore
|
||||
|
||||
// pop SEH frame
|
||||
POPL 0(FS)
|
||||
POPL BX
|
||||
@ -212,10 +241,13 @@ TEXT runtime·callbackasm+0(SB),7,$0
|
||||
POPL SI
|
||||
POPL DI
|
||||
|
||||
// remove callback parameters before return (as per Windows spec)
|
||||
POPL DX
|
||||
ADDL CX, SP
|
||||
PUSHL DX
|
||||
|
||||
CLD
|
||||
|
||||
MOVL -4(CX)(DX*1), AX
|
||||
POPL -4(CX)(DX*1)
|
||||
RET
|
||||
|
||||
// void tstart(M *newm);
|
||||
|
@ -196,32 +196,37 @@ TEXT runtime·externalthreadhandler(SB),7,$0
|
||||
POPQ BP
|
||||
RET
|
||||
|
||||
// Continuation of thunk function created for each callback by ../thread.c compilecallback,
|
||||
// runs on Windows stack (not Go stack).
|
||||
// Thunk code designed to have minimal size for it is copied many (up to thousands) times.
|
||||
//
|
||||
// thunk:
|
||||
// MOVQ $fn, AX
|
||||
// PUSHQ AX
|
||||
// MOVQ $argsize, AX
|
||||
// PUSHQ AX
|
||||
// MOVQ $runtime·callbackasm, AX
|
||||
// JMP AX
|
||||
TEXT runtime·callbackasm(SB),7,$0
|
||||
GLOBL runtime·cbctxts(SB), $8
|
||||
|
||||
TEXT runtime·callbackasm1(SB),7,$0
|
||||
// Construct args vector for cgocallback().
|
||||
// By windows/amd64 calling convention first 4 args are in CX, DX, R8, R9
|
||||
// args from the 5th on are on the stack.
|
||||
// In any case, even if function has 0,1,2,3,4 args, there is reserved
|
||||
// but uninitialized "shadow space" for the first 4 args.
|
||||
// The values are in registers.
|
||||
MOVQ CX, (24+0)(SP)
|
||||
MOVQ DX, (24+8)(SP)
|
||||
MOVQ R8, (24+16)(SP)
|
||||
MOVQ R9, (24+24)(SP)
|
||||
// 6l does not accept writing POPQs here issuing a warning "unbalanced PUSH/POP"
|
||||
MOVQ 0(SP), DX // POPQ DX
|
||||
MOVQ 8(SP), AX // POPQ AX
|
||||
ADDQ $16, SP
|
||||
MOVQ CX, (16+0)(SP)
|
||||
MOVQ DX, (16+8)(SP)
|
||||
MOVQ R8, (16+16)(SP)
|
||||
MOVQ R9, (16+24)(SP)
|
||||
|
||||
// remove return address from stack, we are not returning there
|
||||
MOVQ 0(SP), AX
|
||||
ADDQ $8, SP
|
||||
|
||||
// determine index into runtime·cbctxts table
|
||||
SUBQ $runtime·callbackasm(SB), AX
|
||||
MOVQ $0, DX
|
||||
MOVQ $5, CX // divide by 5 because each call instruction in runtime·callbacks is 5 bytes long
|
||||
DIVL CX,
|
||||
|
||||
// find correspondent runtime·cbctxts table entry
|
||||
MOVQ runtime·cbctxts(SB), CX
|
||||
MOVQ -8(CX)(AX*8), AX
|
||||
|
||||
// extract callback context
|
||||
MOVQ cbctxt_argsize(AX), DX
|
||||
MOVQ cbctxt_gobody(AX), AX
|
||||
|
||||
// preserve whatever's at the memory location that
|
||||
// the callback will use to store the return value
|
||||
@ -231,8 +236,6 @@ TEXT runtime·callbackasm(SB),7,$0
|
||||
|
||||
// DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved
|
||||
// as required by windows callback convention.
|
||||
// 6l does not allow writing many PUSHQs here issuing a warning "nosplit stack overflow"
|
||||
// the warning has no sense as this code uses os thread stack
|
||||
PUSHFQ
|
||||
SUBQ $64, SP
|
||||
MOVQ DI, 56(SP)
|
||||
@ -247,18 +250,17 @@ TEXT runtime·callbackasm(SB),7,$0
|
||||
// prepare call stack. use SUBQ to hide from stack frame checks
|
||||
// cgocallback(Go func, void *frame, uintptr framesize)
|
||||
SUBQ $24, SP
|
||||
MOVQ DX, 16(SP) // uintptr framesize
|
||||
MOVQ CX, 8(SP) // void *frame
|
||||
MOVQ AX, 0(SP) // Go func
|
||||
MOVQ DX, 16(SP) // argsize (including return value)
|
||||
MOVQ CX, 8(SP) // callback parameters
|
||||
MOVQ AX, 0(SP) // address of target Go function
|
||||
CLD
|
||||
CALL runtime·cgocallback_gofunc(SB)
|
||||
CALL runtime·cgocallback_gofunc(SB)
|
||||
MOVQ 0(SP), AX
|
||||
MOVQ 8(SP), CX
|
||||
MOVQ 16(SP), DX
|
||||
ADDQ $24, SP
|
||||
|
||||
// restore registers as required for windows callback
|
||||
// 6l does not allow writing many POPs here issuing a warning "nosplit stack overflow"
|
||||
MOVQ 0(SP), R15
|
||||
MOVQ 8(SP), R14
|
||||
MOVQ 16(SP), R13
|
||||
|
Loading…
Reference in New Issue
Block a user