diff options
Diffstat (limited to 'src/lj_err.c')
-rw-r--r-- | src/lj_err.c | 247 |
1 files changed, 236 insertions, 11 deletions
diff --git a/src/lj_err.c b/src/lj_err.c index ba0fac0a..9fc3adc7 100644 --- a/src/lj_err.c +++ b/src/lj_err.c | |||
@@ -52,6 +52,11 @@ | |||
52 | ** the wrapper function feature. Lua errors thrown through C++ frames | 52 | ** the wrapper function feature. Lua errors thrown through C++ frames |
53 | ** cannot be caught by C++ code and C++ destructors are not run. | 53 | ** cannot be caught by C++ code and C++ destructors are not run. |
54 | ** | 54 | ** |
55 | ** - EXT can handle errors from internal helper functions that are called | ||
56 | ** from JIT-compiled code (except for Windows/x86 and 32 bit ARM). | ||
57 | ** INT has no choice but to call the panic handler, if this happens. | ||
58 | ** Note: this is mainly relevant for out-of-memory errors. | ||
59 | ** | ||
55 | ** EXT is the default on all systems where the toolchain produces unwind | 60 | ** EXT is the default on all systems where the toolchain produces unwind |
56 | ** tables by default (*). This is hard-coded and/or detected in src/Makefile. | 61 | ** tables by default (*). This is hard-coded and/or detected in src/Makefile. |
57 | ** You can thwart the detection with: TARGET_XCFLAGS=-DLUAJIT_UNWIND_INTERNAL | 62 | ** You can thwart the detection with: TARGET_XCFLAGS=-DLUAJIT_UNWIND_INTERNAL |
@@ -305,12 +310,59 @@ LJ_FUNCA int lj_err_unwind_win(EXCEPTION_RECORD *rec, | |||
305 | return 1; /* ExceptionContinueSearch */ | 310 | return 1; /* ExceptionContinueSearch */ |
306 | } | 311 | } |
307 | 312 | ||
313 | #if LJ_UNWIND_JIT | ||
314 | |||
315 | #if LJ_TARGET_X64 | ||
316 | #define CONTEXT_REG_PC Rip | ||
317 | #elif LJ_TARGET_ARM64 | ||
318 | #define CONTEXT_REG_PC Pc | ||
319 | #else | ||
320 | #error "NYI: Windows arch-specific unwinder for JIT-compiled code" | ||
321 | #endif | ||
322 | |||
323 | /* Windows unwinder for JIT-compiled code. */ | ||
324 | static void err_unwind_win_jit(global_State *g, int errcode) | ||
325 | { | ||
326 | CONTEXT ctx; | ||
327 | UNWIND_HISTORY_TABLE hist; | ||
328 | |||
329 | memset(&hist, 0, sizeof(hist)); | ||
330 | RtlCaptureContext(&ctx); | ||
331 | while (1) { | ||
332 | uintptr_t frame, base, addr = ctx.CONTEXT_REG_PC; | ||
333 | void *hdata; | ||
334 | PRUNTIME_FUNCTION func = RtlLookupFunctionEntry(addr, &base, &hist); | ||
335 | if (!func) { /* Found frame without .pdata: must be JIT-compiled code. */ | ||
336 | ExitNo exitno; | ||
337 | uintptr_t stub = lj_trace_unwind(G2J(g), addr - sizeof(MCode), &exitno); | ||
338 | if (stub) { /* Jump to side exit to unwind the trace. */ | ||
339 | ctx.CONTEXT_REG_PC = stub; | ||
340 | G2J(g)->exitcode = errcode; | ||
341 | RtlRestoreContext(&ctx, NULL); /* Does not return. */ | ||
342 | } | ||
343 | break; | ||
344 | } | ||
345 | RtlVirtualUnwind(UNW_FLAG_NHANDLER, base, addr, func, | ||
346 | &ctx, &hdata, &frame, NULL); | ||
347 | if (!addr) break; | ||
348 | } | ||
349 | /* Unwinding failed, if we end up here. */ | ||
350 | } | ||
351 | #endif | ||
352 | |||
308 | /* Raise Windows exception. */ | 353 | /* Raise Windows exception. */ |
309 | static void err_raise_ext(global_State *g, int errcode) | 354 | static void err_raise_ext(global_State *g, int errcode) |
310 | { | 355 | { |
311 | #if LJ_HASJIT | 356 | #if LJ_UNWIND_JIT |
357 | if (tvref(g->jit_base)) { | ||
358 | err_unwind_win_jit(g, errcode); | ||
359 | return; /* Unwinding failed. */ | ||
360 | } | ||
361 | #elif LJ_HASJIT | ||
362 | /* Cannot catch on-trace errors for Windows/x86 SEH. Unwind to interpreter. */ | ||
312 | setmref(g->jit_base, NULL); | 363 | setmref(g->jit_base, NULL); |
313 | #endif | 364 | #endif |
365 | UNUSED(g); | ||
314 | RaiseException(LJ_EXCODE_MAKE(errcode), 1 /* EH_NONCONTINUABLE */, 0, NULL); | 366 | RaiseException(LJ_EXCODE_MAKE(errcode), 1 /* EH_NONCONTINUABLE */, 0, NULL); |
315 | } | 367 | } |
316 | 368 | ||
@@ -324,6 +376,7 @@ static void err_raise_ext(global_State *g, int errcode) | |||
324 | typedef struct _Unwind_Context _Unwind_Context; | 376 | typedef struct _Unwind_Context _Unwind_Context; |
325 | 377 | ||
326 | #define _URC_OK 0 | 378 | #define _URC_OK 0 |
379 | #define _URC_FATAL_PHASE2_ERROR 2 | ||
327 | #define _URC_FATAL_PHASE1_ERROR 3 | 380 | #define _URC_FATAL_PHASE1_ERROR 3 |
328 | #define _URC_HANDLER_FOUND 6 | 381 | #define _URC_HANDLER_FOUND 6 |
329 | #define _URC_INSTALL_CONTEXT 7 | 382 | #define _URC_INSTALL_CONTEXT 7 |
@@ -343,9 +396,11 @@ typedef struct _Unwind_Exception | |||
343 | void (*excleanup)(int, struct _Unwind_Exception *); | 396 | void (*excleanup)(int, struct _Unwind_Exception *); |
344 | uintptr_t p1, p2; | 397 | uintptr_t p1, p2; |
345 | } __attribute__((__aligned__)) _Unwind_Exception; | 398 | } __attribute__((__aligned__)) _Unwind_Exception; |
399 | #define UNWIND_EXCEPTION_TYPE _Unwind_Exception | ||
346 | 400 | ||
347 | extern uintptr_t _Unwind_GetCFA(_Unwind_Context *); | 401 | extern uintptr_t _Unwind_GetCFA(_Unwind_Context *); |
348 | extern void _Unwind_SetGR(_Unwind_Context *, int, uintptr_t); | 402 | extern void _Unwind_SetGR(_Unwind_Context *, int, uintptr_t); |
403 | extern uintptr_t _Unwind_GetIP(_Unwind_Context *); | ||
349 | extern void _Unwind_SetIP(_Unwind_Context *, uintptr_t); | 404 | extern void _Unwind_SetIP(_Unwind_Context *, uintptr_t); |
350 | extern void _Unwind_DeleteException(_Unwind_Exception *); | 405 | extern void _Unwind_DeleteException(_Unwind_Exception *); |
351 | extern int _Unwind_RaiseException(_Unwind_Exception *); | 406 | extern int _Unwind_RaiseException(_Unwind_Exception *); |
@@ -418,8 +473,130 @@ LJ_FUNCA int lj_err_unwind_dwarf(int version, int actions, | |||
418 | return _URC_CONTINUE_UNWIND; | 473 | return _URC_CONTINUE_UNWIND; |
419 | } | 474 | } |
420 | 475 | ||
421 | #if LJ_UNWIND_EXT | 476 | #if LJ_UNWIND_EXT && defined(LUA_USE_ASSERT) |
422 | static __thread _Unwind_Exception static_uex; | 477 | struct dwarf_eh_bases { void *tbase, *dbase, *func; }; |
478 | extern const void *_Unwind_Find_FDE(void *pc, struct dwarf_eh_bases *bases); | ||
479 | |||
480 | /* Verify that external error handling actually has a chance to work. */ | ||
481 | void lj_err_verify(void) | ||
482 | { | ||
483 | struct dwarf_eh_bases ehb; | ||
484 | lj_assertX(_Unwind_Find_FDE((void *)lj_err_throw, &ehb), "broken build: external frame unwinding enabled, but missing -funwind-tables"); | ||
485 | lj_assertX(_Unwind_Find_FDE((void *)_Unwind_RaiseException, &ehb), "broken build: external frame unwinding enabled, but system libraries have no unwind tables"); | ||
486 | } | ||
487 | #endif | ||
488 | |||
489 | #if LJ_UNWIND_JIT | ||
490 | /* DWARF2 personality handler for JIT-compiled code. */ | ||
491 | static int err_unwind_jit(int version, int actions, | ||
492 | uint64_t uexclass, _Unwind_Exception *uex, _Unwind_Context *ctx) | ||
493 | { | ||
494 | /* NYI: FFI C++ exception interoperability. */ | ||
495 | if (version != 1 || !LJ_UEXCLASS_CHECK(uexclass)) | ||
496 | return _URC_FATAL_PHASE1_ERROR; | ||
497 | if ((actions & _UA_SEARCH_PHASE)) { | ||
498 | return _URC_HANDLER_FOUND; | ||
499 | } | ||
500 | if ((actions & _UA_CLEANUP_PHASE)) { | ||
501 | global_State *g = *(global_State **)(uex+1); | ||
502 | ExitNo exitno; | ||
503 | uintptr_t addr = _Unwind_GetIP(ctx); /* Return address _after_ call. */ | ||
504 | uintptr_t stub = lj_trace_unwind(G2J(g), addr - sizeof(MCode), &exitno); | ||
505 | lj_assertG(tvref(g->jit_base), "unexpected throw across mcode frame"); | ||
506 | if (stub) { /* Jump to side exit to unwind the trace. */ | ||
507 | G2J(g)->exitcode = LJ_UEXCLASS_ERRCODE(uexclass); | ||
508 | #ifdef LJ_TARGET_MIPS | ||
509 | _Unwind_SetGR(ctx, 4, stub); | ||
510 | _Unwind_SetGR(ctx, 5, exitno); | ||
511 | _Unwind_SetIP(ctx, (uintptr_t)(void *)lj_vm_unwind_stub); | ||
512 | #else | ||
513 | _Unwind_SetIP(ctx, stub); | ||
514 | #endif | ||
515 | return _URC_INSTALL_CONTEXT; | ||
516 | } | ||
517 | return _URC_FATAL_PHASE2_ERROR; | ||
518 | } | ||
519 | return _URC_FATAL_PHASE1_ERROR; | ||
520 | } | ||
521 | |||
522 | /* DWARF2 template frame info for JIT-compiled code. | ||
523 | ** | ||
524 | ** After copying the template to the start of the mcode segment, | ||
525 | ** the frame handler function and the code size is patched. | ||
526 | ** The frame handler always installs a new context to jump to the exit, | ||
527 | ** so don't bother to add any unwind opcodes. | ||
528 | */ | ||
529 | static const uint8_t err_frame_jit_template[] = { | ||
530 | #if LJ_BE | ||
531 | 0,0,0, | ||
532 | #endif | ||
533 | LJ_64 ? 0x1c : 0x14, /* CIE length. */ | ||
534 | #if LJ_LE | ||
535 | 0,0,0, | ||
536 | #endif | ||
537 | 0,0,0,0, 1, 'z','P','R',0, /* CIE mark, CIE version, augmentation. */ | ||
538 | 1, LJ_64 ? 0x78 : 0x7c, LJ_TARGET_EHRAREG, /* Code/data align, RA. */ | ||
539 | #if LJ_64 | ||
540 | 10, 0, 0,0,0,0,0,0,0,0, 0x1b, /* Aug. data ABS handler, PCREL|SDATA4 code. */ | ||
541 | 0,0,0,0,0, /* Alignment. */ | ||
542 | #else | ||
543 | 6, 0, 0,0,0,0, 0x1b, /* Aug. data ABS handler, PCREL|SDATA4 code. */ | ||
544 | 0, /* Alignment. */ | ||
545 | #endif | ||
546 | #if LJ_BE | ||
547 | 0,0,0, | ||
548 | #endif | ||
549 | LJ_64 ? 0x14 : 0x10, /* FDE length. */ | ||
550 | 0,0,0, | ||
551 | LJ_64 ? 0x24 : 0x1c, /* CIE offset. */ | ||
552 | 0,0,0, | ||
553 | LJ_64 ? 0x14 : 0x10, /* Code offset. After Final FDE. */ | ||
554 | #if LJ_LE | ||
555 | 0,0,0, | ||
556 | #endif | ||
557 | 0,0,0,0, 0, 0,0,0, /* Code size, augmentation length, alignment. */ | ||
558 | #if LJ_64 | ||
559 | 0,0,0,0, /* Alignment. */ | ||
560 | #endif | ||
561 | 0,0,0,0 /* Final FDE. */ | ||
562 | }; | ||
563 | |||
564 | #define ERR_FRAME_JIT_OFS_HANDLER 0x12 | ||
565 | #define ERR_FRAME_JIT_OFS_FDE (LJ_64 ? 0x20 : 0x18) | ||
566 | #define ERR_FRAME_JIT_OFS_CODE_SIZE (LJ_64 ? 0x2c : 0x24) | ||
567 | #if LJ_TARGET_OSX | ||
568 | #define ERR_FRAME_JIT_OFS_REGISTER ERR_FRAME_JIT_OFS_FDE | ||
569 | #else | ||
570 | #define ERR_FRAME_JIT_OFS_REGISTER 0 | ||
571 | #endif | ||
572 | |||
573 | extern void __register_frame(const void *); | ||
574 | extern void __deregister_frame(const void *); | ||
575 | |||
576 | uint8_t *lj_err_register_mcode(void *base, size_t sz, uint8_t *info) | ||
577 | { | ||
578 | void **handler; | ||
579 | memcpy(info, err_frame_jit_template, sizeof(err_frame_jit_template)); | ||
580 | handler = (void *)err_unwind_jit; | ||
581 | memcpy(info + ERR_FRAME_JIT_OFS_HANDLER, &handler, sizeof(handler)); | ||
582 | *(uint32_t *)(info + ERR_FRAME_JIT_OFS_CODE_SIZE) = | ||
583 | (uint32_t)(sz - sizeof(err_frame_jit_template) - (info - (uint8_t *)base)); | ||
584 | __register_frame(info + ERR_FRAME_JIT_OFS_REGISTER); | ||
585 | #ifdef LUA_USE_ASSERT | ||
586 | { | ||
587 | struct dwarf_eh_bases ehb; | ||
588 | lj_assertX(_Unwind_Find_FDE(info + sizeof(err_frame_jit_template)+1, &ehb), | ||
589 | "bad JIT unwind table registration"); | ||
590 | } | ||
591 | #endif | ||
592 | return info + sizeof(err_frame_jit_template); | ||
593 | } | ||
594 | |||
595 | void lj_err_deregister_mcode(void *base, size_t sz, uint8_t *info) | ||
596 | { | ||
597 | UNUSED(base); UNUSED(sz); | ||
598 | __deregister_frame(info + ERR_FRAME_JIT_OFS_REGISTER); | ||
599 | } | ||
423 | #endif | 600 | #endif |
424 | 601 | ||
425 | #else /* LJ_TARGET_ARM */ | 602 | #else /* LJ_TARGET_ARM */ |
@@ -430,6 +607,7 @@ static __thread _Unwind_Exception static_uex; | |||
430 | #define _US_FORCE_UNWIND 8 | 607 | #define _US_FORCE_UNWIND 8 |
431 | 608 | ||
432 | typedef struct _Unwind_Control_Block _Unwind_Control_Block; | 609 | typedef struct _Unwind_Control_Block _Unwind_Control_Block; |
610 | #define UNWIND_EXCEPTION_TYPE _Unwind_Control_Block | ||
433 | 611 | ||
434 | struct _Unwind_Control_Block { | 612 | struct _Unwind_Control_Block { |
435 | uint64_t exclass; | 613 | uint64_t exclass; |
@@ -488,25 +666,62 @@ LJ_FUNCA int lj_err_unwind_arm(int state, _Unwind_Control_Block *ucb, | |||
488 | } | 666 | } |
489 | if (__gnu_unwind_frame(ucb, ctx) != _URC_OK) | 667 | if (__gnu_unwind_frame(ucb, ctx) != _URC_OK) |
490 | return _URC_FAILURE; | 668 | return _URC_FAILURE; |
669 | #ifdef LUA_USE_ASSERT | ||
670 | /* We should never get here unless this is a forced unwind aka backtrace. */ | ||
671 | if (_Unwind_GetGR(ctx, 0) == 0xff33aa77) { | ||
672 | _Unwind_SetGR(ctx, 0, 0xff33aa88); | ||
673 | } | ||
674 | #endif | ||
491 | return _URC_CONTINUE_UNWIND; | 675 | return _URC_CONTINUE_UNWIND; |
492 | } | 676 | } |
493 | 677 | ||
494 | #if LJ_UNWIND_EXT | 678 | #if LJ_UNWIND_EXT && defined(LUA_USE_ASSERT) |
495 | static __thread _Unwind_Control_Block static_uex; | 679 | typedef int (*_Unwind_Trace_Fn)(_Unwind_Context *, void *); |
680 | extern int _Unwind_Backtrace(_Unwind_Trace_Fn, void *); | ||
681 | |||
682 | static int err_verify_bt(_Unwind_Context *ctx, int *got) | ||
683 | { | ||
684 | if (_Unwind_GetGR(ctx, 0) == 0xff33aa88) { *got = 2; } | ||
685 | else if (*got == 0) { *got = 1; _Unwind_SetGR(ctx, 0, 0xff33aa77); } | ||
686 | return _URC_OK; | ||
687 | } | ||
688 | |||
689 | /* Verify that external error handling actually has a chance to work. */ | ||
690 | void lj_err_verify(void) | ||
691 | { | ||
692 | int got = 0; | ||
693 | _Unwind_Backtrace((_Unwind_Trace_Fn)err_verify_bt, &got); | ||
694 | lj_assertX(got == 2, "broken build: external frame unwinding enabled, but missing -funwind-tables"); | ||
695 | } | ||
496 | #endif | 696 | #endif |
697 | |||
698 | /* | ||
699 | ** Note: LJ_UNWIND_JIT is not implemented for 32 bit ARM. | ||
700 | ** | ||
701 | ** The quirky ARM unwind API doesn't have __register_frame(). | ||
702 | ** A potential workaround might involve _Unwind_Backtrace. | ||
703 | ** But most 32 bit ARM targets don't qualify for LJ_UNWIND_EXT, anyway, | ||
704 | ** since they are built without unwind tables by default. | ||
705 | */ | ||
706 | |||
497 | #endif /* LJ_TARGET_ARM */ | 707 | #endif /* LJ_TARGET_ARM */ |
498 | 708 | ||
709 | |||
499 | #if LJ_UNWIND_EXT | 710 | #if LJ_UNWIND_EXT |
711 | static __thread struct { | ||
712 | UNWIND_EXCEPTION_TYPE ex; | ||
713 | global_State *g; | ||
714 | } static_uex; | ||
715 | |||
500 | /* Raise external exception. */ | 716 | /* Raise external exception. */ |
501 | static void err_raise_ext(global_State *g, int errcode) | 717 | static void err_raise_ext(global_State *g, int errcode) |
502 | { | 718 | { |
503 | #if LJ_HASJIT | ||
504 | setmref(g->jit_base, NULL); | ||
505 | #endif | ||
506 | memset(&static_uex, 0, sizeof(static_uex)); | 719 | memset(&static_uex, 0, sizeof(static_uex)); |
507 | static_uex.exclass = LJ_UEXCLASS_MAKE(errcode); | 720 | static_uex.ex.exclass = LJ_UEXCLASS_MAKE(errcode); |
508 | _Unwind_RaiseException(&static_uex); | 721 | static_uex.g = g; |
722 | _Unwind_RaiseException(&static_uex.ex); | ||
509 | } | 723 | } |
724 | |||
510 | #endif | 725 | #endif |
511 | 726 | ||
512 | #endif | 727 | #endif |
@@ -615,7 +830,7 @@ static ptrdiff_t finderrfunc(lua_State *L) | |||
615 | /* Runtime error. */ | 830 | /* Runtime error. */ |
616 | LJ_NOINLINE void LJ_FASTCALL lj_err_run(lua_State *L) | 831 | LJ_NOINLINE void LJ_FASTCALL lj_err_run(lua_State *L) |
617 | { | 832 | { |
618 | ptrdiff_t ef = finderrfunc(L); | 833 | ptrdiff_t ef = (LJ_HASJIT && tvref(G(L)->jit_base)) ? 0 : finderrfunc(L); |
619 | if (ef) { | 834 | if (ef) { |
620 | TValue *errfunc = restorestack(L, ef); | 835 | TValue *errfunc = restorestack(L, ef); |
621 | TValue *top = L->top; | 836 | TValue *top = L->top; |
@@ -634,6 +849,16 @@ LJ_NOINLINE void LJ_FASTCALL lj_err_run(lua_State *L) | |||
634 | lj_err_throw(L, LUA_ERRRUN); | 849 | lj_err_throw(L, LUA_ERRRUN); |
635 | } | 850 | } |
636 | 851 | ||
852 | #if LJ_HASJIT | ||
853 | LJ_NOINLINE void LJ_FASTCALL lj_err_trace(lua_State *L, int errcode) | ||
854 | { | ||
855 | if (errcode == LUA_ERRRUN) | ||
856 | lj_err_run(L); | ||
857 | else | ||
858 | lj_err_throw(L, errcode); | ||
859 | } | ||
860 | #endif | ||
861 | |||
637 | /* Formatted runtime error message. */ | 862 | /* Formatted runtime error message. */ |
638 | LJ_NORET LJ_NOINLINE static void err_msgv(lua_State *L, ErrMsg em, ...) | 863 | LJ_NORET LJ_NOINLINE static void err_msgv(lua_State *L, ErrMsg em, ...) |
639 | { | 864 | { |