On Sunday 21 June 2026 06:13:42 Kirill Makurin wrote:
> Pali Rohár <[email protected]> wrote:
> 
> > Hello,
> >
> > I looked at these issues. And basically there are 3 things which could
> > be done independently.
> >
> > 1. Decide whether C assert() macro would respect _UNICODE and use CRT
> > _assert() or CRT _wassert() function. Or if it would not respect
> > _UNICODE and always forces usage of UTF-16 _wassert() variant.
> >
> > 2. Fixing of mingw-w64 _assert() function. Or removal of mingw-w64
> > _assert() implementation and let to use just broken DLL implementation.
> >
> > 3. Changing/Fixing of the mingw-w64 _wassert() emulation.
> >
> > About application usage. I think it is not important if the recent MS
> > doc say that some function (e.g. _assert()) should not be used by
> > application or not. Application would always use functions which are
> > available and are part of CRT DLL ABI. Saying "do not use" or forcing by
> > "removal of symbol" would just cause breaking.
> 
> Both `_assert` and `_wassert` are documented, so removing them is totally out 
> of question. OTOH, I still would suggest applications to not call them 
> directly. For example, MSVC no longer exposes `_assert`, so any application 
> which references it will not be able to compile with MSVC at this point.
> 
> > Also I think that it is not important of usage of function, macro,
> > parameter flag, enum, whatever... is going to be forbidden in header
> > file or not. Lot of times I saw that applications are doing thing like
> > "#ifndef FLAG\n#define FLAG ...\n#endif" with comment that some older
> > SDK or broken SDK do not provide FLAG. And personally I also did it lot
> > of times when I figured out that FLAG was not exported.
> >
> > So the "Expose _O_{W,U16,U8}TEXT macros only for CRTs..." part is not a
> > good idea as described above. At least this is what I think.
> 
> IMO, if an application compiled against pre-msvcr80.dll calls
> 
>     _setmode (fd, _O_U8TEXT)
> 
> It is a clearly bug in an application. It attempts to use a feature which is 
> not supported by that CRT; it will compile but will behave differently than 
> expected.

For msvcrt.dll (Visual C++ 6.0) this is not a bug. It is documented
behavior in the official documentation for _set_mode:
https://web.archive.org/web/20010418052903/http://msdn.microsoft.com/library/devprods/vs6/visualc/vccore/_crt__setmode.htm

"A return value of –1 indicates an error, in which case errno is set to
either EBADF, indicating an invalid file handle, or EINVAL, indicating
an invalid mode argument (neither _O_TEXT nor _O_BINARY)."

So application can call _setmode (fd, _O_U8TEXT) and check return value
(with errno) if the _O_U8TEXT is supported or not.

I used this technique lot of times (but not for _setmode).

> > Also important is that mingw-w64 header files are for C and C++
> > languages but there are usage also for other languages, which directly
> > uses mingw-w64-crt libraries. My feeling is that separation of
> > mingw-w64-crt and mingw-w64-headers is a good idea to show what is
> > provided for linker and what is provided for C/C++ language.
> 
> I would prefer if we could catch errors at compilation time whenever possible.
> 
> > Now about 1. forward C assert() macro to CRT _wassert() function. I have
> > subjective preference to not do it. I understand concerns and I right
> > now I do not see any good solution. My feeling is that both are bad.
> > I'm looking at this from direction: I explicitly did not set _UNICODE
> > so I want from my application (which includes mingw-w64 header files) to
> > not emit _UNICODE/UTF-16 symbols, for example for compatibility with
> > non-UNICODE systems. MS is for a longer time trying to convince
> > developers to use UNICODE/UTF-16 functions and entry points and the
> > change which switched assert to _wassert in MS header files was done
> > long time ago (when they completely dropped Win9x support). But now I
> > think that direction is moving from UTF-16 to UTF-8. I know this is not
> > very good argument, just my subjective preference. On the other hand,
> > issues which you pointed out are valid which should be solved. And if
> > there is no other option how to solve those issues then probably the
> > switch is the direction. But before that I would like to try fixing
> > other options.
> 
> IMO, Windows-only code must use Unicode whenever possible; the only reason 
> not to is Win9x systems which lack Unicode support.

Which is not the only case of mingw-w64 usage.

> If someone intends to support only Windows 10 and later, using UTF-8 is 
> probably the best solution. However, if they want to support pre-Windows 10 
> systems, they should use UTF-16; otherwise they will be limited to locale's 
> ANSI code pages.

That is truth. But for people from UNIX world can be UTF-16 a pain I
understand if the ANSI code page limitation is taken.

> > Also I do not like the fact that the assert() macro would be forwarded
> > to _wassert() function, which for older libs would be emulated by
> > mingw-w64 which at the end would call _assert() by doing reallocations
> > and conversions back and forward.
> 
> From user perspective, it does not really matter how `assert` is implemented; 
> it could be implemented as simple as macro which calls `fprintf`, `fflush` 
> and `abort`.
> 
> Microsoft docs[1] mention the following detail:
> 
> ```
> The _assert and _wassert functions are internal CRT functions. They help 
> minimize the code required in your object files to support assertions. We 
> don't recommend that you call these functions directly.
> ```
> 
> > About 2. removal vs fix of mingw-w64 _assert() function. If switch in 1.
> > would happen then I think there is no reason to remove _assert() and I
> > would like to try to fix it, not remove.
> 
> My proposal is to simply remove the wrapper around CRT `_assert` which we use 
> to handle Unicode translation modes.
> 
> > About 3. fixing of _wassert() emulation. This emulation always uses the
> > _assert() function for its implementation. Hence if the _assert stays
> > broken then the _wassert() emulation would be also always broken,
> > whatever would be done.
> 
> Basically, CRT has both `_wassert` and support for Unicode translation modes, 
> or neither. If we use `_wassert`, we do not have to worry about `_assert` not 
> working with Unicode translation modes. Otherwise, `_wassert` emulation 
> simply redirects to `_assert`, while doing its best at converting its 
> arguments.

This is a good point. The only problem is with msvcrt40.dll redirector
(which is since first NT 4.0 version) which does not have _wassert but
since some OS version it started supporting Unicode translation modes.
So the detection of _wassert cannot be done via msvcrt40.dll for
msvcrt40 builds, but rather more smarter. And I'm not sure if it is
possible to write it correctly in that smart way to correctly detect it.
And this detection could be too complicated.

> > So I would take 2. + 3. together and my ideas are:
> >
> > - _set_error_mode is not available in crtdll.dll, msvcrt10.dll and
> >   msvcrt20.dll CRT libs; but this is not a problem, I can provide
> >   working "emulation" of this function which would return correct value
> >   for _set_error_mode (_REPORT_ERRMODE) call. This is because these
> >   libraries always prints message to stderr via fprintf; not to GUI.
> 
> I wanted to suggest prividing emulation for `_set_error_mode ` as a 
> follow-up, so we could query current setting from `_wassert` emulation and 
> choose correct code page for conversion.

Ok.

> > - about cleaning resources if the _(w)assert returns back. I would
> >   propose to use SEH unwind handler to do it. I have already looked at
> >   SEH deeply and we can do it. Also longjmp _by default_ on ms builds is
> >   calling the unwind handler, so I think it would be a good idea to do
> >   it also in mingw-w64.
> 
> Sounds really overcomplicated to me.

With clang it is trivial, just put code into __try {...} __finally {...}
blocks. For gcc it would be needed to write manually. I will think about
it and try to write it, so see if it is complicated or easy to
implement.

> > - malloc vs static buffer vs _alloca. Usage of _alloca or variable
> >   length arrays are mostly means issues in code. And having them in
> >   "assert" implementation (which is called by application then something
> >   bad happens) sounds like a trouble. Specially if compiler generates
> >   code for stack overflow protection for any alloca/VLA usage and calls
> >   assert if overflow happens. We could have potentially new issues.
> >   I'm not sure if compilers are doing this now, but I can imagine that
> >   this is a valid thing to do.
> 
> IMO, using `alloca` is totally OK as long as it is used carefully. My 
> proposed changes limit allocation sizes to `BUFSIZ` (512) and `FILENAME_MAX` 
> (260), and even try to allocate smaller buffers when we can.

I was thinking about this and I have not come up with better idea. So I
agree with alloca usage. Buffer with sane upper limit is fine. The only
problem is that in this way the longer assert string would be truncated.
But I have no better idea how to handle this situation.

Maybe we if the truncation is going to happen, we can put the "..." at
the end of buffer to at least visually show that something was truncated
and "visually shows" that it was expected and has it purpose.

> >   But with static buffers I understand issue that multiple threads
> >   cannot at the same time call assert. And when starting to use signal
> >   handlers, then issue can happen even with one thread when signal
> >   handler calls assert with a new signal handler and both would want to
> >   longjump from it based on some condition. In this case the assert
> >   temporary buffer would be used just by one thread, but by two
> >   different callers intermixed. So even possible thread local buffer
> >   would not help here.
> >
> > - usage of GetACP() for codepage. It is wrong like usage of any other
> >   option. When printing to console, it is needed to use function
> >   GetConsoleOutputCP().
> 
> From what I know, CRT stdio functions, when write to terminals, convert 
> strings to code page returned by `GetConsoleOutputCP`. What we must keep in 
> mind is that it interprets original (narrow) strings using code page for 
> active CRT locale (`setlocale`).

Ok. This is important to know and maybe should put this information into
the mingw-w64 _wassert code. So anybody after us in future will read the
code, could understand reason why it is implemented in this way. Because
this codepage stuff is huge and easy to misuse and very easy to forgot
how it works.

> > - problem with encoding comes from the fact that _compiler_ fills the
> >   assert message in its own encoding. But at the same time the _assert
> >   function (provided by DLL) has some encoding contract (with bugs...)
> >   how it should be called. So I have feeling that here we are mixing
> >   more separate issues into one functionality and that is why it is in
> >   this bad shape. gcc has -finput-charset=charset and
> >   -fexec-charset=charset options (and maybe some other internal options)
> >   which if I understand correctly can affect the asserted message which
> >   is then expanded into _assert call. Should not we rather have separate
> >   mingw-w64 function e.g. _assert_called_from_gcc_option1 (or whatever)
> >   and then CRT _assert function which is according to the ABI?
> 
> Sounds like another overcomplication to me. Basically, all narrow CRT 
> functions have this issue, and this is why use of Unicode is preferred.

Now when I'm reading this again, I agree this is overcomplication.
Better not to do it.

> >   Because my feeling is that "assert" is a C macro mostly tied with
> >   compiler nad "_assert" is MS CRT function which follows some ABI and
> >   these two things needs some layer to be correctly connected. Here we
> >   are trying to change "_assert" which looks to be wrong. For example
> >   some other compiler/language can use "_assert" function directly if it
> >   is not C/C++.
> 
> IMO, they should not.
> 
> > About your last point C. We can check if the current symbol for e.g.
> > _assert points to msvcrt.dll or to some other library. And this check
> > can be in the per-CRT library. This should work correctly as forward are
> > done at PE loader level. And it will sets symbol to point to different
> > library. I think that if it is done in this way it should not have a
> > problem. The point is to not use GetProcAddress, but rather to figure
> > out HMODULE directly from __imp_<symbol> pointer at runtime. And I think
> > that in the same way could be implemented _set_error_mode (_REPORT_ERRMODE)
> > call to work reliable in case crtdll.dll/msvcrt20.dll (w)print functions
> > are redirected to msvcrt.dll.
> 
> Sounds like an option.
> 
> > I would like to try to implement alternative solution to these issues.
> > Maybe I will figure out that it is not possible, and your change is the
> > the best what can be done, even it is not ideal. But that is one of
> > scenarios.
> >
> > If I understand correctly, right now in the mingw-w64 git code we have
> > memory leaks in assert and wrong encoding of non-ASCII assert messages
> > and damaging of _O_WTEXT/_O_U16TEXT/_O_U8TEXT mode in case the assert is
> > ignored and is going to continue. IMHO all those cases are not the
> > primary code path, was there a longer time, so should not cause an issue
> > if these issues would be fixed/merged later and not immediately.
> 
> - Kirill Makurin
> 
> [1] 
> https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/assert-macro-assert-wassert


_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to