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)
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
asmbpe(void)
{
......@@ -562,7 +604,8 @@ asmbpe(void)
addexports();
addsymtable();
addpersrc();
addexcept(t);
fh.NumberOfSections = nsect;
fh.TimeDateStamp = time(0);
fh.Characteristics = IMAGE_FILE_RELOCS_STRIPPED|
......@@ -599,8 +642,16 @@ asmbpe(void)
set(Subsystem, IMAGE_SUBSYSTEM_WINDOWS_GUI);
else
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(SizeOfHeapCommit, 0x00001000);
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.
......@@ -37,4 +37,60 @@ struct ExceptionRecord {
byte pad_godefs_0[4];
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
......@@ -6,10 +6,97 @@
#include "defs.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
runtime·initsig(int32 queue)
runtime·initsig(int32)
{
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
......
......@@ -70,6 +70,36 @@ TEXT runtime·setlasterror(SB),7,$0
MOVL AX, 0x68(CX)
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.
TEXT runtime·ctrlhandler(SB),7,$0
PUSHQ BP
......@@ -182,6 +212,13 @@ TEXT runtime·callbackasm(SB),7,$0
POPQ -8(CX)(DX*1) // restore bytes just after the args
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);
TEXT runtime·tstart_stdcall(SB),7,$0
// CX contains first arg newm
......@@ -202,6 +239,7 @@ TEXT runtime·tstart_stdcall(SB),7,$0
// Someday the convention will be D is always cleared.
CLD
CALL runtime·setstacklimits(SB)
CALL runtime·stackcheck(SB) // clobbers AX,CX
CALL runtime·mstart(SB)
......@@ -215,5 +253,6 @@ TEXT runtime·notok(SB),7,$0
// set tls base to DI
TEXT runtime·settls(SB),7,$0
CALL runtime·setstacklimits(SB)
MOVQ DI, 0x58(GS)
RET
......@@ -33,5 +33,10 @@ enum {
};
typedef EXCEPTION_RECORD $ExceptionRecord;
#ifdef _X86_
typedef FLOATING_SAVE_AREA $FloatingSaveArea;
#endif
#ifdef _AMD64_
typedef M128A $M128a;
#endif
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