| /** |
| * This file has no copyright assigned and is placed in the Public Domain. |
| * This file is part of the mingw-w64 runtime package. |
| * No warranty is given; refer to the file DISCLAIMER.PD within this package. |
| */ |
| |
| #include <corecrt.h> |
| #include <windows.h> |
| |
| /* keep SEH_INLINE_ASM in sync with mingw-w64-crt/crt/crtexe.c */ |
| #if defined(__SEH__) && (!defined(__clang__) || __clang_major__ >= 7) |
| #define SEH_INLINE_ASM |
| #endif |
| |
| #if defined(__i386__) |
| DECLSPEC_NOINLINE /* decrease chance of modifying caller's registers and/or stack frame */ |
| #endif |
| static BOOL is_fastfail_available(void) |
| { |
| #if defined(__i386__) |
| HMODULE module = GetModuleHandleA("kernel32.dll"); |
| BOOL (WINAPI *func)(DWORD) = module ? (LPVOID)GetProcAddress(module, "IsProcessorFeaturePresent") : (LPVOID)NULL; |
| return func ? func(PF_FASTFAIL_AVAILABLE) : FALSE; |
| #elif defined(__x86_64__) |
| return IsProcessorFeaturePresent(PF_FASTFAIL_AVAILABLE); |
| #elif defined (__arm__) || defined(__aarch64__) |
| /* On ARM is fastfail always available */ |
| return TRUE; |
| #else |
| #error Unsupported platform |
| #endif |
| } |
| |
| /* DECLSPEC_NOINLINE is needed for __builtin_return_address(), __builtin_frame_address() and __builtin_dwarf_cfa() usage */ |
| DECLSPEC_NOINLINE __MINGW_ATTRIB_NORETURN void __cdecl _invoke_watson(const wchar_t * __UNUSED_PARAM(expression), const wchar_t * __UNUSED_PARAM(function_name), const wchar_t * __UNUSED_PARAM(file_name), unsigned int __UNUSED_PARAM(line_number), uintptr_t __UNUSED_PARAM(reserved)) |
| { |
| if (is_fastfail_available()) |
| __fastfail(FAST_FAIL_INVALID_ARG); |
| |
| CONTEXT context; /* do not initialize context (to zeros) to prevent using registers and clobbering their content. */ |
| /* |
| * RtlCaptureContext() is available since Windows XP. |
| * UCRT runtime uses inline assemly on 32-bit x86. |
| * For compatibility with UCRT do same thing. |
| */ |
| #if defined(__i386__) |
| /* Values for context.Eip, context.Esp and context.Ebp are explicitly |
| * not filled because they are overwritten few lines below. |
| */ |
| asm volatile( |
| "mov %%eax, %0\n\t" |
| "mov %%ecx, %1\n\t" |
| "mov %%edx, %2\n\t" |
| "mov %%ebx, %3\n\t" |
| "mov %%esi, %4\n\t" |
| "mov %%edi, %5\n\t" |
| "movw %%ss, %w6\n\t" |
| "movw %%cs, %w7\n\t" |
| "movw %%ds, %w8\n\t" |
| "movw %%es, %w9\n\t" |
| "movw %%fs, %w10\n\t" |
| "movw %%gs, %w11\n\t" |
| "pushf\n\t" |
| "pop %12" |
| : |
| /* Specify only "m" (memory) to prevent using registers and clobbering their content. */ |
| "=m" (context.Eax), "=m" (context.Ecx), "=m" (context.Edx), |
| "=m" (context.Ebx), "=m" (context.Esi), "=m" (context.Edi), |
| "=m" (context.SegSs), "=m" (context.SegCs), "=m" (context.SegDs), |
| "=m" (context.SegEs), "=m" (context.SegFs), "=m" (context.SegGs), |
| "=m" (context.EFlags) |
| : |
| : "memory" /* compiler barrier */ |
| ); |
| context.ContextFlags = CONTEXT_CONTROL; |
| #else |
| RtlCaptureContext(&context); |
| #endif |
| |
| /* Define and fill all other variables after capturing registers. */ |
| EXCEPTION_RECORD exception_record = { 0, }; |
| EXCEPTION_POINTERS exception_pointers = { &exception_record, &context }; |
| #if defined(__x86_64__) |
| ULONG64 image_base; |
| PRUNTIME_FUNCTION function_entry; |
| #endif |
| |
| /* Fill additional platform specific fields of the parent (caller) function into context. */ |
| #if defined(__i386__) |
| /* |
| * Current stack frame returned by __builtin_frame_address(0) contains |
| * values of caller function: ebp, eip. Therefore ebp of the called |
| * function is dereferenced first value (index 0). |
| */ |
| context.Eip = (uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0)); /* msvc uses _ReturnAddress() */ |
| context.Esp = (uintptr_t)&expression - 4; /* msvc uses _AddressOfReturnAddress() */ |
| context.Ebp = ((uintptr_t *)__builtin_frame_address(0))[0]; /* msvc uses *((ULONG *)_AddressOfReturnAddress()-1) */ |
| #elif defined(__x86_64__) |
| #if defined(SEH_INLINE_ASM) |
| /* FIXME: function_entry is pointer to SEH unwind data table for the current |
| * function, which is generated by linker at the link time. Linker should be |
| * able to provide this pointer at link time and therefore it would not be |
| * required to lookup it at runtime. |
| * TODO: Let linker to resolve function_entry at link time and avoid calling |
| * RtlLookupFunctionEntry() function at all. |
| */ |
| function_entry = RtlLookupFunctionEntry(context.Rip, &image_base, NULL); |
| #else |
| /* SEH unwind data table is not used, so RtlVirtualUnwind() cannot be called. */ |
| function_entry = NULL; |
| #endif |
| if (function_entry) { |
| /* If possible use SEH unwind data table to fill the context structure |
| * with data from the parent (caller). This will update at least fields: |
| * context.Rip, context.Rsp, context.Rbp |
| */ |
| RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, context.Rip, function_entry, &context, &(PVOID){NULL} /*out: HandlerData*/, &(ULONG64){0} /*out: EstablisherFrame*/, NULL); |
| } else { |
| /* If SEH unwind data table is not available then just fix RIP/RSP/RBP. |
| * For gcc we can use __builtin_dwarf_cfa() which returns pointer to the |
| * original rsp with value before caller issued "call" instruction to |
| * our function. Like on x86, current stack frame returned by |
| * __builtin_frame_address(0) contains values of caller function: rbp, |
| * rip. Therefore rbp of the called function is dereferenced first value. |
| * Note that msvc _AddressOfReturnAddress() is address of the rip value |
| * which should be &(((uintptr_t *)__builtin_dwarf_cfa())[-1]) and |
| * therefore below code for context.Rsp should match the msvc behavior. |
| * For clang we cannot use __builtin_dwarf_cfa() as it returns the rsp |
| * value _after_ the function prologue which allocated space for local |
| * variables. This looks like a bug in clang/llvm, which was reported: |
| * https://github.com/llvm/llvm-project/issues/227606 |
| * As a workaround calculate that address from the pointer of 5th |
| * function argument which is passed on the stack above the 32 bytes |
| * of shadow space. __builtin_frame_address() has the same problem, so |
| * it is only used to force rbp to be saved right below the rip value. |
| */ |
| context.Rip = (uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0)); /* msvc uses _ReturnAddress() */ |
| #if defined(__clang__) |
| asm volatile("" : : "r" (__builtin_frame_address(0))); |
| context.Rsp = (uintptr_t)&reserved - 32; /* msvc uses _AddressOfReturnAddress()+8 */ |
| context.Rbp = ((uintptr_t *)context.Rsp)[-2]; /* not filled by msvc */ |
| #else |
| context.Rsp = (uintptr_t)__builtin_dwarf_cfa(); /* msvc uses _AddressOfReturnAddress()+8 */ |
| context.Rbp = ((uintptr_t *)__builtin_frame_address(0))[0]; /* not filled by msvc */ |
| #endif |
| } |
| #endif |
| |
| exception_record.ExceptionCode = STATUS_INVALID_CRUNTIME_PARAMETER; |
| exception_record.ExceptionFlags = EXCEPTION_NONCONTINUABLE; |
| exception_record.ExceptionAddress = __builtin_extract_return_addr(__builtin_return_address(0)); /* msvc uses _ReturnAddress() */ |
| |
| /* Remove top level exception filter and throw unhandled exception. |
| * It will break into debugger or directly start Dr. Watson. */ |
| SetUnhandledExceptionFilter(NULL); |
| UnhandledExceptionFilter(&exception_pointers); |
| |
| /* In case debugger or Dr. Watson returned back then terminate the process. */ |
| TerminateProcess(GetCurrentProcess(), STATUS_INVALID_CRUNTIME_PARAMETER); |
| __builtin_unreachable(); |
| } |
| __MINGW_ATTRIB_NORETURN void (__cdecl *__MINGW_IMP_SYMBOL(_invoke_watson))(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int, uintptr_t) = _invoke_watson; |