Commit 9b011500 authored by Hector Chu's avatar Hector Chu Committed by Alex Brainman

runtime: implement exception handling on windows/amd64

Fixes #2194.

R=rsc, alex.brainman, vcc.163, jp
CC=golang-dev
https://golang.org/cl/4977044
parent 2b6d3b49
...@@ -525,6 +525,48 @@ addpersrc(void) ...@@ -525,6 +525,48 @@ addpersrc(void)
dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h->VirtualSize; dd[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size = h->VirtualSize;
} }
static void
addexcept(IMAGE_SECTION_HEADER *text)
{
IMAGE_SECTION_HEADER *pdata, *xdata;
vlong startoff;
uvlong n;
Sym *sym;
if(thechar != '6')
return;
// write unwind info
sym = lookup("runtime.sigtramp", 0);
startoff = cpos();
lputl(9); // version=1, flags=UNW_FLAG_EHANDLER, rest 0
lputl(sym->value - PEBASE);
lputl(0);
n = cpos() - startoff;
xdata = addpesection(".xdata", n, n);
xdata->Characteristics = IMAGE_SCN_MEM_READ|
IMAGE_SCN_CNT_INITIALIZED_DATA;
chksectoff(xdata, startoff);
strnput("", xdata->SizeOfRawData - n);
// write a function table entry for the whole text segment
startoff = cpos();
lputl(text->VirtualAddress);
lputl(text->VirtualAddress + text->VirtualSize);
lputl(xdata->VirtualAddress);
n = cpos() - startoff;
pdata = addpesection(".pdata", n, n);
pdata->Characteristics = IMAGE_SCN_MEM_READ|
IMAGE_SCN_CNT_INITIALIZED_DATA;
chksectoff(pdata, startoff);
strnput("", pdata->SizeOfRawData - n);
dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].VirtualAddress = pdata->VirtualAddress;
dd[IMAGE_DIRECTORY_ENTRY_EXCEPTION].Size = pdata->VirtualSize;
}
void void
asmbpe(void) asmbpe(void)
{ {
...@@ -562,7 +604,8 @@ asmbpe(void) ...@@ -562,7 +604,8 @@ asmbpe(void)
addexports(); addexports();
addsymtable(); addsymtable();
addpersrc(); addpersrc();
addexcept(t);
fh.NumberOfSections = nsect; fh.NumberOfSections = nsect;
fh.TimeDateStamp = time(0); fh.TimeDateStamp = time(0);
fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED| fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED|
...@@ -599,8 +642,16 @@ asmbpe(void) ...@@ -599,8 +642,16 @@ asmbpe(void)
set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI); set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI);
else else
set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_CUI); set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_CUI);
set(SizeOfStackReserve, 0x0040000);
set(SizeOfStackCommit, 0x00001000); // Disable stack growth as we don't want Windows to
// fiddle with the thread stack limits, which we set
// ourselves to circumvent the stack checks in the
// Windows exception dispatcher.
// Commit size must be strictly less than reserve
// size otherwise reserve will be rounded up to a
// larger size, as verified with VMMap.
set(SizeOfStackReserve, 0x00010000);
set(SizeOfStackCommit, 0x0000ffff);
set(SizeOfHeapReserve, 0x00100000); set(SizeOfHeapReserve, 0x00100000);
set(SizeOfHeapCommit, 0x00001000); set(SizeOfHeapCommit, 0x00001000);
set(NumberOfRvaAndSizes, 16); set(NumberOfRvaAndSizes, 16);
......
// g:\opensource\go\bin\godefs.exe -f -m64 defs.c // c:\go\bin\godefs.exe -f -m64 defs.c
// MACHINE GENERATED - DO NOT EDIT. // MACHINE GENERATED - DO NOT EDIT.
...@@ -37,4 +37,60 @@ struct ExceptionRecord { ...@@ -37,4 +37,60 @@ struct ExceptionRecord {
byte pad_godefs_0[4]; byte pad_godefs_0[4];
uint64 ExceptionInformation[15]; uint64 ExceptionInformation[15];
}; };
typedef struct M128a M128a;
struct M128a {
uint64 Low;
int64 High;
};
typedef struct Context Context;
struct Context {
uint64 P1Home;
uint64 P2Home;
uint64 P3Home;
uint64 P4Home;
uint64 P5Home;
uint64 P6Home;
uint32 ContextFlags;
uint32 MxCsr;
uint16 SegCs;
uint16 SegDs;
uint16 SegEs;
uint16 SegFs;
uint16 SegGs;
uint16 SegSs;
uint32 EFlags;
uint64 Dr0;
uint64 Dr1;
uint64 Dr2;
uint64 Dr3;
uint64 Dr6;
uint64 Dr7;
uint64 Rax;
uint64 Rcx;
uint64 Rdx;
uint64 Rbx;
uint64 Rsp;
uint64 Rbp;
uint64 Rsi;
uint64 Rdi;
uint64 R8;
uint64 R9;
uint64 R10;
uint64 R11;
uint64 R12;
uint64 R13;
uint64 R14;
uint64 R15;
uint64 Rip;
byte Pad_godefs_0[512];
M128a VectorRegister[26];
uint64 VectorControl;
uint64 DebugControl;
uint64 LastBranchToRip;
uint64 LastBranchFromRip;
uint64 LastExceptionToRip;
uint64 LastExceptionFromRip;
};
#pragma pack off #pragma pack off
...@@ -6,10 +6,97 @@ ...@@ -6,10 +6,97 @@
#include "defs.h" #include "defs.h"
#include "os.h" #include "os.h"
extern void *runtime·sigtramp;
void
runtime·dumpregs(Context *r)
{
runtime·printf("rax %X\n", r->Rax);
runtime·printf("rbx %X\n", r->Rbx);
runtime·printf("rcx %X\n", r->Rcx);
runtime·printf("rdx %X\n", r->Rdx);
runtime·printf("rdi %X\n", r->Rdi);
runtime·printf("rsi %X\n", r->Rsi);
runtime·printf("rbp %X\n", r->Rbp);
runtime·printf("rsp %X\n", r->Rsp);
runtime·printf("r8 %X\n", r->R8 );
runtime·printf("r9 %X\n", r->R9 );
runtime·printf("r10 %X\n", r->R10);
runtime·printf("r11 %X\n", r->R11);
runtime·printf("r12 %X\n", r->R12);
runtime·printf("r13 %X\n", r->R13);
runtime·printf("r14 %X\n", r->R14);
runtime·printf("r15 %X\n", r->R15);
runtime·printf("rip %X\n", r->Rip);
runtime·printf("rflags %X\n", r->EFlags);
runtime·printf("cs %X\n", (uint64)r->SegCs);
runtime·printf("fs %X\n", (uint64)r->SegFs);
runtime·printf("gs %X\n", (uint64)r->SegGs);
}
void void
runtime·initsig(int32 queue) runtime·initsig(int32)
{ {
runtime·siginit(); runtime·siginit();
// following line keeps sigtramp alive at link stage
// if there's a better way please write it here
void *p = runtime·sigtramp;
USED(p);
}
uint32
runtime·sighandler(ExceptionRecord *info, Context *r, G *gp)
{
uintptr *sp;
switch(info->ExceptionCode) {
case EXCEPTION_BREAKPOINT:
return 1;
}
if(gp != 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];
gp->sigpc = r->Rip;
// Only push runtime·sigpanic if r->rip != 0.
// If r->rip == 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->Rip != 0) {
sp = (uintptr*)r->Rsp;
*--sp = r->Rip;
r->Rsp = (uintptr)sp;
}
r->Rip = (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->Rip);
runtime·printf("\n");
if(runtime·gotraceback()){
runtime·traceback((void*)r->Rip, (void*)r->Rsp, 0, gp);
runtime·tracebackothers(gp);
runtime·dumpregs(r);
}
runtime·exit(2);
return 0;
} }
void void
......
...@@ -70,6 +70,36 @@ TEXT runtime·setlasterror(SB),7,$0 ...@@ -70,6 +70,36 @@ TEXT runtime·setlasterror(SB),7,$0
MOVL AX, 0x68(CX) MOVL AX, 0x68(CX)
RET RET
TEXT runtime·sigtramp(SB),7,$56
// CX: exception record
// R8: context
// unwinding?
TESTL $6, 4(CX) // exception flags
MOVL $1, AX
JNZ sigdone
// copy arguments for call to sighandler
MOVQ CX, 0(SP)
MOVQ R8, 8(SP)
get_tls(CX)
MOVQ g(CX), CX
MOVQ CX, 16(SP)
MOVQ BX, 24(SP)
MOVQ BP, 32(SP)
MOVQ SI, 40(SP)
MOVQ DI, 48(SP)
CALL runtime·sighandler(SB)
MOVQ 24(SP), BX
MOVQ 32(SP), BP
MOVQ 40(SP), SI
MOVQ 48(SP), DI
sigdone:
RET
// Windows runs the ctrl handler in a new thread. // Windows runs the ctrl handler in a new thread.
TEXT runtime·ctrlhandler(SB),7,$0 TEXT runtime·ctrlhandler(SB),7,$0
PUSHQ BP PUSHQ BP
...@@ -182,6 +212,13 @@ TEXT runtime·callbackasm(SB),7,$0 ...@@ -182,6 +212,13 @@ TEXT runtime·callbackasm(SB),7,$0
POPQ -8(CX)(DX*1) // restore bytes just after the args POPQ -8(CX)(DX*1) // restore bytes just after the args
RET RET
TEXT runtime·setstacklimits(SB),7,$0
MOVQ 0x30(GS), CX
MOVQ $0, 0x10(CX)
MOVQ $0xffffffffffff, AX
MOVQ AX, 0x08(CX)
RET
// uint32 tstart_stdcall(M *newm); // uint32 tstart_stdcall(M *newm);
TEXT runtime·tstart_stdcall(SB),7,$0 TEXT runtime·tstart_stdcall(SB),7,$0
// CX contains first arg newm // CX contains first arg newm
...@@ -202,6 +239,7 @@ TEXT runtime·tstart_stdcall(SB),7,$0 ...@@ -202,6 +239,7 @@ TEXT runtime·tstart_stdcall(SB),7,$0
// Someday the convention will be D is always cleared. // Someday the convention will be D is always cleared.
CLD CLD
CALL runtime·setstacklimits(SB)
CALL runtime·stackcheck(SB) // clobbers AX,CX CALL runtime·stackcheck(SB) // clobbers AX,CX
CALL runtime·mstart(SB) CALL runtime·mstart(SB)
...@@ -215,5 +253,6 @@ TEXT runtime·notok(SB),7,$0 ...@@ -215,5 +253,6 @@ TEXT runtime·notok(SB),7,$0
// set tls base to DI // set tls base to DI
TEXT runtime·settls(SB),7,$0 TEXT runtime·settls(SB),7,$0
CALL runtime·setstacklimits(SB)
MOVQ DI, 0x58(GS) MOVQ DI, 0x58(GS)
RET RET
...@@ -33,5 +33,10 @@ enum { ...@@ -33,5 +33,10 @@ enum {
}; };
typedef EXCEPTION_RECORD $ExceptionRecord; typedef EXCEPTION_RECORD $ExceptionRecord;
#ifdef _X86_
typedef FLOATING_SAVE_AREA $FloatingSaveArea; typedef FLOATING_SAVE_AREA $FloatingSaveArea;
#endif
#ifdef _AMD64_
typedef M128A $M128a;
#endif
typedef CONTEXT $Context; typedef CONTEXT $Context;
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