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

Reply via email to