On Wed, 1 Jan 2025, Pali Rohár wrote:
To continue my investigation, I finally found the documentation from
where are those 0x0601 values coming from. In mingw32 project is this:
https://sourceforge.net/p/mingw/mingw-org-wsl/ci/5.1-trunk/tree/mingwrt/msvcrt-xref/msvcrt.def.in
* Note that mapping of symbols is directed by __MSVCRT_VERSION__;
* for the MSVCRT.DLL versions shipped with the various versions of
* MS-Windows, this must be a 32-bit unsigned long value with the
* high order 16-bits set to zero, and the low order 16-bits set
* to the appropriate platform specific hexadecimal value:--
*
* NT4 0x0400UL
* Win98 0x0410UL
* Win98-SE 0x0412UL
* Win2K 0x0500UL
* WinXP 0x0501UL
* Vista 0x0600UL
* Win7 0x0601UL
While these version numbers, essentially NT version numbers, seem to have
been used here like this, I don't think this is how they were used for
__MSVCRT_VERSION__ in headers though.
In mingw32 file include/msvcrtver.h is this content:
/* When it is intended to link an application with any one of the
* MSVC version specific MSVCRxx.DLL libraries, rather than with the
* OS default MSVCRT.DLL, the particular substitute MSVCRxx.DLL may
* be specified as any one of the following...
*/
#define __MSVCR60_DLL 0x0600
#define __MSVCR61_DLL 0x0601
#define __MSVCR70_DLL 0x0700
#define __MSVCR71_DLL 0x0701
#define __MSVCR80_DLL 0x0800
#define __MSVCR90_DLL 0x0900
#define __MSVCR100_DLL 0x1000
#define __MSVCR110_DLL 0x1100
#define __MSVCR120_DLL 0x1200
Note that while the msvcr70, 71, etc ones map to real msvcrXX.dlls that
have been shipped, there's no msvcr60.dll or msvcr61.dll anywhere, AFAIK.
#ifndef __MSVCRT_VERSION__
/* This may be set, when the intent is to link with any of the above
* non-freely distributable MSVCRxx.DLL libraries, rather than with the
* pseudo-free MSVCRT.DLL provided as an OS component. High byte is the
* major version number, low byte is the minor; however, users are advised
* to use custom GCC specs files to set this, while also substituting the
* appropriate library in place of MSVCRT.DLL, rather than to simply set
* it directly.
*
* It should be noted that __MSVCRT_VERSION__ is NOT a good indicator of
* evolving MSVCRT.DLL features; that is better accomplished by using the
* NTDDI_VERSION setting from the Windows API. Thus, users of MSVCRT.DLL
* should NOT set __MSVCRT_VERSION__, leaving us to establish a default,
* equivalent to MSVCR60.DLL, which seems reasonably well aligned with
* the feature set of the earliest MSVCRT.DLL version we support.
*/
# define __MSVCRT_VERSION__ __MSVCR60_DLL
#endif
So it looks like that current mingw32 is setting 0x0600 as default value
for __MSVCRT_VERSION__.
Next, I looked into mingw-org-wsl git repo history and I found out that
in more released mingw32 versions following patterns in header files:
/* These require msvcr70.dll or higher. */
#if __MSVCRT_VERSION__ >= 0x0700
_CRTIMP void * __cdecl __MINGW_NOTHROW _aligned_offset_malloc(size_t, size_t,
size_t);
_CRTIMP void * __cdecl __MINGW_NOTHROW _aligned_offset_realloc(void*, size_t,
size_t, size_t);
_CRTIMP void * __cdecl __MINGW_NOTHROW _aligned_offset_recalloc(void*,
size_t, size_t, size_t, size_t);
...
#endif
/* This require msvcr70.dll or higher. */
#if __MSVCRT_VERSION__ >= 0x0700
_CRTIMP int __cdecl _set_SSE2_enable (int);
#endif /* __MSVCRT_VERSION__ >= 0x0700 */
#if __MSVCRT_VERSION__ >= 0x0800
_CRTIMP int __cdecl __MINGW_NOTHROW _utime32 (const char*, struct
__utimbuf32*);
_CRTIMP int __cdecl __MINGW_NOTHROW _wutime32 (const wchar_t*, struct
__utimbuf32*);
_CRTIMP int __cdecl __MINGW_NOTHROW _futime32 (int, struct __utimbuf32*);
...
#endif
Also there are patterns:
#if __MSVCRT_VERSION__ >= 0x0601
_CRTIMP int __cdecl __MINGW_NOTHROW _wstat64 (const wchar_t*, struct
__stat64*);
#endif /* __MSVCRT_VERSION__ >= 0x0601 */
So it means that those "big" values 0x07000000UL were never used at all
in header file, and that values 0x0400UL-0x0601UL for system msvcrt.dll
are/were used.
Also in all older versions was __MSVCRT_VERSION__ by default set to the
0x0600 value.
With all information, it looks now clear why applications are checking
__MSVCRT_VERSION__ against 0x0601 value or are changing to higher value.
It means that they want Windows 7 APIs.
No, 0x0601 for __MSVCRT_VERSION__ doesn't mean Windows 7. I think you've
made way too much conclusions based on the info in msvcrt.def.in, which
does not seem to be using the same numbering scheme as the headers.
I believe the version numbering for __MSVCRT_VERSION__ in headers was
meant to use both the msvcrXX.dll version numbers, just like we do, but
also based on the actual embedded version number of the msvcrt.dll that
shipped with the OS.
I don't have a copy of msvcrt.dll from win9x or older versions handy for
looking at, but I have a set of the modern ones. Using "peres -v
msvcrt.dll" (the "peres" tool from the "pev" or "readpe" package on
Ubuntu), I'm getting the following version:
Win2k 6.1.8637.0
WinXP 7.0.2600.5512
Vista 7.0.6002.18551
Win7 7.0.7601.17744
This also lines up perfectly with some of the ifdefs you referenced - e.g.
_aligned_malloc exists in msvcrt.dll since XP, and is guarded by
__MSVCRT_VERSION__ >= 0x0700. And _wstat64 which is guarded by
__MSVCRT_VERSION__ >= 0x0601 is available since Win2k.
So the mythical __MSVCR61_DLL means Win2k.
So, based on this investigation, I think that changing default value of
__MSVCRT_VERSION__ again should not be needed. I would suggest to just
adjust checks in mingw-w64 header files for:
* OS system msvcrt.dll version (any OS):
- #if __MSVCRT_VERSION__ > 0x400 && __MSVCRT_VERSION__ < 0x700
- This will match any mingw32 OS system msvcrt.dll version except the Windows
NT4
- It does not conflict with neither mingw-w64 msvcrt40 (== 0x400) nor with
msvcr70 (== 0x700)
I don't think they used __MSVCRT_VERSION__ < 0x600 for anything, other
than potentially msvcrXX.dll versions just like we do.
* msvcr100+:
- (__MSVCRT_VERSION__ >= 0xA00 && __MSVCRT_VERSION__ < 0x1000) ||
(__MSVCRT_VERSION__ >= 0x1000)
- First part is for mingw-w64, second for mingw32
* msvcr110+:
- (__MSVCRT_VERSION__ >= 0xB00 && __MSVCRT_VERSION__ < 0x1000) ||
(__MSVCRT_VERSION__ >= 0x1100)
- First part is for mingw-w64, second for mingw32
* msvcr120+:
- (__MSVCRT_VERSION__ >= 0xC00 && __MSVCRT_VERSION__ < 0x1000) ||
(__MSVCRT_VERSION__ >= 0x1200)
- First part is for mingw-w64, second for mingw32
I don't think we should do anything to complicate things for numbered
msvcrXX.dll here - using anything other than msvcrt.dll and UCRT is
exceedingly rare. (I only practically know of two cases; you, and VLC did
need to target msvcr120*.dll for Windows Store distribution at some point,
and that's no longer relevant for them.)
Would be this enough to finally fix those issues with __MSVCRT_VERSION__?
I don't think this would make any difference here at all.
I believe that the issues that can be caused by our changes fall into two
classes: (Sorry, I didn't browse all the code search results to see what
other concrete cases there were.)
- Projects that look at __MSVCRT_VERSION__ and conditionally compile
things. This may lead to projects building with fewer features (by
skipping things that would require a newer msvcrt.dll), or trying to use
extra compat routines that wouldn't be necessary. Or there can be clashes,
if they see our __MSVCRT_VERSION__ and conclude that function X isn't
provided, and they try to provide their own replacement function X, and
they can get duplicate conflicting definitions.
Or the ifdefs are only tested in one configuration and simply break in the
other one, like I think was the case in the linked msys2 issue.
Addapting our header ifdefs to also recognize different values from
mingw.org toolchains won't make any difference here at all.
Most of the cases I saw in the code search I browsed earlier seemed to be
this case - e.g. trying to provide workarounds for 64 bit stat functions
or similar. I haven't tried compiling them to see if our new definitions
actually cause a problem or not.
- Projects that themselves try to change __MSVCRT_VERSION__ to expose more
features, e.g. setting __MSVCRT_VERSION__ to 0x700 to get XP level
features like _aligned_malloc available. This is mostly harmless for us,
even if it in corner cases could cause problems (by exposing msvcr70.dll
features when they actually meant msvcrt.dll/XP - but mingw.org headers
also seem to generally consider the two equal).
One such case is familiar to me,
https://github.com/FFmpeg/FFmpeg/blob/n7.1/configure#L6036-L6037. But that
one is only done if the headers are mingw.org headers, not mingw-w64, so
it doesn't make any difference for us. (It's used for getting specifically
_aligned_malloc.)
So I think the main source of problems for user code is that we used to
default to __MSVCRT_VERSION__ == 0x700 and now we default to something
else.
We could consider defaulting to 0x0601 (or 0x06FF); this would pick the
same branch as before, for all the cases of __MSVCRT_VERSION__ >= 0x0601.
So it could reduce the amount of breakage that we cause.
For any code that checks __MSVCRT_VERSION__ >= 0x700 though, things would
unfortunately change.
// Martin
_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public