Hello,
This is updated version of assert() changes.
There are major changes in the third patch which updates _wassert() emulation:
- Remove incorrect reference to `wcstombs` as being thread-unsafe in commit
message
- Call `WideCharToMultiByte` to calculate buffer size required to hold
converted strings
- If converted string is being truncated, append "..." to end of the converted
string
While testing truncation logic, I realized that `WideCharToMultiByte` does not
store partial output in the buffer, but rather fails with
`ERROR_INSUFFICIENT_BUFFER`. For this reason, when a string is being truncated,
we convert wide characters one-by-one. Once we add `_set_error_mode` emulation
for crtdll.dll and msvcrt{10,20}.dll, this loop can be extended to handle
special case for "C" locale.
There is also one more patch with a small change for t_assert test. It now
waits until child process terminates before reading from the pipe, which allows
us to print and examine assertion message written by the child process. It was
really helpful in testing truncation logic.
- Kirill Makurin
From 6adc4d27b7a430bea7ce259b9196d6d7eb5dada2 Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 22 Jun 2026 18:59:42 +0900
Subject: [PATCH 1/4] crt: always use _wassert() to implement assert() macro
Both `_assert` and `_wassert` are helper function used to implement `assert`
macro; they can be called directly, but it is highly discouraged.
Using `_assert` has multiple issues:
First, string literals passed to `_assert` are usually encoded using
"execution charset," which is usually UTF-8 with gcc and clang. Those strings
are either printed to `stderr` or displayed in a message box. In former case,
the string will be interpreted using code page used by active CRT locale, in
the latter case, using active ANSI code page. If either string contains
non-ASCII characters, there is a high chance that that string will be displayed
incorrectly (mojibake). Also note that only UCRT supports UTF-8 locales.
Second, since msvcr80.dll, and msvcrt.dll on Vista, it is possible to enable
Unicode translation modes on `stderr`. Doing so, makes `_assert` produce no
output at all; with debug version of CRT, it will trigger an internal assertion.
Using `_wassert` solves both issues. For CRTs where `_wassert` is not available,
we already provide a wrapper which redirects to `_assert`; for msvcrt.dll,
this wrapper also checks whether `_wassert` is available and can be used.
Signed-off-by: Kirill Makurin <[email protected]>
---
mingw-w64-headers/crt/assert.h | 7 -------
1 file changed, 7 deletions(-)
diff --git a/mingw-w64-headers/crt/assert.h b/mingw-w64-headers/crt/assert.h
index 51a223a0b..99c127e4f 100644
--- a/mingw-w64-headers/crt/assert.h
+++ b/mingw-w64-headers/crt/assert.h
@@ -41,15 +41,8 @@ void __cdecl _assert (const char *_Message, const char
*_File, unsigned _Line);
#ifdef NDEBUG
#define assert(_Expression) ((void)0)
#else /* !defined (NDEBUG) */
-#if defined(_UNICODE) || defined(UNICODE)
#define assert(_Expression) \
(void) \
((!!(_Expression)) || \
(_wassert(_CRT_WIDE(#_Expression),_CRT_WIDE(__FILE__),__LINE__),0))
-#else /* not unicode */
-#define assert(_Expression) \
- (void) \
- ((!!(_Expression)) || \
- (_assert(#_Expression,__FILE__,__LINE__),0))
-#endif /* _UNICODE||UNICODE */
#endif /* !defined (NDEBUG) */
--
2.51.0.windows.1
From b6910641a29a9aee674942a8c52825cf1ba3a59e Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 22 Jun 2026 18:59:42 +0900
Subject: [PATCH 2/4] crt: remove mingw-w64 wrapper for _assert()
Now that we always use `_wassert` to implement `assert` macro, there is no need
to provide wrapper for `_assert`.
The purpose of `_assert` wrapper was to make `assert` usable when an Unicode
translation mode was enabled on stderr. At the time when this wrapper was added,
it was asumed that `_assert` does not return, when in fact it can.
It is important to keep in mind that after displaying the message, `_assert`
terminates the process by calling `abort`; it is possible to escape from `abort`
by setting signal handler for SIGABRT which calls `longjmp`.
If an application were to perform such escape, previous translation mode used on
stderr will not be restored and it will be left in `_O_TEXT` translation mode.
`_assert` is also called from mingw-w64 wrapper for `_wassert`, which allocates
memory using `malloc`; such escape from `_assert` would result in a memory leak.
To avoid issues descrived above, remove wrapper for `_assert`. Any application
that calls `_assert` directly must take its limitations into account.
This change effectively reverts b2c3c92b542df57ed8bfcd798928692e94a35308.
Signed-off-by: Kirill Makurin <[email protected]>
---
mingw-w64-crt/Makefile.am | 1 -
.../api-ms-win-crt-runtime-l1-1-0.def.in | 2 +-
mingw-w64-crt/lib-common/msvcr120_app.def.in | 2 +-
mingw-w64-crt/lib-common/msvcrt.def.in | 2 +-
.../lib-common/ucrtbase-common.def.in | 2 +-
mingw-w64-crt/lib32/crtdll.def.in | 2 +-
mingw-w64-crt/lib32/msvcr100.def.in | 2 +-
mingw-w64-crt/lib32/msvcr100d.def.in | 2 +-
mingw-w64-crt/lib32/msvcr110.def.in | 2 +-
mingw-w64-crt/lib32/msvcr110d.def.in | 2 +-
mingw-w64-crt/lib32/msvcr120.def.in | 2 +-
mingw-w64-crt/lib32/msvcr120d.def.in | 2 +-
mingw-w64-crt/lib32/msvcr40d.def.in | 2 +-
mingw-w64-crt/lib32/msvcr70.def.in | 2 +-
mingw-w64-crt/lib32/msvcr70d.def.in | 2 +-
mingw-w64-crt/lib32/msvcr71.def.in | 2 +-
mingw-w64-crt/lib32/msvcr71d.def.in | 2 +-
mingw-w64-crt/lib32/msvcr80.def.in | 2 +-
mingw-w64-crt/lib32/msvcr80d.def.in | 2 +-
mingw-w64-crt/lib32/msvcr90.def.in | 2 +-
mingw-w64-crt/lib32/msvcr90d.def.in | 2 +-
mingw-w64-crt/lib32/msvcrt10.def.in | 2 +-
mingw-w64-crt/lib32/msvcrt20.def.in | 2 +-
mingw-w64-crt/lib32/msvcrt40.def.in | 2 +-
mingw-w64-crt/lib32/msvcrtd.def.in | 2 +-
mingw-w64-crt/lib64/msvcr100.def.in | 2 +-
mingw-w64-crt/lib64/msvcr100d.def.in | 2 +-
mingw-w64-crt/lib64/msvcr110.def.in | 2 +-
mingw-w64-crt/lib64/msvcr110d.def.in | 2 +-
mingw-w64-crt/lib64/msvcr120.def.in | 2 +-
mingw-w64-crt/lib64/msvcr120d.def.in | 2 +-
mingw-w64-crt/lib64/msvcr80.def.in | 2 +-
mingw-w64-crt/lib64/msvcr80d.def.in | 2 +-
mingw-w64-crt/lib64/msvcr90.def.in | 2 +-
mingw-w64-crt/lib64/msvcr90d.def.in | 2 +-
mingw-w64-crt/libarm32/msvcr110.def.in | 2 +-
mingw-w64-crt/libarm32/msvcr110d.def.in | 2 +-
mingw-w64-crt/libarm32/msvcr120.def.in | 2 +-
mingw-w64-crt/libarm32/msvcr120d.def.in | 2 +-
mingw-w64-crt/misc/_assert.c | 46 -------------------
mingw-w64-headers/crt/assert.h | 2 +-
41 files changed, 39 insertions(+), 86 deletions(-)
delete mode 100644 mingw-w64-crt/misc/_assert.c
diff --git a/mingw-w64-crt/Makefile.am b/mingw-w64-crt/Makefile.am
index b651dce59..b122f84ce 100644
--- a/mingw-w64-crt/Makefile.am
+++ b/mingw-w64-crt/Makefile.am
@@ -1268,7 +1268,6 @@ src_libmingwex=\
math/powi.def.h math/sqrt.def.h \
math/cephes_mconf.h math/fp_consts.h math/pi_const.h \
\
- misc/_assert.c \
misc/mingw_longjmp.S \
misc/mingw_getsp.S \
misc/alarm.c \
diff --git a/mingw-w64-crt/lib-common/api-ms-win-crt-runtime-l1-1-0.def.in
b/mingw-w64-crt/lib-common/api-ms-win-crt-runtime-l1-1-0.def.in
index ef0aa03ba..79a7ac15c 100644
--- a/mingw-w64-crt/lib-common/api-ms-win-crt-runtime-l1-1-0.def.in
+++ b/mingw-w64-crt/lib-common/api-ms-win-crt-runtime-l1-1-0.def.in
@@ -24,7 +24,7 @@ __threadid
__wcserror
__wcserror_s
; DATA set manually
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_beginthread
_beginthreadex
_c_exit
diff --git a/mingw-w64-crt/lib-common/msvcr120_app.def.in
b/mingw-w64-crt/lib-common/msvcr120_app.def.in
index 0963eea67..06260eee3 100644
--- a/mingw-w64-crt/lib-common/msvcr120_app.def.in
+++ b/mingw-w64-crt/lib-common/msvcr120_app.def.in
@@ -1179,7 +1179,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib-common/msvcrt.def.in
b/mingw-w64-crt/lib-common/msvcrt.def.in
index 790915920..39ece04fe 100644
--- a/mingw-w64-crt/lib-common/msvcrt.def.in
+++ b/mingw-w64-crt/lib-common/msvcrt.def.in
@@ -531,7 +531,7 @@ F_I386(_adj_fptan)
F_I386(_adjust_fdiv DATA)
_aexit_rtn DATA
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atoi64
_atoldbl
diff --git a/mingw-w64-crt/lib-common/ucrtbase-common.def.in
b/mingw-w64-crt/lib-common/ucrtbase-common.def.in
index 377ceef2f..38c505072 100644
--- a/mingw-w64-crt/lib-common/ucrtbase-common.def.in
+++ b/mingw-w64-crt/lib-common/ucrtbase-common.def.in
@@ -246,7 +246,7 @@ _aligned_realloc
F_DEBUG(_aligned_realloc_dbg)
_aligned_recalloc
F_DEBUG(_aligned_recalloc_dbg)
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/crtdll.def.in
b/mingw-w64-crt/lib32/crtdll.def.in
index 99ededa69..154d7add2 100644
--- a/mingw-w64-crt/lib32/crtdll.def.in
+++ b/mingw-w64-crt/lib32/crtdll.def.in
@@ -110,7 +110,7 @@ _acmdln DATA == _acmdln_dll
_aexit_rtn_dll DATA
_aexit_rtn DATA == _aexit_rtn_dll
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_basemajor_dll DATA
_baseminor_dll DATA
_baseversion_dll DATA
diff --git a/mingw-w64-crt/lib32/msvcr100.def.in
b/mingw-w64-crt/lib32/msvcr100.def.in
index 50d31699a..1db3e0720 100644
--- a/mingw-w64-crt/lib32/msvcr100.def.in
+++ b/mingw-w64-crt/lib32/msvcr100.def.in
@@ -730,7 +730,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/msvcr100d.def.in
b/mingw-w64-crt/lib32/msvcr100d.def.in
index ee5717d07..9899fe057 100644
--- a/mingw-w64-crt/lib32/msvcr100d.def.in
+++ b/mingw-w64-crt/lib32/msvcr100d.def.in
@@ -777,7 +777,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/msvcr110.def.in
b/mingw-w64-crt/lib32/msvcr110.def.in
index 09a7e68bf..1a4db8110 100644
--- a/mingw-w64-crt/lib32/msvcr110.def.in
+++ b/mingw-w64-crt/lib32/msvcr110.def.in
@@ -854,7 +854,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/msvcr110d.def.in
b/mingw-w64-crt/lib32/msvcr110d.def.in
index 3448d40e7..0b5269046 100644
--- a/mingw-w64-crt/lib32/msvcr110d.def.in
+++ b/mingw-w64-crt/lib32/msvcr110d.def.in
@@ -901,7 +901,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/msvcr120.def.in
b/mingw-w64-crt/lib32/msvcr120.def.in
index 25933f156..af88bbbd5 100644
--- a/mingw-w64-crt/lib32/msvcr120.def.in
+++ b/mingw-w64-crt/lib32/msvcr120.def.in
@@ -865,7 +865,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/msvcr120d.def.in
b/mingw-w64-crt/lib32/msvcr120d.def.in
index e362dcd87..b8d4c9bb1 100644
--- a/mingw-w64-crt/lib32/msvcr120d.def.in
+++ b/mingw-w64-crt/lib32/msvcr120d.def.in
@@ -912,7 +912,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/msvcr40d.def.in
b/mingw-w64-crt/lib32/msvcr40d.def.in
index 5f5f75409..aced5b3c5 100644
--- a/mingw-w64-crt/lib32/msvcr40d.def.in
+++ b/mingw-w64-crt/lib32/msvcr40d.def.in
@@ -1081,7 +1081,7 @@ _adj_fprem1
_adj_fptan
_adjust_fdiv DATA
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atoldbl
_beep
diff --git a/mingw-w64-crt/lib32/msvcr70.def.in
b/mingw-w64-crt/lib32/msvcr70.def.in
index 29a8aa95e..53e2bfe2d 100644
--- a/mingw-w64-crt/lib32/msvcr70.def.in
+++ b/mingw-w64-crt/lib32/msvcr70.def.in
@@ -258,7 +258,7 @@ _aligned_offset_malloc
_aligned_offset_realloc
_aligned_realloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atoi64
_atoldbl
diff --git a/mingw-w64-crt/lib32/msvcr70d.def.in
b/mingw-w64-crt/lib32/msvcr70d.def.in
index 3bbf5c542..7784d76f2 100644
--- a/mingw-w64-crt/lib32/msvcr70d.def.in
+++ b/mingw-w64-crt/lib32/msvcr70d.def.in
@@ -292,7 +292,7 @@ _aligned_offset_realloc_dbg
_aligned_realloc
_aligned_realloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atoi64
_atoldbl
diff --git a/mingw-w64-crt/lib32/msvcr71.def.in
b/mingw-w64-crt/lib32/msvcr71.def.in
index 6248aa604..aa33cd3d0 100644
--- a/mingw-w64-crt/lib32/msvcr71.def.in
+++ b/mingw-w64-crt/lib32/msvcr71.def.in
@@ -251,7 +251,7 @@ _aligned_offset_malloc
_aligned_offset_realloc
_aligned_realloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atoi64
_atoldbl
diff --git a/mingw-w64-crt/lib32/msvcr71d.def.in
b/mingw-w64-crt/lib32/msvcr71d.def.in
index 71fee41ac..a317d1ef2 100644
--- a/mingw-w64-crt/lib32/msvcr71d.def.in
+++ b/mingw-w64-crt/lib32/msvcr71d.def.in
@@ -285,7 +285,7 @@ _aligned_offset_realloc_dbg
_aligned_realloc
_aligned_realloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atoi64
_atoldbl
diff --git a/mingw-w64-crt/lib32/msvcr80.def.in
b/mingw-w64-crt/lib32/msvcr80.def.in
index d84908cc5..7d579983e 100644
--- a/mingw-w64-crt/lib32/msvcr80.def.in
+++ b/mingw-w64-crt/lib32/msvcr80.def.in
@@ -355,7 +355,7 @@ _aligned_offset_malloc
_aligned_offset_realloc
_aligned_realloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/msvcr80d.def.in
b/mingw-w64-crt/lib32/msvcr80d.def.in
index 30302a197..d4deb170d 100644
--- a/mingw-w64-crt/lib32/msvcr80d.def.in
+++ b/mingw-w64-crt/lib32/msvcr80d.def.in
@@ -416,7 +416,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/msvcr90.def.in
b/mingw-w64-crt/lib32/msvcr90.def.in
index 5c3d6a48c..a6f575212 100644
--- a/mingw-w64-crt/lib32/msvcr90.def.in
+++ b/mingw-w64-crt/lib32/msvcr90.def.in
@@ -355,7 +355,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/msvcr90d.def.in
b/mingw-w64-crt/lib32/msvcr90d.def.in
index 294cb57f8..eb4f68ec0 100644
--- a/mingw-w64-crt/lib32/msvcr90d.def.in
+++ b/mingw-w64-crt/lib32/msvcr90d.def.in
@@ -406,7 +406,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib32/msvcrt10.def.in
b/mingw-w64-crt/lib32/msvcrt10.def.in
index ab6773406..8a7d40842 100644
--- a/mingw-w64-crt/lib32/msvcrt10.def.in
+++ b/mingw-w64-crt/lib32/msvcrt10.def.in
@@ -902,7 +902,7 @@ _access
_acmdln DATA
_aexit_rtn DATA
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
__msvcrt_beginthread == _beginthread ; rename _beginthread as it does not
handle SSE floating point exceptions, real _beginthread provided by emu
_c_exit
_cabs
diff --git a/mingw-w64-crt/lib32/msvcrt20.def.in
b/mingw-w64-crt/lib32/msvcrt20.def.in
index 0b13fc424..8a87561df 100644
--- a/mingw-w64-crt/lib32/msvcrt20.def.in
+++ b/mingw-w64-crt/lib32/msvcrt20.def.in
@@ -937,7 +937,7 @@ __msvcrt20_wgetmainargs == __wgetmainargs ; msvcrt20.dll's
__wgetmainargs is inc
_abnormal_termination
_access
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atoldbl
_beep
diff --git a/mingw-w64-crt/lib32/msvcrt40.def.in
b/mingw-w64-crt/lib32/msvcrt40.def.in
index 95654f5f8..4c698c153 100644
--- a/mingw-w64-crt/lib32/msvcrt40.def.in
+++ b/mingw-w64-crt/lib32/msvcrt40.def.in
@@ -1055,7 +1055,7 @@ _adj_fprem1
_adj_fptan
_adjust_fdiv DATA
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atoldbl
_beep
diff --git a/mingw-w64-crt/lib32/msvcrtd.def.in
b/mingw-w64-crt/lib32/msvcrtd.def.in
index f7d5dce9f..ea0c17c3c 100644
--- a/mingw-w64-crt/lib32/msvcrtd.def.in
+++ b/mingw-w64-crt/lib32/msvcrtd.def.in
@@ -237,7 +237,7 @@ _adj_fptan
_adjust_fdiv DATA
_aexit_rtn DATA
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atoi64
_atoldbl
diff --git a/mingw-w64-crt/lib64/msvcr100.def.in
b/mingw-w64-crt/lib64/msvcr100.def.in
index 7e70caa86..2e7991c6d 100644
--- a/mingw-w64-crt/lib64/msvcr100.def.in
+++ b/mingw-w64-crt/lib64/msvcr100.def.in
@@ -687,7 +687,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib64/msvcr100d.def.in
b/mingw-w64-crt/lib64/msvcr100d.def.in
index 65ed8c2ca..26c568dc4 100644
--- a/mingw-w64-crt/lib64/msvcr100d.def.in
+++ b/mingw-w64-crt/lib64/msvcr100d.def.in
@@ -732,7 +732,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib64/msvcr110.def.in
b/mingw-w64-crt/lib64/msvcr110.def.in
index aea6fabe6..83362bce9 100644
--- a/mingw-w64-crt/lib64/msvcr110.def.in
+++ b/mingw-w64-crt/lib64/msvcr110.def.in
@@ -813,7 +813,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib64/msvcr110d.def.in
b/mingw-w64-crt/lib64/msvcr110d.def.in
index ee51330b8..621768782 100644
--- a/mingw-w64-crt/lib64/msvcr110d.def.in
+++ b/mingw-w64-crt/lib64/msvcr110d.def.in
@@ -858,7 +858,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib64/msvcr120.def.in
b/mingw-w64-crt/lib64/msvcr120.def.in
index 6442bb755..9095011ac 100644
--- a/mingw-w64-crt/lib64/msvcr120.def.in
+++ b/mingw-w64-crt/lib64/msvcr120.def.in
@@ -825,7 +825,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib64/msvcr120d.def.in
b/mingw-w64-crt/lib64/msvcr120d.def.in
index 51156e5d7..31c325805 100644
--- a/mingw-w64-crt/lib64/msvcr120d.def.in
+++ b/mingw-w64-crt/lib64/msvcr120d.def.in
@@ -870,7 +870,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib64/msvcr80.def.in
b/mingw-w64-crt/lib64/msvcr80.def.in
index db1710d34..f81f10e29 100644
--- a/mingw-w64-crt/lib64/msvcr80.def.in
+++ b/mingw-w64-crt/lib64/msvcr80.def.in
@@ -298,7 +298,7 @@ _aligned_offset_malloc
_aligned_offset_realloc
_aligned_realloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib64/msvcr80d.def.in
b/mingw-w64-crt/lib64/msvcr80d.def.in
index a28217c94..3fbac9478 100644
--- a/mingw-w64-crt/lib64/msvcr80d.def.in
+++ b/mingw-w64-crt/lib64/msvcr80d.def.in
@@ -353,7 +353,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib64/msvcr90.def.in
b/mingw-w64-crt/lib64/msvcr90.def.in
index 1bc30455d..3efea2608 100644
--- a/mingw-w64-crt/lib64/msvcr90.def.in
+++ b/mingw-w64-crt/lib64/msvcr90.def.in
@@ -302,7 +302,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/lib64/msvcr90d.def.in
b/mingw-w64-crt/lib64/msvcr90d.def.in
index 864e0e10d..c95a741a8 100644
--- a/mingw-w64-crt/lib64/msvcr90d.def.in
+++ b/mingw-w64-crt/lib64/msvcr90d.def.in
@@ -347,7 +347,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/libarm32/msvcr110.def.in
b/mingw-w64-crt/libarm32/msvcr110.def.in
index d70854050..3c067e002 100644
--- a/mingw-w64-crt/libarm32/msvcr110.def.in
+++ b/mingw-w64-crt/libarm32/msvcr110.def.in
@@ -807,7 +807,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/libarm32/msvcr110d.def.in
b/mingw-w64-crt/libarm32/msvcr110d.def.in
index 80a3e3029..7164dbb88 100644
--- a/mingw-w64-crt/libarm32/msvcr110d.def.in
+++ b/mingw-w64-crt/libarm32/msvcr110d.def.in
@@ -852,7 +852,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/libarm32/msvcr120.def.in
b/mingw-w64-crt/libarm32/msvcr120.def.in
index 3a2f9267e..8e3b65f26 100644
--- a/mingw-w64-crt/libarm32/msvcr120.def.in
+++ b/mingw-w64-crt/libarm32/msvcr120.def.in
@@ -799,7 +799,7 @@ _aligned_offset_recalloc
_aligned_realloc
_aligned_recalloc
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/libarm32/msvcr120d.def.in
b/mingw-w64-crt/libarm32/msvcr120d.def.in
index 4380106fa..ef278913e 100644
--- a/mingw-w64-crt/libarm32/msvcr120d.def.in
+++ b/mingw-w64-crt/libarm32/msvcr120d.def.in
@@ -844,7 +844,7 @@ _aligned_realloc_dbg
_aligned_recalloc
_aligned_recalloc_dbg
_amsg_exit
-__msvcrt_assert DATA == _assert ; mingw-w64 provides _assert() function as
wrapper around renamed __msvcrt_assert symbol
+_assert
_atodbl
_atodbl_l
_atof_l
diff --git a/mingw-w64-crt/misc/_assert.c b/mingw-w64-crt/misc/_assert.c
deleted file mode 100644
index c8c854820..000000000
--- a/mingw-w64-crt/misc/_assert.c
+++ /dev/null
@@ -1,46 +0,0 @@
-/**
- * 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 <assert.h>
-#include <fcntl.h>
-#include <io.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-/* This is import symbol name for "_assert" from CRT DLL library */
-extern void (__cdecl *__MINGW_IMP_SYMBOL(__msvcrt_assert))(const char
*message, const char *file, unsigned line);
-
-/* Turn off _O_WTEXT, _O_U16TEXT or _O_U8TEXT mode on stderr stream
- * by changing mode to _O_TEXT, because fprintf (called by __msvcrt_assert)
- * does not work (and does nothing) on FILE* stream in some of those modes.
- * Only fwprintf works with those modes, but _assert uses fprintf.
- * Before changing the FILE* stream mode, it is required to flush buffers. */
-void __cdecl _assert(const char *message, const char *file, unsigned line)
-{
- /* stderr expands to function call */
- FILE *stream = stderr;
- /* Cache fd used by `stderr` */
- int fd = _fileno (stream);
- /* We need to restore previous mode in case `_assert` returns; it can happen
- * if program has called _set_error_mode(_OUT_TO_MSGBOX) and user pressed
- * "Ignore" button in popped up message box. */
- int oldmode;
-
- /* Change `stderr` mode to `_O_TEXT` */
- fflush(stream);
- oldmode = _setmode(fd, _O_TEXT);
-
- /* Call CRT `_assert` */
- __MINGW_IMP_SYMBOL(__msvcrt_assert)(message, file, line);
-
- /* Restore `stderr` mode to `oldmode` */
- fflush (stream);
- if (_setmode (fd, oldmode) != _O_TEXT) {
- abort ();
- }
-}
-
-void (__cdecl *__MINGW_IMP_SYMBOL(_assert))(const char *message, const char
*file, unsigned line) = _assert;
diff --git a/mingw-w64-headers/crt/assert.h b/mingw-w64-headers/crt/assert.h
index 99c127e4f..ad522f667 100644
--- a/mingw-w64-headers/crt/assert.h
+++ b/mingw-w64-headers/crt/assert.h
@@ -21,8 +21,8 @@
extern "C" {
#endif
+_CRTIMP void __cdecl _assert(const char *_Message,const char *_File,unsigned
_Line);
_CRTIMP void __cdecl _wassert(const wchar_t *_Message,const wchar_t
*_File,unsigned _Line);
-void __cdecl _assert (const char *_Message, const char *_File, unsigned _Line);
#ifdef __cplusplus
}
--
2.51.0.windows.1
From 17cc1d34a1b3dea9b11f1d4f62b3fd35f9fb3553 Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 22 Jun 2026 18:59:43 +0900
Subject: [PATCH 3/4] crt: improve _wassert() emulation
Previous implementaion used `malloc` to allocate memory for converted string,
falling back to static buffer if memory allocation fails. This approach was
reasonable assuming `_wassert` cannot return and always terminates the process.
There are two cases when `_wassert` does not terminate the process:
A. Application sets signal handler for SIGABRT which calls `longjmp`;
`abort` calls this handler, which gives application an opportunity to escape.
B. Application calls `_set_error_mode (_OUT_TO_MSGBOX)` and user presses
"Ingore" button in popped up message box; in this case `_wassert` returns
normally.
Case A makes it impossible to free allocated memory, and so dynamic memory
allocation in `_wassert` must be avoided. Using fallback to static buffer makes
`_wassert` thread-unsafe, which may be a serious issue in case B.
Instead, use `_alloca` to allocate buffers on stack; add an arbitrary size
limit for buffers to minimize possibility of stack overflow.
Previous implementaion used `wcstombs` to convert its wide string arguments to
narrow strings, which are then passed to `_assert`. Using `wcstombs` has a few
issues:
1. It uses code page used by active CRT locale; this may be an issue when assert
message is displayed in message box, which assumes string to be encoded using
active ANSI code page.
2. In case when entire string cannot be converted to target code page, it does
not allow to convert only part of the string.
To make `_wassert` more reliable on older systems, use `WideCharToMultiByte` to
perform conversion; it allows to replace characters which cannot be converted
with '?', while preserving the rest of information.
Signed-off-by: Kirill Makurin <[email protected]>
---
mingw-w64-crt/misc/wassert.c | 201 +++++++++++++++++++++++++++++++----
1 file changed, 178 insertions(+), 23 deletions(-)
diff --git a/mingw-w64-crt/misc/wassert.c b/mingw-w64-crt/misc/wassert.c
index 11b08cfd3..26ff7a0a9 100644
--- a/mingw-w64-crt/misc/wassert.c
+++ b/mingw-w64-crt/misc/wassert.c
@@ -5,38 +5,193 @@
*/
#include <assert.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
+#include <wchar.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+/**
+ * Convert wide character string `wcs` to code page `cp`.
+ *
+ * The converted string is written to `buffer` with size `buffer_size`.
+ *
+ * If `buffer_size` is less than buffer size required to store converted `wcs`,
+ * set `truncated` to `true`; in this case, this function will convert as much
+ * wide characters in `wcs` as fits into `buffer`.
+ */
+static void conv (char *buffer, int buffer_size, const wchar_t *wcs, unsigned
cp, bool truncated) {
+ if (!truncated) {
+ /**
+ * Calling `WideCharToMultiByte` with zero flags allows best-fit
+ * conversion and makes it replace characters which cannot be converted
+ * to `cp` with '?'.
+ *
+ * This allows us to preserve and display as much information as
possible.
+ */
+ int written = WideCharToMultiByte (cp, 0, wcs, -1, buffer,
buffer_size, NULL, NULL);
+
+ if (written == 0) {
+ buffer[0] = '\0';
+ }
+
+ return;
+ }
+
+ /**
+ * `WideCharToMultiByte` fails with `ERROR_INSUFFICIENT_BUFFER` when called
+ * with insufficient buffer size. We have to convert wide characters in
+ * `wcs` one-by-one.
+ */
+
+ /**
+ * If `wcs` is being truncated, reserve space in `buffer` to add "...".
+ */
+ buffer_size -= 4;
+
+ /**
+ * Total number of bytes written to `buffer`.
+ */
+ int written = 0;
+
+ while (1) {
+ if (wcs[0] == L'\0') {
+ break;
+ }
+
+ /**
+ * Buffer to store next converted character from `wcs`.
+ */
+ char buf[MB_LEN_MAX] = {'?', '\0', '\0', '\0', '\0'};
+
+ int wcLength = 1;
+ int mbLength = 1;
+
+ if (IS_LOW_SURROGATE (wcs[0])) {
+ goto invalid_unicode;
+ }
+
+ if (IS_HIGH_SURROGATE (wcs[0])) {
+ if (!IS_LOW_SURROGATE (wcs[1])) {
+ goto invalid_unicode;
+ }
+
+ wcLength = 2;
+ }
+
+ mbLength = WideCharToMultiByte (cp, 0, wcs, wcLength, buf, MB_LEN_MAX,
NULL, NULL);
+
+ if (mbLength == 0) {
+ break;
+ }
+
+ if (written + mbLength > buffer_size) {
+ break;
+ }
+
+invalid_unicode:
+ memcpy (buffer, buf, mbLength);
+
+ buffer += mbLength;
+ written += mbLength;
+ wcs += wcLength;
+
+ if (written == buffer_size) {
+ break;
+ }
+ }
+
+ /**
+ * Append "..." to the end of `buffer`.
+ */
+ memcpy (buffer, (const char[]) {'.', '.', '.', '\0'}, 4);
+}
/* _wassert is not available on XP, so forward it to _assert if needed */
static void __cdecl emu__wassert(const wchar_t *_Message, const wchar_t
*_File, unsigned _Line)
{
- static char static_message_buf[128]; /* thread unsafe */
- static char static_file_buf[_MAX_PATH]; /* thread unsafe */
- char *message = NULL, *file = NULL;
- size_t len;
-
- if ((len = wcstombs(NULL, _Message, 0)) != (size_t)-1)
- {
- message = malloc(len + 1);
- if (!message)
- {
- message = static_message_buf;
- len = sizeof(static_message_buf) - 2; /* -1 to not touch the last
nul byte */
- }
- wcstombs(message, _Message, len + 1);
+ /**
+ * _assert() prints `_Message` to stderr or displays a message box,
+ * depending on `_set_error_mode` setting.
+ *
+ * When printing message to stderr, we want to convert `_Message` to
+ * code page used by active CRT locale.
+ *
+ * When displaying a message box, we want to convert `_Message` to active
+ * ANSI code page; most ANSI APIs interpret strings using that code page.
+ *
+ * Perfectly, we would call `_set_error_mode (_REPORT_ERRMODE)` to query
+ * current setting and decide which code page to use; we cannot do this
+ * because `_set_error_mode` is not available in crtdll.dll, msvcrt10.dll
+ * and msvcrt20.dll.
+ *
+ * Use active ANSI code page as the conservative option.
+ */
+ unsigned cp = GetACP ();
+
+ /**
+ * After _assert() displays the message, it calls abort() to terminate
+ * the process, which makes it possible to escape from _assert() by
+ * setting signal handler for SIGABRT which calls longjmp().
+ *
+ * As the result, we should avoid dynamic memory allocations to avoid
+ * possible memory leaks.
+ *
+ * Also, since msvcrt40.dll, _assert() can return if application has
+ * called `_set_error_mode (_OUT_TO_MSGBOX)` and user pressed "Ignore"
+ * button in the popped up message box.
+ *
+ * As the result, we should avoid using static buffer to avoid race
+ * condition when multiple threads call _wassert() at the same time.
+ */
+ char *message = NULL;
+ char *file = NULL;
+
+ /**
+ * We use `_alloca` to allocate buffers to hold converted `_Message` and
+ * `_File`. In order to reduce possibility of stack overflow, we limit
+ * allocation size for each buffer.
+ *
+ * In case when buffer size is not enough to store full converted string,
+ * we will truncate the string.
+ */
+ bool truncate_message = false;
+ bool truncate_file = false;
+
+ /**
+ * Call `WideCharToMultiByte` to calculate buffer size required to hold
+ * converted `_Message` and `_File`.
+ *
+ * On failure, it returns zero; we must be careful not to allocate
+ * zero-length buffer.
+ */
+ int message_length = WideCharToMultiByte (cp, 0, _Message, -1, NULL, 0,
NULL, NULL);
+ int file_length = WideCharToMultiByte (cp, 0, _File, -1, NULL, 0, NULL,
NULL);
+
+ if (message_length == 0) {
+ message_length = 1;
+ } else if (message_length > BUFSIZ) {
+ truncate_message = true;
+ message_length = BUFSIZ;
}
- if ((len = wcstombs(NULL, _File, 0)) != (size_t)-1)
- {
- file = malloc(len + 1);
- if (!file)
- {
- file = static_file_buf;
- len = sizeof(static_file_buf) - 2; /* -1 to not touch the last nul
byte */
- }
- wcstombs(file, _File, len + 1);
+ if (file_length == 0) {
+ file_length = 1;
+ } else if (file_length > FILENAME_MAX) {
+ truncate_file = true;
+ file_length = FILENAME_MAX;
}
+ message = _alloca (message_length);
+ file = _alloca (file_length);
+
+ conv (message, message_length, _Message, cp, truncate_message);
+ conv (file, file_length, _File, cp, truncate_file);
+
_assert(message, file, _Line);
}
--
2.51.0.windows.1
From 3fb6aa08f1cdc6a62c4a553391220c6740c0cf69 Mon Sep 17 00:00:00 2001
From: Kirill Makurin <[email protected]>
Date: Mon, 22 Jun 2026 18:59:43 +0900
Subject: [PATCH 4/4] crt: tests: update t_assert
Wait until the child process termintaes before reading from the pipe;
this allows to read complete output produces by the child process.
Print data read from the pipe to stdout; this allows to examine assertion
message printed by the child process.
Signed-off-by: Kirill Makurin <[email protected]>
---
mingw-w64-crt/testcases/t_assert.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/mingw-w64-crt/testcases/t_assert.c
b/mingw-w64-crt/testcases/t_assert.c
index e66c406e8..6b603ad35 100644
--- a/mingw-w64-crt/testcases/t_assert.c
+++ b/mingw-w64-crt/testcases/t_assert.c
@@ -35,14 +35,15 @@ int main(int argc, char *argv[]) {
assert(process != -1);
+ /* wait until child process exits */
+ assert(_cwait(&exit_code, process, _WAIT_CHILD) == process);
+ assert(exit_code != 0);
+
size = read(pipefd[0], buf, sizeof(buf));
close(pipefd[0]);
assert(size > 0); /* some data were written by child process */
assert(strnlen(buf, sizeof(buf)) > 0);
-
- /* wait until child process exits */
- assert(_cwait(&exit_code, process, _WAIT_CHILD) == process);
- assert(exit_code != 0);
+ write (STDOUT_FILENO, buf, size);
return 0;
}
--
2.51.0.windows.1
_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public