Commit 05a5de30 authored by Alex Brainman's avatar Alex Brainman

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
parent ecdbcaf4
...@@ -94,6 +94,7 @@ void mkenam(char*, char*); ...@@ -94,6 +94,7 @@ void mkenam(char*, char*);
// buildruntime.c // buildruntime.c
void mkzasm(char*, char*); void mkzasm(char*, char*);
void mkzsys(char*, char*);
void mkzgoarch(char*, char*); void mkzgoarch(char*, char*);
void mkzgoos(char*, char*); void mkzgoos(char*, char*);
void mkzruntimedefs(char*, char*); void mkzruntimedefs(char*, char*);
......
...@@ -528,6 +528,7 @@ static struct { ...@@ -528,6 +528,7 @@ static struct {
}}, }},
{"pkg/runtime", { {"pkg/runtime", {
"zasm_$GOOS_$GOARCH.h", "zasm_$GOOS_$GOARCH.h",
"zsys_$GOOS_$GOARCH.s",
"zgoarch_$GOARCH.go", "zgoarch_$GOARCH.go",
"zgoos_$GOOS.go", "zgoos_$GOOS.go",
"zruntime_defs_$GOOS_$GOARCH.go", "zruntime_defs_$GOOS_$GOARCH.go",
...@@ -552,6 +553,7 @@ static struct { ...@@ -552,6 +553,7 @@ static struct {
{"opnames.h", gcopnames}, {"opnames.h", gcopnames},
{"enam.c", mkenam}, {"enam.c", mkenam},
{"zasm_", mkzasm}, {"zasm_", mkzasm},
{"zsys_", mkzsys},
{"zgoarch_", mkzgoarch}, {"zgoarch_", mkzgoarch},
{"zgoos_", mkzgoos}, {"zgoos_", mkzgoos},
{"zruntime_defs_", mkzruntimedefs}, {"zruntime_defs_", mkzruntimedefs},
......
...@@ -178,6 +178,8 @@ static struct { ...@@ -178,6 +178,8 @@ static struct {
}, },
}; };
#define MAXWINCB 2000 /* maximum number of windows callbacks allowed */
// mkzasm writes zasm_$GOOS_$GOARCH.h, // mkzasm writes zasm_$GOOS_$GOARCH.h,
// which contains struct offsets for use by // which contains struct offsets for use by
// assembly files. It also writes a copy to the work space // assembly files. It also writes a copy to the work space
...@@ -249,6 +251,8 @@ ok: ...@@ -249,6 +251,8 @@ ok:
aggr = "gobuf"; aggr = "gobuf";
else if(streq(fields.p[1], "WinCall")) else if(streq(fields.p[1], "WinCall"))
aggr = "wincall"; aggr = "wincall";
else if(streq(fields.p[1], "WinCallbackContext"))
aggr = "cbctxt";
else if(streq(fields.p[1], "SEH")) else if(streq(fields.p[1], "SEH"))
aggr = "seh"; aggr = "seh";
} }
...@@ -262,6 +266,11 @@ ok: ...@@ -262,6 +266,11 @@ ok:
bwritestr(&out, bprintf(&b, "#define %s_%s %s\n", aggr, fields.p[n-1], fields.p[n-2])); 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. // Write both to file and to workdir/zasm_GOOS_GOARCH.h.
writefile(&out, file, 0); writefile(&out, file, 0);
...@@ -275,6 +284,41 @@ ok: ...@@ -275,6 +284,41 @@ ok:
vfree(&fields); 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[] = { static char *runtimedefs[] = {
"proc.c", "proc.c",
"iface.c", "iface.c",
......
...@@ -7,24 +7,19 @@ ...@@ -7,24 +7,19 @@
#include "typekind.h" #include "typekind.h"
#include "defs_GOOS_GOARCH.h" #include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h" #include "os_GOOS.h"
#include "zasm_GOOS_GOARCH.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; typedef struct Callbacks Callbacks;
struct Callbacks { struct Callbacks {
Lock; Lock;
Callback* link; WinCallbackContext* ctxt[cb_max];
int32 n; int32 n;
}; };
static Callbacks cbs; static Callbacks cbs;
WinCallbackContext** runtime·cbctxts; // to simplify access to cbs.ctxt in sys_windows_*.s
// Call back from windows dll into go. // Call back from windows dll into go.
byte * byte *
runtime·compilecallback(Eface fn, bool cleanstack) runtime·compilecallback(Eface fn, bool cleanstack)
...@@ -32,8 +27,7 @@ runtime·compilecallback(Eface fn, bool cleanstack) ...@@ -32,8 +27,7 @@ runtime·compilecallback(Eface fn, bool cleanstack)
FuncType *ft; FuncType *ft;
Type *t; Type *t;
int32 argsize, i, n; int32 argsize, i, n;
byte *p; WinCallbackContext *c;
Callback *c;
if(fn.type == nil || fn.type->kind != KindFunc) if(fn.type == nil || fn.type->kind != KindFunc)
runtime·panicstring("compilecallback: not a function"); runtime·panicstring("compilecallback: not a function");
...@@ -50,59 +44,33 @@ runtime·compilecallback(Eface fn, bool cleanstack) ...@@ -50,59 +44,33 @@ runtime·compilecallback(Eface fn, bool cleanstack)
argsize += sizeof(uintptr); 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); runtime·lock(&cbs);
for(c = cbs.link; c != nil; c = c->link) { if(runtime·cbctxts == nil)
if(c->gobody == fn.data) { runtime·cbctxts = &(cbs.ctxt[0]);
n = cbs.n;
for(i=0; i<n; i++) {
if(cbs.ctxt[i]->gobody == fn.data) {
runtime·unlock(&cbs); 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"); runtime·throw("too many callback functions");
c = runtime·mal(sizeof *c + n); c = runtime·mal(sizeof *c);
c->gobody = fn.data; c->gobody = fn.data;
c->link = cbs.link; c->argsize = argsize;
cbs.link = c; if(cleanstack && argsize!=0)
c->restorestack = argsize;
else
c->restorestack = 0;
cbs.ctxt[n] = c;
cbs.n++; cbs.n++;
runtime·unlock(&cbs); runtime·unlock(&cbs);
p = &c->asmbody; // as before
return (byte*)runtime·callbackasm + n * 5;
// 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;
} }
// 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; ...@@ -78,6 +78,7 @@ typedef struct Complex64 Complex64;
typedef struct Complex128 Complex128; typedef struct Complex128 Complex128;
typedef struct WinCall WinCall; typedef struct WinCall WinCall;
typedef struct SEH SEH; typedef struct SEH SEH;
typedef struct WinCallbackContext WinCallbackContext;
typedef struct Timers Timers; typedef struct Timers Timers;
typedef struct Timer Timer; typedef struct Timer Timer;
typedef struct GCStats GCStats; typedef struct GCStats GCStats;
...@@ -444,6 +445,13 @@ struct SEH ...@@ -444,6 +445,13 @@ struct SEH
void* prev; void* prev;
void* handler; 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 #ifdef GOOS_windows
enum { enum {
......
...@@ -164,19 +164,16 @@ TEXT runtime·externalthreadhandler(SB),7,$0 ...@@ -164,19 +164,16 @@ TEXT runtime·externalthreadhandler(SB),7,$0
POPL BP POPL BP
RET RET
// Called from dynamic function created by ../thread.c compilecallback, GLOBL runtime·cbctxts(SB), $4
// running on Windows stack (not Go stack).
// BX, BP, SI, DI registers and DF flag are preserved TEXT runtime·callbackasm1+0(SB),7,$0
// as required by windows callback convention. MOVL 0(SP), AX // will use to find our callback context
// AX = address of go func we need to call
// DX = total size of arguments // remove return address from stack, we are not returning there
// ADDL $4, SP
TEXT runtime·callbackasm+0(SB),7,$0
// preserve whatever's at the memory location that // address to callback parameters into CX
// the callback will use to store the return value LEAL 4(SP), CX
LEAL 8(SP), CX
PUSHL 0(CX)(DX*1)
ADDL $4, DX // extend argsize by size of return value
// save registers as required for windows callback // save registers as required for windows callback
PUSHL DI PUSHL DI
...@@ -189,19 +186,51 @@ TEXT runtime·callbackasm+0(SB),7,$0 ...@@ -189,19 +186,51 @@ TEXT runtime·callbackasm+0(SB),7,$0
PUSHL 0(FS) PUSHL 0(FS)
MOVL SP, 0(FS) MOVL SP, 0(FS)
// callback parameters // determine index into runtime·cbctxts table
PUSHL DX SUBL $runtime·callbackasm(SB), AX
PUSHL CX MOVL $0, DX
PUSHL AX MOVL $5, BX // divide by 5 because each call instruction in runtime·callbacks is 5 bytes long
DIVL BX,
CLD // find correspondent runtime·cbctxts table entry
MOVL runtime·cbctxts(SB), BX
MOVL -4(BX)(AX*4), BX
CALL runtime·cgocallback_gofunc(SB) // 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 AX
POPL CX POPL CX
POPL DX 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 // pop SEH frame
POPL 0(FS) POPL 0(FS)
POPL BX POPL BX
...@@ -212,10 +241,13 @@ TEXT runtime·callbackasm+0(SB),7,$0 ...@@ -212,10 +241,13 @@ TEXT runtime·callbackasm+0(SB),7,$0
POPL SI POPL SI
POPL DI POPL DI
// remove callback parameters before return (as per Windows spec)
POPL DX
ADDL CX, SP
PUSHL DX
CLD CLD
MOVL -4(CX)(DX*1), AX
POPL -4(CX)(DX*1)
RET RET
// void tstart(M *newm); // void tstart(M *newm);
......
...@@ -196,32 +196,37 @@ TEXT runtime·externalthreadhandler(SB),7,$0 ...@@ -196,32 +196,37 @@ TEXT runtime·externalthreadhandler(SB),7,$0
POPQ BP POPQ BP
RET RET
// Continuation of thunk function created for each callback by ../thread.c compilecallback, GLOBL runtime·cbctxts(SB), $8
// runs on Windows stack (not Go stack).
// Thunk code designed to have minimal size for it is copied many (up to thousands) times. TEXT runtime·callbackasm1(SB),7,$0
//
// thunk:
// MOVQ $fn, AX
// PUSHQ AX
// MOVQ $argsize, AX
// PUSHQ AX
// MOVQ $runtime·callbackasm, AX
// JMP AX
TEXT runtime·callbackasm(SB),7,$0
// Construct args vector for cgocallback(). // Construct args vector for cgocallback().
// By windows/amd64 calling convention first 4 args are in CX, DX, R8, R9 // By windows/amd64 calling convention first 4 args are in CX, DX, R8, R9
// args from the 5th on are on the stack. // 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 // 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. // but uninitialized "shadow space" for the first 4 args.
// The values are in registers. // The values are in registers.
MOVQ CX, (24+0)(SP) MOVQ CX, (16+0)(SP)
MOVQ DX, (24+8)(SP) MOVQ DX, (16+8)(SP)
MOVQ R8, (24+16)(SP) MOVQ R8, (16+16)(SP)
MOVQ R9, (24+24)(SP) MOVQ R9, (16+24)(SP)
// 6l does not accept writing POPQs here issuing a warning "unbalanced PUSH/POP"
MOVQ 0(SP), DX // POPQ DX // remove return address from stack, we are not returning there
MOVQ 8(SP), AX // POPQ AX MOVQ 0(SP), AX
ADDQ $16, SP 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 // preserve whatever's at the memory location that
// the callback will use to store the return value // the callback will use to store the return value
...@@ -231,8 +236,6 @@ TEXT runtime·callbackasm(SB),7,$0 ...@@ -231,8 +236,6 @@ TEXT runtime·callbackasm(SB),7,$0
// DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved // DI SI BP BX R12 R13 R14 R15 registers and DF flag are preserved
// as required by windows callback convention. // 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 PUSHFQ
SUBQ $64, SP SUBQ $64, SP
MOVQ DI, 56(SP) MOVQ DI, 56(SP)
...@@ -247,18 +250,17 @@ TEXT runtime·callbackasm(SB),7,$0 ...@@ -247,18 +250,17 @@ TEXT runtime·callbackasm(SB),7,$0
// prepare call stack. use SUBQ to hide from stack frame checks // prepare call stack. use SUBQ to hide from stack frame checks
// cgocallback(Go func, void *frame, uintptr framesize) // cgocallback(Go func, void *frame, uintptr framesize)
SUBQ $24, SP SUBQ $24, SP
MOVQ DX, 16(SP) // uintptr framesize MOVQ DX, 16(SP) // argsize (including return value)
MOVQ CX, 8(SP) // void *frame MOVQ CX, 8(SP) // callback parameters
MOVQ AX, 0(SP) // Go func MOVQ AX, 0(SP) // address of target Go function
CLD CLD
CALL runtime·cgocallback_gofunc(SB) CALL runtime·cgocallback_gofunc(SB)
MOVQ 0(SP), AX MOVQ 0(SP), AX
MOVQ 8(SP), CX MOVQ 8(SP), CX
MOVQ 16(SP), DX MOVQ 16(SP), DX
ADDQ $24, SP ADDQ $24, SP
// restore registers as required for windows callback // 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 0(SP), R15
MOVQ 8(SP), R14 MOVQ 8(SP), R14
MOVQ 16(SP), R13 MOVQ 16(SP), R13
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment