blob: 6bc1e250583abc191033f81aaabb297d42f0846c [file]
/**
* 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;