The note about _invalid_parameter function in msvcrt.dll not working is interesting.
I noticed that some msvcrt.dll also export debug functions declared crtdbg.h (such as _CrtDebugReportW), but similar to _invalid_parameter they appear to be useless. I'm making use of these debug functions (mostly _RPT* macros[1]), and they produce no output with msvcrt.dll. I was hoping that I could use those with debug builds with msvcrt.dll, but unfortunately it does not seem to be the case. - Kirill Makurin [1] https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/rpt-rptf-rptw-rptfw-macros ________________________________ From: Pali Rohár <[email protected]> Sent: Monday, September 1, 2025 4:18 AM To: [email protected] <[email protected]> Subject: [Mingw-w64-public] [PATCH 2/6] crt: Add support for _invoke_watson and _invalid_parameter* functions into all CRT import libraries Functions _invoke_watson(), _invalid_parameter(), _invalid_parameter_noinfo(), _invalid_parameter_noinfo_noreturn() are supported since msvcr80.dll. Function _invalid_parameter_noinfo_noreturn() is supported since msvcr100.dll. For older CRT import libraries provide simple wrapper around the _invalid_parameter() and _invoke_watson() calls. OS system version of msvcrt.dll provides function _invalid_parameter() since Windows Vista but it is mostly useless. Seems that it does absolutely nothing. Moreover OS system version of msvcrt.dll does not provide _set_invalid_parameter_handler() function, so application has no way to instruct what OS system version of _invalid_parameter() should call. Therefore threat msvcrt.dll's _invalid_parameter() function as unusable and disable it in msvcrt.def.in file. Note that MS WDK 7.1.0 toolchain which allows to link against the Windows 7 OS system msvcrt.dll library, does not use the msvcrt.dll's _invalid_parameter symbol but rather provides its own code statically linked into executable. This can indicate the fact that the msvcrt.dll's _invalid_parameter is not usable or compatible with msvcr80+. So for pre-msvcr80 CRT import libraries and also for OS system msvcrt.dll import library provide mingw-w64 implementation of those functions. _invalid_parameter() implementation uses _get_invalid_parameter_handler() to retrieve the invalid parameter handler, which is already provided by mingw-w64 for all CRT import libraries. Implementation of _invoke_watson() mimics the behavior of msvcr80+ and UCRT. --- mingw-w64-crt/Makefile.am | 8 ++ mingw-w64-crt/lib-common/msvcrt.def.in | 2 +- mingw-w64-crt/misc/_invalid_parameter.c | 33 ++++++ .../misc/_invalid_parameter_noinfo.c | 16 +++ .../misc/_invalid_parameter_noinfo_noreturn.c | 15 +++ mingw-w64-crt/misc/_invoke_watson.c | 105 ++++++++++++++++++ 6 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 mingw-w64-crt/misc/_invalid_parameter.c create mode 100644 mingw-w64-crt/misc/_invalid_parameter_noinfo.c create mode 100644 mingw-w64-crt/misc/_invalid_parameter_noinfo_noreturn.c create mode 100644 mingw-w64-crt/misc/_invoke_watson.c diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am index c417ed9ebeb5..dc98a298ce1e 100644 --- a/mingw-w64-crt/Makefile.am +++ b/mingw-w64-crt/Makefile.am @@ -344,6 +344,10 @@ src_msvcrt=\ misc/_get_timezone.c \ misc/_get_tzname.c \ misc/_get_wpgmptr.c \ + misc/_invalid_parameter.c \ + misc/_invalid_parameter_noinfo.c \ + misc/_invalid_parameter_noinfo_noreturn.c \ + misc/_invoke_watson.c \ misc/_recalloc.c \ misc/_set_purecall_handler.c \ misc/imaxdiv.c \ @@ -906,6 +910,9 @@ src_pre_msvcr80=\ misc/_get_tzname.c \ misc/_get_wpgmptr.c \ misc/_initterm_e.c \ + misc/_invalid_parameter.c \ + misc/_invalid_parameter_noinfo.c \ + misc/_invoke_watson.c \ misc/_recalloc.c \ misc/_set_doserrno.c \ misc/_set_errno.c \ @@ -926,6 +933,7 @@ src_pre_msvcr80=\ string/wcstok.c src_pre_msvcr100=\ + misc/_invalid_parameter_noinfo_noreturn.c \ misc/imaxdiv.c src_pre_msvcr110=\ diff --git a/mingw-w64-crt/lib-common/msvcrt.def.in b/mingw-w64-crt/lib-common/msvcrt.def.in index 91430d083407..fefddc5b7229 100644 --- a/mingw-w64-crt/lib-common/msvcrt.def.in +++ b/mingw-w64-crt/lib-common/msvcrt.def.in @@ -1445,7 +1445,7 @@ F_ARM_ANY(_gmtime64_s) ; i386 and x64 _gmtime64_s replaced by emu _i64toa_s _i64tow_s F_ARM_ANY(_initterm_e) ; i386 and x64 _initterm_e replaced by emu -_invalid_parameter +; _invalid_parameter replaced by emu, msvcrt.dll's _invalid_parameter is not compatible with msvcr80+/UCRT _isalnum_l _isalpha_l _iscntrl_l diff --git a/mingw-w64-crt/misc/_invalid_parameter.c b/mingw-w64-crt/misc/_invalid_parameter.c new file mode 100644 index 000000000000..67578e3c73be --- /dev/null +++ b/mingw-w64-crt/misc/_invalid_parameter.c @@ -0,0 +1,33 @@ +/** + * 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. + */ + +#define _DEBUG /* needed for _invalid_parameter */ +#include <corecrt.h> +#undef _DEBUG +#include <stdlib.h> + +void __cdecl _invalid_parameter(const wchar_t *expression, const wchar_t *function_name, const wchar_t *file_name, unsigned int line_number, uintptr_t reserved) +{ + _invalid_parameter_handler handler; + + /* TODO: Enable this code when mingw-w64 has _get_thread_local_invalid_parameter_handler() support */ +#if 0 + handler = _get_thread_local_invalid_parameter_handler(); + if (handler) { + handler(expression, function_name, file_name, line_number, reserved); + return; + } +#endif + + handler = _get_invalid_parameter_handler(); + if (handler) { + handler(expression, function_name, file_name, line_number, reserved); + return; + } + + _invoke_watson(expression, function_name, file_name, line_number, reserved); +} +void (__cdecl *__MINGW_IMP_SYMBOL(_invalid_parameter))(const wchar_t *, const wchar_t *, const wchar_t *, unsigned int, uintptr_t) = _invalid_parameter; diff --git a/mingw-w64-crt/misc/_invalid_parameter_noinfo.c b/mingw-w64-crt/misc/_invalid_parameter_noinfo.c new file mode 100644 index 000000000000..ed68e5b521da --- /dev/null +++ b/mingw-w64-crt/misc/_invalid_parameter_noinfo.c @@ -0,0 +1,16 @@ +/** + * 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. + */ + +#define _DEBUG /* needed for _invalid_parameter */ +#include <corecrt.h> +#undef _DEBUG +#include <stddef.h> + +void __cdecl _invalid_parameter_noinfo(void) +{ + _invalid_parameter(NULL, NULL, NULL, 0, 0); +} +void (__cdecl *__MINGW_IMP_SYMBOL(_invalid_parameter_noinfo))(void) = _invalid_parameter_noinfo; diff --git a/mingw-w64-crt/misc/_invalid_parameter_noinfo_noreturn.c b/mingw-w64-crt/misc/_invalid_parameter_noinfo_noreturn.c new file mode 100644 index 000000000000..500860d3f5b5 --- /dev/null +++ b/mingw-w64-crt/misc/_invalid_parameter_noinfo_noreturn.c @@ -0,0 +1,15 @@ +/** + * 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 <stddef.h> + +__MINGW_ATTRIB_NORETURN void __cdecl _invalid_parameter_noinfo_noreturn(void) +{ + _invalid_parameter_noinfo(); + _invoke_watson(NULL, NULL, NULL, 0, 0); +} +__MINGW_ATTRIB_NORETURN void (__cdecl *__MINGW_IMP_SYMBOL(_invalid_parameter_noinfo_noreturn))(void) = _invalid_parameter_noinfo_noreturn; 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 + + 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 all filters, trigger exception and terminate the process. */ + SetUnhandledExceptionFilter(NULL); + UnhandledExceptionFilter(&exception_pointers); + 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; -- 2.20.1 _______________________________________________ Mingw-w64-public mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/mingw-w64-public _______________________________________________ Mingw-w64-public mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/mingw-w64-public
