summaryrefslogtreecommitdiff
path: root/src/pkg
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg')
-rw-r--r--src/pkg/reflect/asm_386.s4
-rw-r--r--src/pkg/reflect/asm_amd64.s4
-rw-r--r--src/pkg/reflect/asm_arm.s4
-rw-r--r--src/pkg/reflect/value.go8
-rw-r--r--src/pkg/runtime/asm_386.s2
-rw-r--r--src/pkg/runtime/asm_amd64.s2
-rw-r--r--src/pkg/runtime/asm_arm.s2
-rw-r--r--src/pkg/runtime/panic.c78
-rw-r--r--src/pkg/runtime/proc.c1
-rw-r--r--src/pkg/runtime/runtime.h4
-rw-r--r--src/pkg/runtime/stack.c13
11 files changed, 51 insertions, 71 deletions
diff --git a/src/pkg/reflect/asm_386.s b/src/pkg/reflect/asm_386.s
index ef814966e..75413c752 100644
--- a/src/pkg/reflect/asm_386.s
+++ b/src/pkg/reflect/asm_386.s
@@ -8,7 +8,7 @@
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No argsize here, gc generates argsize info at call site.
-TEXT ·makeFuncStub(SB),NOSPLIT,$8
+TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$8
MOVL DX, 0(SP)
LEAL argframe+0(FP), CX
MOVL CX, 4(SP)
@@ -19,7 +19,7 @@ TEXT ·makeFuncStub(SB),NOSPLIT,$8
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No argsize here, gc generates argsize info at call site.
-TEXT ·methodValueCall(SB),NOSPLIT,$8
+TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$8
MOVL DX, 0(SP)
LEAL argframe+0(FP), CX
MOVL CX, 4(SP)
diff --git a/src/pkg/reflect/asm_amd64.s b/src/pkg/reflect/asm_amd64.s
index 1aa10440c..712959843 100644
--- a/src/pkg/reflect/asm_amd64.s
+++ b/src/pkg/reflect/asm_amd64.s
@@ -8,7 +8,7 @@
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No argsize here, gc generates argsize info at call site.
-TEXT ·makeFuncStub(SB),NOSPLIT,$16
+TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$16
MOVQ DX, 0(SP)
LEAQ argframe+0(FP), CX
MOVQ CX, 8(SP)
@@ -19,7 +19,7 @@ TEXT ·makeFuncStub(SB),NOSPLIT,$16
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No argsize here, gc generates argsize info at call site.
-TEXT ·methodValueCall(SB),NOSPLIT,$16
+TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$16
MOVQ DX, 0(SP)
LEAQ argframe+0(FP), CX
MOVQ CX, 8(SP)
diff --git a/src/pkg/reflect/asm_arm.s b/src/pkg/reflect/asm_arm.s
index 5e456ea2c..68ded4ac6 100644
--- a/src/pkg/reflect/asm_arm.s
+++ b/src/pkg/reflect/asm_arm.s
@@ -8,7 +8,7 @@
// See the comment on the declaration of makeFuncStub in makefunc.go
// for more details.
// No argsize here, gc generates argsize info at call site.
-TEXT ·makeFuncStub(SB),NOSPLIT,$8
+TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$8
MOVW R7, 4(R13)
MOVW $argframe+0(FP), R1
MOVW R1, 8(R13)
@@ -19,7 +19,7 @@ TEXT ·makeFuncStub(SB),NOSPLIT,$8
// See the comment on the declaration of methodValueCall in makefunc.go
// for more details.
// No argsize here, gc generates argsize info at call site.
-TEXT ·methodValueCall(SB),NOSPLIT,$8
+TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$8
MOVW R7, 4(R13)
MOVW $argframe+0(FP), R1
MOVW R1, 8(R13)
diff --git a/src/pkg/reflect/value.go b/src/pkg/reflect/value.go
index dbecc59da..20fc459e5 100644
--- a/src/pkg/reflect/value.go
+++ b/src/pkg/reflect/value.go
@@ -497,6 +497,10 @@ func (v Value) call(op string, in []Value) []Value {
// frame into a call using Values.
// It is in this file so that it can be next to the call method above.
// The remainder of the MakeFunc implementation is in makefunc.go.
+//
+// NOTE: This function must be marked as a "wrapper" in the generated code,
+// so that the linker can make it work correctly for panic and recover.
+// The gc compilers know to do that for the name "reflect.callReflect".
func callReflect(ctxt *makeFuncImpl, frame unsafe.Pointer) {
ftyp := ctxt.typ
f := ctxt.fn
@@ -650,6 +654,10 @@ func frameSize(t *rtype, rcvr bool) (total, in, outOffset, out uintptr) {
// to deal with individual Values for each argument.
// It is in this file so that it can be next to the two similar functions above.
// The remainder of the makeMethodValue implementation is in makefunc.go.
+//
+// NOTE: This function must be marked as a "wrapper" in the generated code,
+// so that the linker can make it work correctly for panic and recover.
+// The gc compilers know to do that for the name "reflect.callMethod".
func callMethod(ctxt *methodValue, frame unsafe.Pointer) {
t, fn, rcvr := methodReceiver("call", ctxt.rcvr, ctxt.method)
total, in, outOffset, out := frameSize(t, true)
diff --git a/src/pkg/runtime/asm_386.s b/src/pkg/runtime/asm_386.s
index 79f2e7929..5c642c0ed 100644
--- a/src/pkg/runtime/asm_386.s
+++ b/src/pkg/runtime/asm_386.s
@@ -340,7 +340,7 @@ TEXT reflect·call(SB), NOSPLIT, $0-12
JMP AX
#define CALLFN(NAME,MAXSIZE) \
-TEXT runtime·NAME(SB), 0, $MAXSIZE-12; \
+TEXT runtime·NAME(SB), WRAPPER, $MAXSIZE-12; \
/* copy arguments to stack */ \
MOVL argptr+4(FP), SI; \
MOVL argsize+8(FP), CX; \
diff --git a/src/pkg/runtime/asm_amd64.s b/src/pkg/runtime/asm_amd64.s
index a85056c9e..2c2ffedd1 100644
--- a/src/pkg/runtime/asm_amd64.s
+++ b/src/pkg/runtime/asm_amd64.s
@@ -319,7 +319,7 @@ TEXT reflect·call(SB), NOSPLIT, $0-20
JMP AX
#define CALLFN(NAME,MAXSIZE) \
-TEXT runtime·NAME(SB), 0, $MAXSIZE-20; \
+TEXT runtime·NAME(SB), WRAPPER, $MAXSIZE-20; \
/* copy arguments to stack */ \
MOVQ argptr+8(FP), SI; \
MOVLQZX argsize+16(FP), CX; \
diff --git a/src/pkg/runtime/asm_arm.s b/src/pkg/runtime/asm_arm.s
index b66f80e2c..f483e6fc8 100644
--- a/src/pkg/runtime/asm_arm.s
+++ b/src/pkg/runtime/asm_arm.s
@@ -300,7 +300,7 @@ TEXT reflect·call(SB), NOSPLIT, $-4-12
B (R1)
#define CALLFN(NAME,MAXSIZE) \
-TEXT runtime·NAME(SB), 0, $MAXSIZE-12; \
+TEXT runtime·NAME(SB), WRAPPER, $MAXSIZE-12; \
/* copy arguments to stack */ \
MOVW argptr+4(FP), R0; \
MOVW argsize+8(FP), R2; \
diff --git a/src/pkg/runtime/panic.c b/src/pkg/runtime/panic.c
index c14d52016..a1e91d3d8 100644
--- a/src/pkg/runtime/panic.c
+++ b/src/pkg/runtime/panic.c
@@ -339,69 +339,27 @@ runtime·unwindstack(G *gp, byte *sp)
void
runtime·recover(byte *argp, Eface ret)
{
- Stktop *top, *oldtop;
Panic *p;
+ Stktop *top;
- // Must be a panic going on.
- if((p = g->panic) == nil || p->recovered)
- goto nomatch;
-
- // Frame must be at the top of the stack segment,
- // because each deferred call starts a new stack
- // segment as a side effect of using reflect.call.
- // (There has to be some way to remember the
- // variable argument frame size, and the segment
- // code already takes care of that for us, so we
- // reuse it.)
- //
- // As usual closures complicate things: the fp that
- // the closure implementation function claims to have
- // is where the explicit arguments start, after the
- // implicit pointer arguments and PC slot.
- // If we're on the first new segment for a closure,
- // then fp == top - top->args is correct, but if
- // the closure has its own big argument frame and
- // allocated a second segment (see below),
- // the fp is slightly above top - top->args.
- // That condition can't happen normally though
- // (stack pointers go down, not up), so we can accept
- // any fp between top and top - top->args as
- // indicating the top of the segment.
+ // Must be an unrecovered panic in progress.
+ // Must be on a stack segment created for a deferred call during a panic.
+ // Must be at the top of that segment, meaning the deferred call itself
+ // and not something it called. The top frame in the segment will have
+ // argument pointer argp == top - top->argsize.
+ // The subtraction of g->panicwrap allows wrapper functions that
+ // do not count as official calls to adjust what we consider the top frame
+ // while they are active on the stack. The linker emits adjustments of
+ // g->panicwrap in the prologue and epilogue of functions marked as wrappers.
top = (Stktop*)g->stackbase;
- if(argp < (byte*)top - top->argsize || (byte*)top < argp)
- goto nomatch;
-
- // The deferred call makes a new segment big enough
- // for the argument frame but not necessarily big
- // enough for the function's local frame (size unknown
- // at the time of the call), so the function might have
- // made its own segment immediately. If that's the
- // case, back top up to the older one, the one that
- // reflect.call would have made for the panic.
- //
- // The fp comparison here checks that the argument
- // frame that was copied during the split (the top->args
- // bytes above top->fp) abuts the old top of stack.
- // This is a correct test for both closure and non-closure code.
- oldtop = (Stktop*)top->stackbase;
- if(oldtop != nil && top->argp == (byte*)oldtop - top->argsize)
- top = oldtop;
-
- // Now we have the segment that was created to
- // run this call. It must have been marked as a panic segment.
- if(!top->panic)
- goto nomatch;
-
- // Okay, this is the top frame of a deferred call
- // in response to a panic. It can see the panic argument.
- p->recovered = 1;
- ret = p->arg;
- FLUSH(&ret);
- return;
-
-nomatch:
- ret.type = nil;
- ret.data = nil;
+ p = g->panic;
+ if(p != nil && !p->recovered && top->panic && argp == (byte*)top - top->argsize - g->panicwrap) {
+ p->recovered = 1;
+ ret = p->arg;
+ } else {
+ ret.type = nil;
+ ret.data = nil;
+ }
FLUSH(&ret);
}
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index d37014f3a..0edd7e0ac 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -1773,6 +1773,7 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
newg->gopc = (uintptr)callerpc;
newg->status = Grunnable;
newg->goid = runtime·xadd64(&runtime·sched.goidgen, 1);
+ newg->panicwrap = 0;
if(raceenabled)
newg->racectx = runtime·racegostart((void*)callerpc);
runqput(m->p, newg);
diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h
index 151804f2a..9974fa326 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -250,6 +250,8 @@ struct G
// stackguard0 can be set to StackPreempt as opposed to stackguard
uintptr stackguard0; // cannot move - also known to linker, libmach, runtime/cgo
uintptr stackbase; // cannot move - also known to libmach, runtime/cgo
+ uint32 panicwrap; // cannot move - also known to linker
+ uint32 selgen; // valid sudog pointer
Defer* defer;
Panic* panic;
Gobuf sched;
@@ -264,7 +266,6 @@ struct G
void* param; // passed parameter on wakeup
int16 status;
int64 goid;
- uint32 selgen; // valid sudog pointer
int8* waitreason; // if status==Gwaiting
G* schedlink;
bool ispanic;
@@ -403,6 +404,7 @@ struct Stktop
uintptr stackbase;
Gobuf gobuf;
uint32 argsize;
+ uint32 panicwrap;
uint8* argp; // pointer to arguments in old frame
uintptr free; // if free>0, call stackfree using free as size
diff --git a/src/pkg/runtime/stack.c b/src/pkg/runtime/stack.c
index 6b34f091e..011c616ba 100644
--- a/src/pkg/runtime/stack.c
+++ b/src/pkg/runtime/stack.c
@@ -174,6 +174,7 @@ runtime·oldstack(void)
gp->stackbase = top->stackbase;
gp->stackguard = top->stackguard;
gp->stackguard0 = gp->stackguard;
+ gp->panicwrap = top->panicwrap;
if(top->free != 0) {
gp->stacksize -= top->free;
@@ -195,7 +196,7 @@ void
runtime·newstack(void)
{
int32 framesize, argsize, oldstatus;
- Stktop *top;
+ Stktop *top, *oldtop;
byte *stk;
uintptr sp;
uintptr *src, *dst, *dstend;
@@ -316,6 +317,16 @@ runtime·newstack(void)
// copy flag from panic
top->panic = gp->ispanic;
gp->ispanic = false;
+
+ // if this isn't a panic, maybe we're splitting the stack for a panic.
+ // if we're splitting in the top frame, propagate the panic flag
+ // forward so that recover will know we're in a panic.
+ oldtop = (Stktop*)top->stackbase;
+ if(oldtop != nil && oldtop->panic && top->argp == (byte*)oldtop - oldtop->argsize - gp->panicwrap)
+ top->panic = true;
+
+ top->panicwrap = gp->panicwrap;
+ gp->panicwrap = 0;
gp->stackbase = (uintptr)top;
gp->stackguard = (uintptr)stk + StackGuard;