Windows/x86: Full exception interoperability.

This commit is contained in:
Peter Cawley 2016-05-04 00:13:46 +01:00
parent c31d693761
commit 7a4c9b231a
6 changed files with 79 additions and 14 deletions

View File

@ -110,7 +110,7 @@ static const char *sym_decorate(BuildCtx *ctx,
if (p) { if (p) {
#if LJ_TARGET_X86ORX64 #if LJ_TARGET_X86ORX64
if (!LJ_64 && (ctx->mode == BUILD_coffasm || ctx->mode == BUILD_peobj)) if (!LJ_64 && (ctx->mode == BUILD_coffasm || ctx->mode == BUILD_peobj))
name[0] = '@'; name[0] = name[1] == 'R' ? '_' : '@'; /* Special case for RtlUnwind. */
else else
*p = '\0'; *p = '\0';
#elif (LJ_TARGET_PPC || LJ_TARGET_PPCSPE) && !LJ_TARGET_CONSOLE #elif (LJ_TARGET_PPC || LJ_TARGET_PPCSPE) && !LJ_TARGET_CONSOLE

View File

@ -109,6 +109,8 @@ enum {
#if LJ_TARGET_X64 #if LJ_TARGET_X64
PEOBJ_SECT_PDATA, PEOBJ_SECT_PDATA,
PEOBJ_SECT_XDATA, PEOBJ_SECT_XDATA,
#elif LJ_TARGET_X86
PEOBJ_SECT_SXDATA,
#endif #endif
PEOBJ_SECT_RDATA_Z, PEOBJ_SECT_RDATA_Z,
PEOBJ_NSECTIONS PEOBJ_NSECTIONS
@ -208,6 +210,13 @@ void emit_peobj(BuildCtx *ctx)
sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE; sofs += (pesect[PEOBJ_SECT_XDATA].nreloc = 1) * PEOBJ_RELOC_SIZE;
/* Flags: 40 = read, 30 = align4, 40 = initialized data. */ /* Flags: 40 = read, 30 = align4, 40 = initialized data. */
pesect[PEOBJ_SECT_XDATA].flags = 0x40300040; pesect[PEOBJ_SECT_XDATA].flags = 0x40300040;
#elif LJ_TARGET_X86
memcpy(pesect[PEOBJ_SECT_SXDATA].name, ".sxdata", sizeof(".sxdata")-1);
pesect[PEOBJ_SECT_SXDATA].ofs = sofs;
sofs += (pesect[PEOBJ_SECT_SXDATA].size = 4);
pesect[PEOBJ_SECT_SXDATA].relocofs = sofs;
/* Flags: 40 = read, 30 = align4, 02 = lnk_info, 40 = initialized data. */
pesect[PEOBJ_SECT_SXDATA].flags = 0x40300240;
#endif #endif
memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1); memcpy(pesect[PEOBJ_SECT_RDATA_Z].name, ".rdata$Z", sizeof(".rdata$Z")-1);
@ -232,7 +241,7 @@ void emit_peobj(BuildCtx *ctx)
nrsym = ctx->nrelocsym; nrsym = ctx->nrelocsym;
pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym; pehdr.nsyms = 1+PEOBJ_NSECTIONS*2 + 1+ctx->nsym + nrsym;
#if LJ_TARGET_X64 #if LJ_TARGET_X64
pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win64. */ pehdr.nsyms += 1; /* Symbol for lj_err_unwind_win. */
#endif #endif
/* Write PE object header and all sections. */ /* Write PE object header and all sections. */
@ -312,6 +321,19 @@ void emit_peobj(BuildCtx *ctx)
reloc.type = PEOBJ_RELOC_ADDR32NB; reloc.type = PEOBJ_RELOC_ADDR32NB;
owrite(ctx, &reloc, PEOBJ_RELOC_SIZE); owrite(ctx, &reloc, PEOBJ_RELOC_SIZE);
} }
#elif LJ_TARGET_X86
/* Write .sxdata section. */
for (i = 0; i < nrsym; i++) {
if (!strcmp(ctx->relocsym[i], "_lj_err_unwind_win")) {
uint32_t symidx = 1+2+i;
owrite(ctx, &symidx, 4);
break;
}
}
if (i == nrsym) {
fprintf(stderr, "Error: extern lj_err_unwind_win not used\n");
exit(1);
}
#endif #endif
/* Write .rdata$Z section. */ /* Write .rdata$Z section. */
@ -333,8 +355,10 @@ void emit_peobj(BuildCtx *ctx)
#if LJ_TARGET_X64 #if LJ_TARGET_X64
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA); emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_PDATA);
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA); emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_XDATA);
emit_peobj_sym(ctx, "lj_err_unwind_win64", 0, emit_peobj_sym(ctx, "lj_err_unwind_win", 0,
PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN); PEOBJ_SECT_UNDEF, PEOBJ_TYPE_FUNC, PEOBJ_SCL_EXTERN);
#elif LJ_TARGET_X86
emit_peobj_sym_sect(ctx, pesect, PEOBJ_SECT_SXDATA);
#endif #endif
emit_peobj_sym(ctx, ctx->beginsym, 0, emit_peobj_sym(ctx, ctx->beginsym, 0,

View File

@ -45,7 +45,8 @@
** the wrapper function feature. Lua errors thrown through C++ frames ** the wrapper function feature. Lua errors thrown through C++ frames
** cannot be caught by C++ code and C++ destructors are not run. ** cannot be caught by C++ code and C++ destructors are not run.
** **
** EXT is the default on x64 systems, INT is the default on all other systems. ** EXT is the default on x64 systems and on Windows, INT is the default on all
** other systems.
** **
** EXT can be manually enabled on POSIX systems using GCC and DWARF2 stack ** EXT can be manually enabled on POSIX systems using GCC and DWARF2 stack
** unwinding with -DLUAJIT_UNWIND_EXTERNAL. *All* C code must be compiled ** unwinding with -DLUAJIT_UNWIND_EXTERNAL. *All* C code must be compiled
@ -54,7 +55,6 @@
** and all C libraries that have callbacks which may be used to call back ** and all C libraries that have callbacks which may be used to call back
** into Lua. C++ code must *not* be compiled with -fno-exceptions. ** into Lua. C++ code must *not* be compiled with -fno-exceptions.
** **
** EXT cannot be enabled on WIN32 since system exceptions use code-driven SEH.
** EXT is mandatory on WIN64 since the calling convention has an abundance ** EXT is mandatory on WIN64 since the calling convention has an abundance
** of callee-saved registers (rbx, rbp, rsi, rdi, r12-r15, xmm6-xmm15). ** of callee-saved registers (rbx, rbp, rsi, rdi, r12-r15, xmm6-xmm15).
** The POSIX/x64 interpreter only saves r12/r13 for INT (e.g. PS4). ** The POSIX/x64 interpreter only saves r12/r13 for INT (e.g. PS4).
@ -62,7 +62,7 @@
#if defined(__GNUC__) && (LJ_TARGET_X64 || defined(LUAJIT_UNWIND_EXTERNAL)) && !LJ_NO_UNWIND #if defined(__GNUC__) && (LJ_TARGET_X64 || defined(LUAJIT_UNWIND_EXTERNAL)) && !LJ_NO_UNWIND
#define LJ_UNWIND_EXT 1 #define LJ_UNWIND_EXT 1
#elif LJ_TARGET_X64 && LJ_TARGET_WINDOWS #elif LJ_TARGET_WINDOWS
#define LJ_UNWIND_EXT 1 #define LJ_UNWIND_EXT 1
#endif #endif
@ -352,7 +352,7 @@ LJ_FUNCA int lj_err_unwind_arm(int state, void *ucb, _Unwind_Context *ctx)
#endif #endif
#elif LJ_TARGET_X64 && LJ_ABI_WIN #elif LJ_ABI_WIN
/* /*
** Someone in Redmond owes me several days of my life. A lot of this is ** Someone in Redmond owes me several days of my life. A lot of this is
@ -370,6 +370,7 @@ LJ_FUNCA int lj_err_unwind_arm(int state, void *ucb, _Unwind_Context *ctx)
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#ifdef LJ_TARGET_X64
/* Taken from: http://www.nynaeve.net/?p=99 */ /* Taken from: http://www.nynaeve.net/?p=99 */
typedef struct UndocumentedDispatcherContext { typedef struct UndocumentedDispatcherContext {
ULONG64 ControlPc; ULONG64 ControlPc;
@ -384,11 +385,14 @@ typedef struct UndocumentedDispatcherContext {
ULONG ScopeIndex; ULONG ScopeIndex;
ULONG Fill0; ULONG Fill0;
} UndocumentedDispatcherContext; } UndocumentedDispatcherContext;
#else
typedef void UndocumentedDispatcherContext;
#endif
/* Another wild guess. */ /* Another wild guess. */
extern void __DestructExceptionObject(EXCEPTION_RECORD *rec, int nothrow); extern void __DestructExceptionObject(EXCEPTION_RECORD *rec, int nothrow);
#ifdef MINGW_SDK_INIT #if LJ_TARGET_X64 && defined(MINGW_SDK_INIT)
/* Workaround for broken MinGW64 declaration. */ /* Workaround for broken MinGW64 declaration. */
VOID RtlUnwindEx_FIXED(PVOID,PVOID,PVOID,PVOID,PVOID,PVOID) asm("RtlUnwindEx"); VOID RtlUnwindEx_FIXED(PVOID,PVOID,PVOID,PVOID,PVOID,PVOID) asm("RtlUnwindEx");
#define RtlUnwindEx RtlUnwindEx_FIXED #define RtlUnwindEx RtlUnwindEx_FIXED
@ -402,10 +406,15 @@ VOID RtlUnwindEx_FIXED(PVOID,PVOID,PVOID,PVOID,PVOID,PVOID) asm("RtlUnwindEx");
#define LJ_EXCODE_CHECK(cl) (((cl) ^ LJ_EXCODE) <= 0xff) #define LJ_EXCODE_CHECK(cl) (((cl) ^ LJ_EXCODE) <= 0xff)
#define LJ_EXCODE_ERRCODE(cl) ((int)((cl) & 0xff)) #define LJ_EXCODE_ERRCODE(cl) ((int)((cl) & 0xff))
/* Win64 exception handler for interpreter frame. */ /* Windows exception handler for interpreter frame. */
LJ_FUNCA EXCEPTION_DISPOSITION lj_err_unwind_win64(EXCEPTION_RECORD *rec, LJ_FUNCA EXCEPTION_DISPOSITION lj_err_unwind_win(EXCEPTION_RECORD *rec,
void *cf, CONTEXT *ctx, UndocumentedDispatcherContext *dispatch) void *f, CONTEXT *ctx, UndocumentedDispatcherContext *dispatch)
{ {
#if LJ_TARGET_X64
void *cf = f;
#else
void *cf = (char *)f - CFRAME_OFS_SEH;
#endif
lua_State *L = cframe_L(cf); lua_State *L = cframe_L(cf);
int errcode = LJ_EXCODE_CHECK(rec->ExceptionCode) ? int errcode = LJ_EXCODE_CHECK(rec->ExceptionCode) ?
LJ_EXCODE_ERRCODE(rec->ExceptionCode) : LUA_ERRRUN; LJ_EXCODE_ERRCODE(rec->ExceptionCode) : LUA_ERRRUN;
@ -425,6 +434,7 @@ LJ_FUNCA EXCEPTION_DISPOSITION lj_err_unwind_win64(EXCEPTION_RECORD *rec,
/* Don't catch access violations etc. */ /* Don't catch access violations etc. */
return ExceptionContinueSearch; return ExceptionContinueSearch;
} }
#if LJ_TARGET_X64
/* Unwind the stack and call all handlers for all lower C frames /* Unwind the stack and call all handlers for all lower C frames
** (including ourselves) again with EH_UNWINDING set. Then set ** (including ourselves) again with EH_UNWINDING set. Then set
** rsp = cf, rax = errcode and jump to the specified target. ** rsp = cf, rax = errcode and jump to the specified target.
@ -434,6 +444,18 @@ LJ_FUNCA EXCEPTION_DISPOSITION lj_err_unwind_win64(EXCEPTION_RECORD *rec,
lj_vm_unwind_c_eh), lj_vm_unwind_c_eh),
rec, (void *)(uintptr_t)errcode, ctx, dispatch->HistoryTable); rec, (void *)(uintptr_t)errcode, ctx, dispatch->HistoryTable);
/* RtlUnwindEx should never return. */ /* RtlUnwindEx should never return. */
#else
UNUSED(ctx);
UNUSED(dispatch);
/* Call all handlers for all lower C frames (including ourselves) again
** with EH_UNWINDING set. Then call the specified function, passing cf
** and errcode.
*/
lj_vm_rtlunwind(cf, (void *)rec,
(cframe_unwind_ff(cf2) && errcode != LUA_YIELD) ?
(void *)lj_vm_unwind_ff : (void *)lj_vm_unwind_c, errcode);
/* lj_vm_rtlunwind does not return. */
#endif
} }
} }
return ExceptionContinueSearch; return ExceptionContinueSearch;

View File

@ -65,6 +65,7 @@ enum {
#define CFRAME_OFS_NRES (18*4) #define CFRAME_OFS_NRES (18*4)
#define CFRAME_OFS_PREV (17*4) #define CFRAME_OFS_PREV (17*4)
#define CFRAME_OFS_L (16*4) #define CFRAME_OFS_L (16*4)
#define CFRAME_OFS_SEH (9*4)
#define CFRAME_OFS_PC (6*4) #define CFRAME_OFS_PC (6*4)
#define CFRAME_OFS_MULTRES (5*4) #define CFRAME_OFS_MULTRES (5*4)
#define CFRAME_SIZE (16*4) #define CFRAME_SIZE (16*4)

View File

@ -17,6 +17,10 @@ LJ_ASMF int lj_vm_cpcall(lua_State *L, lua_CFunction func, void *ud,
LJ_ASMF int lj_vm_resume(lua_State *L, TValue *base, int nres1, ptrdiff_t ef); LJ_ASMF int lj_vm_resume(lua_State *L, TValue *base, int nres1, ptrdiff_t ef);
LJ_ASMF_NORET void LJ_FASTCALL lj_vm_unwind_c(void *cframe, int errcode); LJ_ASMF_NORET void LJ_FASTCALL lj_vm_unwind_c(void *cframe, int errcode);
LJ_ASMF_NORET void LJ_FASTCALL lj_vm_unwind_ff(void *cframe); LJ_ASMF_NORET void LJ_FASTCALL lj_vm_unwind_ff(void *cframe);
#if LJ_ABI_WIN && LJ_TARGET_X86
LJ_ASMF_NORET void LJ_FASTCALL lj_vm_rtlunwind(void *cframe, void *excptrec,
void *unwinder, int errcode);
#endif
LJ_ASMF void lj_vm_unwind_c_eh(void); LJ_ASMF void lj_vm_unwind_c_eh(void);
LJ_ASMF void lj_vm_unwind_ff_eh(void); LJ_ASMF void lj_vm_unwind_ff_eh(void);
#if LJ_TARGET_X86ORX64 #if LJ_TARGET_X86ORX64

View File

@ -125,8 +125,9 @@
|.define CFRAME_SPACE, aword*9 // Delta for esp (see <--). |.define CFRAME_SPACE, aword*9 // Delta for esp (see <--).
|.macro saveregs_ |.macro saveregs_
| push edi; push esi; push ebx | push edi; push esi; push ebx
| push 0 | push extern lj_err_unwind_win
| fs; push dword [0] | fs; push dword [0]
| fs; mov [0], esp
| sub esp, CFRAME_SPACE | sub esp, CFRAME_SPACE
|.endmacro |.endmacro
|.macro restoreregs |.macro restoreregs
@ -600,7 +601,7 @@ static void build_subroutines(BuildCtx *ctx)
| mov eax, FCARG2 // Error return status for vm_pcall. | mov eax, FCARG2 // Error return status for vm_pcall.
| mov esp, FCARG1 | mov esp, FCARG1
|.if WIN |.if WIN
| mov FCARG1, SEH_NEXT | lea FCARG1, SEH_NEXT
| fs; mov [0], FCARG1 | fs; mov [0], FCARG1
|.endif |.endif
|.endif |.endif
@ -627,7 +628,7 @@ static void build_subroutines(BuildCtx *ctx)
| and FCARG1, CFRAME_RAWMASK | and FCARG1, CFRAME_RAWMASK
| mov esp, FCARG1 | mov esp, FCARG1
|.if WIN |.if WIN
| mov FCARG1, SEH_NEXT | lea FCARG1, SEH_NEXT
| fs; mov [0], FCARG1 | fs; mov [0], FCARG1
|.endif |.endif
|.endif |.endif
@ -643,6 +644,19 @@ static void build_subroutines(BuildCtx *ctx)
| set_vmstate INTERP | set_vmstate INTERP
| jmp ->vm_returnc // Increments RD/MULTRES and returns. | jmp ->vm_returnc // Increments RD/MULTRES and returns.
| |
|.if WIN and not X64
|->vm_rtlunwind@16: // Thin layer around RtlUnwind
| // (void *cframe, void *excptrec, void *unwinder, int errcode)
| mov [esp], FCARG1 // Return value for RtlUnwind
| push FCARG2 // Exception record for RtlUnwind
| push 0 // Ignored by RtlUnwind
| push dword [FCARG1+CFRAME_OFS_SEH]
| call extern RtlUnwind@16 // Violates ABI (clobbers too much)
| mov FCARG1, eax
| mov FCARG2, [esp+4] // errcode (for vm_unwind_c)
| ret // Jump to unwinder
|.endif
|
|//----------------------------------------------------------------------- |//-----------------------------------------------------------------------
|//-- Grow stack for calls ----------------------------------------------- |//-- Grow stack for calls -----------------------------------------------
|//----------------------------------------------------------------------- |//-----------------------------------------------------------------------