windows: implement exception handling
R=rsc, brainman
CC=golang-dev
https://golang.org/cl/4079041
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 09c7a1d..5eb466e 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -733,6 +733,14 @@
*/
enum
{
+#ifdef __WINDOWS__
+ // need enough room in guard area for exception handler.
+ // use larger stacks to compensate for larger stack guard.
+ StackSmall = 256,
+ StackGuard = 2048,
+ StackBig = 8192,
+ StackExtra = StackGuard,
+#else
// byte offset of stack guard (g->stackguard) above bottom of stack.
StackGuard = 256,
@@ -745,6 +753,10 @@
// the frame is allocated) is assumed not to be much bigger
// than this amount. it may not be used efficiently if it is.
StackBig = 4096,
+
+ // extra room over frame size when allocating a stack.
+ StackExtra = 1024,
+#endif
};
void
@@ -812,7 +824,7 @@
framesize += argsize;
if(framesize < StackBig)
framesize = StackBig;
- framesize += 1024; // room for more functions, Stktop.
+ framesize += StackExtra; // room for more functions, Stktop.
stk = runtime·stackalloc(framesize);
top = (Stktop*)(stk+framesize-sizeof(*top));
free = true;
@@ -915,7 +927,7 @@
if(newg->stackguard - StackGuard != newg->stack0)
runtime·throw("invalid stack in newg");
} else {
- newg = runtime·malg(4096);
+ newg = runtime·malg(StackBig);
newg->status = Gwaiting;
newg->alllink = runtime·allg;
runtime·allg = newg;
diff --git a/src/pkg/runtime/windows/386/defs.h b/src/pkg/runtime/windows/386/defs.h
index f5a1636..a2a8821 100644
--- a/src/pkg/runtime/windows/386/defs.h
+++ b/src/pkg/runtime/windows/386/defs.h
@@ -10,8 +10,69 @@
PROT_EXEC = 0x4,
MAP_ANON = 0x1,
MAP_PRIVATE = 0x2,
+ 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,
};
// Types
#pragma pack on
+
+typedef struct ExceptionRecord ExceptionRecord;
+struct ExceptionRecord {
+ uint32 ExceptionCode;
+ uint32 ExceptionFlags;
+ ExceptionRecord *ExceptionRecord;
+ void *ExceptionAddress;
+ uint32 NumberParameters;
+ uint32 ExceptionInformation[15];
+};
+
+typedef struct FloatingSaveArea FloatingSaveArea;
+struct FloatingSaveArea {
+ uint32 ControlWord;
+ uint32 StatusWord;
+ uint32 TagWord;
+ uint32 ErrorOffset;
+ uint32 ErrorSelector;
+ uint32 DataOffset;
+ uint32 DataSelector;
+ uint8 RegisterArea[80];
+ uint32 Cr0NpxState;
+};
+
+typedef struct Context Context;
+struct Context {
+ uint32 ContextFlags;
+ uint32 Dr0;
+ uint32 Dr1;
+ uint32 Dr2;
+ uint32 Dr3;
+ uint32 Dr6;
+ uint32 Dr7;
+ FloatingSaveArea FloatSave;
+ uint32 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;
+ uint8 ExtendedRegisters[512];
+};
#pragma pack off
diff --git a/src/pkg/runtime/windows/386/rt0.s b/src/pkg/runtime/windows/386/rt0.s
index e379830..4b67a9f 100644
--- a/src/pkg/runtime/windows/386/rt0.s
+++ b/src/pkg/runtime/windows/386/rt0.s
@@ -3,4 +3,9 @@
// license that can be found in the LICENSE file.
TEXT _rt0_386_windows(SB),7,$0
+ // Set up SEH frame for bootstrap m
+ PUSHL $runtime·sigtramp(SB)
+ PUSHL 0(FS)
+ MOVL SP, 0(FS)
+
JMP _rt0_386(SB)
diff --git a/src/pkg/runtime/windows/386/signal.c b/src/pkg/runtime/windows/386/signal.c
index 2ae79e5..3360252 100644
--- a/src/pkg/runtime/windows/386/signal.c
+++ b/src/pkg/runtime/windows/386/signal.c
@@ -3,6 +3,26 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "defs.h"
+#include "os.h"
+
+void
+runtime·dumpregs(Context *r)
+{
+ runtime·printf("eax %x\n", r->Eax);
+ runtime·printf("ebx %x\n", r->Ebx);
+ runtime·printf("ecx %x\n", r->Ecx);
+ runtime·printf("edx %x\n", r->Edx);
+ runtime·printf("edi %x\n", r->Edi);
+ runtime·printf("esi %x\n", r->Esi);
+ runtime·printf("ebp %x\n", r->Ebp);
+ runtime·printf("esp %x\n", r->Esp);
+ runtime·printf("eip %x\n", r->Eip);
+ runtime·printf("eflags %x\n", r->EFlags);
+ runtime·printf("cs %x\n", r->SegCs);
+ runtime·printf("fs %x\n", r->SegFs);
+ runtime·printf("gs %x\n", r->SegGs);
+}
void
runtime·initsig(int32)
@@ -15,3 +35,61 @@
return runtime·emptystring;
}
+uint32
+runtime·sighandler(ExceptionRecord *info, void *frame, Context *r)
+{
+ uintptr *sp;
+ G *gp;
+
+ USED(frame);
+
+ switch(info->ExceptionCode) {
+ case EXCEPTION_BREAKPOINT:
+ r->Eip--; // because 8l generates 2 bytes for INT3
+ return 1;
+ }
+
+ if((gp = m->curg) != nil && runtime·issigpanic(info->ExceptionCode)) {
+ // Make it look like a call to the signal func.
+ // Have to pass arguments out of band since
+ // augmenting the stack frame would break
+ // the unwinding code.
+ gp->sig = info->ExceptionCode;
+ gp->sigcode0 = info->ExceptionInformation[0];
+ gp->sigcode1 = info->ExceptionInformation[1];
+
+ // Only push runtime·sigpanic if r->eip != 0.
+ // If r->eip == 0, probably panicked because of a
+ // call to a nil func. Not pushing that onto sp will
+ // make the trace look like a call to runtime·sigpanic instead.
+ // (Otherwise the trace will end at runtime·sigpanic and we
+ // won't get to see who faulted.)
+ if(r->Eip != 0) {
+ sp = (uintptr*)r->Esp;
+ *--sp = r->Eip;
+ r->Esp = (uintptr)sp;
+ }
+ r->Eip = (uintptr)runtime·sigpanic;
+ return 0;
+ }
+
+ if(runtime·panicking) // traceback already printed
+ runtime·exit(2);
+ runtime·panicking = 1;
+
+ runtime·printf("Exception %x %p %p\n", info->ExceptionCode,
+ info->ExceptionInformation[0], info->ExceptionInformation[1]);
+
+ runtime·printf("PC=%x\n", r->Eip);
+ runtime·printf("\n");
+
+ if(runtime·gotraceback()){
+ runtime·traceback((void*)r->Eip, (void*)r->Esp, 0, m->curg);
+ runtime·tracebackothers(m->curg);
+ runtime·dumpregs(r);
+ }
+
+ runtime·breakpoint();
+ runtime·exit(2);
+ return 0;
+}
diff --git a/src/pkg/runtime/windows/386/sys.s b/src/pkg/runtime/windows/386/sys.s
index 7f99b34..b3abab5 100644
--- a/src/pkg/runtime/windows/386/sys.s
+++ b/src/pkg/runtime/windows/386/sys.s
@@ -48,12 +48,58 @@
RET
+TEXT runtime·sigtramp(SB),7,$0
+ PUSHL BP // cdecl
+ PUSHL 0(FS)
+ CALL runtime·sigtramp1(SB)
+ POPL 0(FS)
+ POPL BP
+ RET
+
+TEXT runtime·sigtramp1(SB),0,$16-28
+ // unwinding?
+ MOVL info+12(FP), BX
+ MOVL 4(BX), CX // exception flags
+ ANDL $6, CX
+ MOVL $1, AX
+ JNZ sigdone
+
+ // place ourselves at the top of the SEH chain to
+ // ensure SEH frames lie within thread stack bounds
+ MOVL frame+16(FP), CX // our SEH frame
+ MOVL CX, 0(FS)
+
+ // copy arguments for call to sighandler
+ MOVL BX, 0(SP)
+ MOVL CX, 4(SP)
+ MOVL context+20(FP), BX
+ MOVL BX, 8(SP)
+ MOVL dispatcher+24(FP), BX
+ MOVL BX, 12(SP)
+
+ CALL runtime·sighandler(SB)
+ TESTL AX, AX
+ JZ sigdone
+
+ // call windows default handler early
+ MOVL 4(SP), BX // our SEH frame
+ MOVL 0(BX), BX // SEH frame of default handler
+ MOVL 4(BX), AX // handler function pointer
+ MOVL BX, 4(SP) // set establisher frame
+ CALL AX
+
+sigdone:
+ RET
+
// void tstart(M *newm);
TEXT runtime·tstart(SB),7,$0
MOVL newm+4(SP), CX // m
MOVL m_g0(CX), DX // g
- MOVL SP, DI // remember stack
+ // Set up SEH frame
+ PUSHL $runtime·sigtramp(SB)
+ PUSHL 0(FS)
+ MOVL SP, 0(FS)
// Layout new m scheduler stack on os stack.
MOVL SP, AX
@@ -74,14 +120,14 @@
// Someday the convention will be D is always cleared.
CLD
- PUSHL DI // original stack
-
CALL runtime·stackcheck(SB) // clobbers AX,CX
CALL runtime·mstart(SB)
- POPL DI // original stack
- MOVL DI, SP
+ // Pop SEH frame
+ MOVL 0(FS), SP
+ POPL 0(FS)
+ POPL CX
RET
diff --git a/src/pkg/runtime/windows/defs.c b/src/pkg/runtime/windows/defs.c
index db5f140..5aac03c 100644
--- a/src/pkg/runtime/windows/defs.c
+++ b/src/pkg/runtime/windows/defs.c
@@ -2,6 +2,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
+#include <stdarg.h>
+#include <windef.h>
+#include <winbase.h>
+
enum {
$PROT_NONE = 0,
$PROT_READ = 1,
@@ -10,4 +14,18 @@
$MAP_ANON = 1,
$MAP_PRIVATE = 2,
+
+ $EXCEPTION_ACCESS_VIOLATION = STATUS_ACCESS_VIOLATION,
+ $EXCEPTION_BREAKPOINT = STATUS_BREAKPOINT,
+ $EXCEPTION_FLT_DENORMAL_OPERAND = STATUS_FLOAT_DENORMAL_OPERAND,
+ $EXCEPTION_FLT_DIVIDE_BY_ZERO = STATUS_FLOAT_DIVIDE_BY_ZERO,
+ $EXCEPTION_FLT_INEXACT_RESULT = STATUS_FLOAT_INEXACT_RESULT,
+ $EXCEPTION_FLT_OVERFLOW = STATUS_FLOAT_OVERFLOW,
+ $EXCEPTION_FLT_UNDERFLOW = STATUS_FLOAT_UNDERFLOW,
+ $EXCEPTION_INT_DIVIDE_BY_ZERO = STATUS_INTEGER_DIVIDE_BY_ZERO,
+ $EXCEPTION_INT_OVERFLOW = STATUS_INTEGER_OVERFLOW,
};
+
+typedef EXCEPTION_RECORD $ExceptionRecord;
+typedef FLOATING_SAVE_AREA $FloatingSaveArea;
+typedef CONTEXT $Context;
diff --git a/src/pkg/runtime/windows/mem.c b/src/pkg/runtime/windows/mem.c
index ba89887..c42bf9f 100644
--- a/src/pkg/runtime/windows/mem.c
+++ b/src/pkg/runtime/windows/mem.c
@@ -53,6 +53,7 @@
{
uintptr r;
+ USED(n);
r = (uintptr)runtime·stdcall(runtime·VirtualFree, 3, v, 0, MEM_RELEASE);
if(r == 0)
abort("VirtualFree");
diff --git a/src/pkg/runtime/windows/os.h b/src/pkg/runtime/windows/os.h
index 77d0d32..23f6863 100644
--- a/src/pkg/runtime/windows/os.h
+++ b/src/pkg/runtime/windows/os.h
@@ -34,3 +34,5 @@
};
void runtime·syscall(StdcallParams *p);
+uint32 runtime·issigpanic(uint32);
+void runtime·sigpanic(void);
diff --git a/src/pkg/runtime/windows/thread.c b/src/pkg/runtime/windows/thread.c
index 9b51813..5ab5128 100644
--- a/src/pkg/runtime/windows/thread.c
+++ b/src/pkg/runtime/windows/thread.c
@@ -3,6 +3,7 @@
// license that can be found in the LICENSE file.
#include "runtime.h"
+#include "defs.h"
#include "os.h"
#pragma dynimport runtime·LoadLibraryEx LoadLibraryExA "kernel32.dll"
@@ -215,3 +216,43 @@
p->err = (uintptr)runtime·stdcall_raw(runtime·GetLastError, 0, &a);
runtime·exitsyscall();
}
+
+uint32
+runtime·issigpanic(uint32 code)
+{
+ switch(code) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ case EXCEPTION_INT_OVERFLOW:
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_UNDERFLOW:
+ return 1;
+ }
+ return 0;
+}
+
+void
+runtime·sigpanic(void)
+{
+ switch(g->sig) {
+ case EXCEPTION_ACCESS_VIOLATION:
+ if(g->sigcode1 < 0x1000)
+ runtime·panicstring("invalid memory address or nil pointer dereference");
+ runtime·printf("unexpected fault address %p\n", g->sigcode1);
+ runtime·throw("fault");
+ case EXCEPTION_INT_DIVIDE_BY_ZERO:
+ runtime·panicstring("integer divide by zero");
+ case EXCEPTION_INT_OVERFLOW:
+ runtime·panicstring("integer overflow");
+ case EXCEPTION_FLT_DENORMAL_OPERAND:
+ case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+ case EXCEPTION_FLT_INEXACT_RESULT:
+ case EXCEPTION_FLT_OVERFLOW:
+ case EXCEPTION_FLT_UNDERFLOW:
+ runtime·panicstring("floating point error");
+ }
+ runtime·throw("fault");
+}