On Saturday 13 September 2025 00:32:04 Martin Storsjö wrote:
> On Sun, 31 Aug 2025, Pali Rohár wrote:
>
> > diff --git a/mingw-w64-crt/misc/_invoke_watson.c
> > b/mingw-w64-crt/misc/_invoke_watson.c
> > new file mode 100644
> > index 000000000000..a7fa505024b2
> > --- /dev/null
> > +++ b/mingw-w64-crt/misc/_invoke_watson.c
> > @@ -0,0 +1,105 @@
> > +/**
> > + * 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>
> > +
> > +#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() and
> > __builtin_frame_address() 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))
> > +{
> > + EXCEPTION_RECORD exception_record = { 0, };
> > + CONTEXT context = { 0, };
> > + EXCEPTION_POINTERS exception_pointers = { &exception_record, &context
> > };
> > +#if defined(__x86_64__)
> > + ULONG64 establisher_frame;
> > + ULONG64 image_base;
> > + PRUNTIME_FUNCTION function_entry;
> > + PVOID handler_data;
> > +#endif
> > +
> > + if (is_fastfail_available())
> > + __fastfail(FAST_FAIL_INVALID_ARG);
> > +
> > + /*
> > + * 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__)
> > + 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)
> > + :
> > + :
> > + );
> > +#else
> > + RtlCaptureContext(&context);
> > +#endif
> > +
> > + /* Fill additional platform specific fields of the parent (caller)
> > function into context. */
> > +#if defined(__i386__)
> > + context.ContextFlags = CONTEXT_CONTROL;
> > + context.Eip =
> > (uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0)); /*
> > msvc uses _ReturnAddress() */
> > + context.Esp = (uintptr_t)__builtin_frame_address(0); /* msvc uses
> > _AddressOfReturnAddress() */
> > + context.Ebp = *((uintptr_t *)__builtin_frame_address(0)-1); /* msvc
> > uses *((ULONG *)_AddressOfReturnAddress()-1) */
> > +#elif defined(__x86_64__)
> > + function_entry = RtlLookupFunctionEntry(context.Rip, &image_base,
> > NULL);
> > + if (function_entry)
> > + RtlVirtualUnwind(UNW_FLAG_NHANDLER, image_base, context.Rip,
> > function_entry, &context, &handler_data, &establisher_frame, NULL);
> > + context.Rip =
> > (uintptr_t)__builtin_extract_return_addr(__builtin_return_address(0)); /*
> > msvc uses _ReturnAddress() */
> > + context.Rsp = (uintptr_t)__builtin_frame_address(0); /* msvc uses
> > _AddressOfReturnAddress()+8 */
> > + context.Rbp = *((uintptr_t *)__builtin_frame_address(0)-1); /* not
> > filled filled by msvc */
> > +#endif
>
> Shouldn't we do something similar to this for arm/aarch64 too?
>
> // Martin
The RtlCaptureContext() above should take all platform dependent
registers. Just for x86 some adjustments is done to make the IP/SP/BP of
the caller, not of the RtlCaptureContext(). Similar thing is in the
UCRT for msvc compiler.
Note that is_fastfail_available() is always TRUE on arm platforms, so
this code is not called. UCRT contains for arm platforms different
_invoke_watson implementation which just invoke udf / brk.
objdump on ucrtbase.dll binary shows me this:
_invoke_watson:
0: 2D E9 00 48 push.w {fp, lr}
4: EB 46 mov fp, sp
6: 05 20 movs r0, #5
8: FB DE udf #0xfb
00000001800291d0 <_invoke_watson>:
1800291d0: a0 00 80 52 mov w0, #5
1800291d4: 60 00 3e d4 brk #0xf003
This should match the __fastfail at the beginning of the _invoke_watson.
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public