On Thursday 02 January 2025 00:17:06 Martin Storsjö wrote:
> 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.

Exactly, there is no msvcr60.dll or msvcr61.dll.

> > 
> > #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 see, I was wrong, based on that file which I found...

> 
> 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

So for completeness, I run peres -v on these system versions:

NT4.0    4.20.0.6201
Win98    5.0.0.7128
Win98SE  6.0.8397.0
WinMe    6.1.8637.0
Win2k    6.1.8637.0
Win2ksp3 6.1.9359.0
Win2ksp4 6.1.9844.0
WinXP    7.0.2600.0
WinXPsp1 7.0.2600.1106
WinXPsp2 7.0.2600.2180
WinXPsp3 7.0.2600.5512
WinXPx64 7.0.3790.1830

NT4 msvcrt.dll has same set of symbols as VC4.2 msvcrt version, Win98 FE
has same set as VC5.0SP1, and Win98 SE has same as as VC6.0. So it looks
like that version numbers from peres for NT4/Win98 matches the VC++
versions.

> 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.

That make sense. I checked in our msvcrt.def.in and _wstat64 symbol is in
Windows ME/2000 section.

> So the mythical __MSVCR61_DLL means Win2k.

Yes, that would make sense.

> > 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.

Probably yes. So the condition could be:

#if __MSVCRT_VERSION__ >= 0x600 && __MSVCRT_VERSION__ < 0x700

> > * 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.)

Ok. It was just a suggestion for completeness. As nobody complained
then it is not needed.

> > 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

I see. But if we change the default value again then it would be
necessary to updates also mingw-w64 headers with new check for system
msvcrt.dll value. Something what I wrote above (just first two cases).


_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to