In Visual C++ 2005, time is a wrapper for _time64 and time_t is, by
default, equivalent to __time64_t. If you need to force the compiler
to interpret time_t as the old 32-bit time_t, you can define
_USE_32BIT_TIME_T.
Regards,
Jan Nijtmans
Last edit: Earnie Boyd 2013-05-22
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Following on from earnie's comments on the bug reported against Tcl: much of the confusion here seems to derive from not respecting the distinction between the C runtime library and headers, and the platform (WIN32/WIN64) libraries and header.
If you are linking against MSVCRT.DLL (which is the C runtime) then you must compile against the corresponding headers (which don't understand and thus ignore _USE_32BIT_TIME_T). If you are compiling against headers for MSVCRT80 then you must link against MSVCRT80.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Here (See: mingw.patch) is my attempt to bring this issue forward. With this patch against today's git dev-4.0, Tcl 8.6 and earlier again compile fine without problems. I'm sure that it still contains problems, but for Tcl it works. The patch
expects _HAVE_32BIT_TIME_T to be set when linking against MSVCRT80.dll or higher, _HAVE_32BIT_TIME_T should not be defined when linking against MSVCRT.dll. You will probably want to replace that with checks like "MSVCRT_VERSION >= 800".
With this patch against today's git dev-4.0, Tcl 8.6 and earlier again compile fine without problems.
Did you test on XP, the executable?
The patch expects _HAVE_32BIT_TIME_T to be set when linking against MSVCRT80.dll or higher, _HAVE_32BIT_TIME_T should not be defined when linking against MSVCRT.dll. You will probably want to replace that with checks like "MSVCRT_VERSION >= 800".
I'm removing the _HAVE_32BIT_TIME_T in favor of MSVCRT_VERSION; that change is in my working area, yet to be committed.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Is this the same patch as you added to the TCL ticket? If so, I've already tested it on XP and it did not work. The only thing I received during testing were SEGV errors.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The issue is MSVCRT.DLL on XP does not contain the functions for _USE_32BIT_TIME_T. If _USE_32BIT_TIME_T is defined and one builds on a system whose MSVCRT.DLL supports the functions of _USE_32BIT_TIME_T and distributes that executable to someone using XP the XP user will have a sad experience. The issue with TCL is that the win/tclWinPort.h file sets _USE_32BIT_TIME_T explicitly. This causes the XP user to need to add -lmsvcr80 to build the application on XP while the Vista user could build TCL and execute it without -lmsvcr80 assuming the imports for the functions are added to libmsvcrt.a.
Eventually I hope to resolve the issue in a later version of the MinGW runtime.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
With my patch (mingw.patch, attached here):
$ nm tcl84.dll|grep I|grep time
6297a6a0 I __impftime
6297a6b0 I _imp_timezone
6297a6b4 I __imputime
6297a6bc I _imp_wutime
6297a714 I __imp__gmtime
6297a71c I __imp__localtime
6297a734 I __imp__mktime
629719e0 b _timeInfo
Tcl doesn't use any of the "stat" functions, but this is
what I expect for the "time"-related functions.
Now, doing exactly the same with unpatched wsl-4.0-rc1 headers:
$ nm tcl84.dll|grep I|grep time
6297a6a0 I __impftime64
6297a6a4 I _imp_gmtime64
6297a6b0 I __implocaltime64
6297a6b4 I _imp_mktime64
6297a6bc I __imptimezone
6297a6c0 I _imp_utime
6297a6c8 I __imp___wutime
629719e0 b _timeInfo
Which means that _USE_32BIT_TIME_T didn't do what it's
supposed to do. It's wrong. mktime64 didn't exist in
Win95/ME, so Tcl 8.4 wouldn't run there any more.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
_USE_32BIT_TIME_T is not supposed to do what you're expecting it to do. It's supposed to call MSVCRT80+ functions that use a 32-bit time_t (i.e. gmtime32), as opposed to MSVCRT80+ functions that use a 64-bit time_t (i.e. gmtime64). It's not supposed to call MSVCRT (6.x) functions (i.e. gmtime).
Defining _USE_32BIT_TIME_T doesn't magically allow your application to link against MSVCRT (6.x). It has nothing to do with MSVCRT 6.x. It's only understood by (the headers for) MSVCRT 8.x+ and will choose between two 8.x-specific behaviours (neither of which is backwards compatible with 6.x).
http://msdn.microsoft.com/en-us/library/0z9czt0w%28v=vs.80%29.aspx: "In Visual C++ 2005, gmtime is an inline function which evaluates to _gmtime64 and time_t is equivalent to __time64_t. If you need to force the compiler to interpret time_t as the old 32-bit time_t, you can define _USE_32BIT_TIME_T. Doing this will cause gmtime to be in-lined to _gmtime32. This is not recommended because your application may fail after January 18, 2038, and it is not allowed on 64-bit platforms."
This is implemented (in the MSVC headers) by include/time.inl.
To resolve this problem you HAVE to match the headers with the CRT you are linking against. That either means pointing at a different include path for the CRT, or telling the compiler (via a preprocessor define) which CRT you're linking against and modifying the headers to do the right thing.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Earnie: "The issue with TCL is that the win/tclWinPort.h file sets _USE_32BIT_TIME_T explicitly. This causes the XP user to need to add -lmsvcr80 to build the application on XP while the Vista user could build TCL and execute it without -lmsvcr80 assuming the imports for the functions are added to libmsvcrt.a."
No, the issue is that your compiler is supplying the headers for MSVCRT 8.x (or doing some trickery to achieve the equivalent), and linking against MSVCRT 6.x. The headers for MSVCRT 6.x don't understand _USE_32BIT_TIME_T and silently ignore it, so 'gmtime' resolves to the correct function in MSVCRT.DLL.
Looking at https://sourceforge.net/p/mingw/mingw-org-wsl/ci/master/tree/include/time.h#l159 I can see the wrong assumption that is being made on line 156: it is assumed that if _USE_32BIT_TIME_T is defined then you want to link against the xxx32() functions. If you were compiling with MSVC the distinction would be more nuanced: MSVC6 headers (for MSVCRT.DLL v6.x) don't understand _USE_32BIT_TIME_T and link against the xxx() functions; MSVC8 headers (for MSVCRT80.DLL v8.x+) link against xxx64() functions by default (if _USE_32BIT_TIME_T is not defined) or xxx32() functions is _USE_32BIT_TIME_T is defined.
The logic you want (in time.h) is:
#if (MSVCRT_VERSION >= 800) && defined(_USE_32BIT_TIME_T) && defined(_HAVE_32BIT_TIME_T)
// inline functions xxx() that call xxx32() in MSVCRT80+.DLL
#elif (MSVCRT_VERSION >= 800)
// inline functions xxx() that call xxx64() in MSVCRT80+.DLL
#else
// no special handling, functions xxx() are in MSVCRT.DLL
#endif
This of course assumes that the compiler is given the correct MSVCRT_VERSION for the lib you are going to link against.
Last edit: Keith Marshall 2013-05-23
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
The logic you want (in time.h) is:
...
Or, if you assume Windows XP with 64-bit time_t by default:
#ifndef _USE_32BIT_TIME_T// inline functions xxx() that call xxx64() in any MSVCRT??.DLL#elif (MSVCRT_VERSION >= 800)// inline functions xxx() that call xxx32() in MSVCRT80+.DLL#else// no special handling, functions xxx() are in MSVCRT.DLL#endif
Then, a user who requires 95/98/ME compatibility has two
options to get it:
1) define _USE_32BIT_TIME_T, which gives back a 32-bit time_t
2) set MSVCRT_VERSION to 800 and link with -lmsvcrt80
(and distribute msvcrt80.dll with the application).
That would work fine too, although it's not 100%
compatible with what Microsoft does. For Tcl this
would work fine.
Last edit: Keith Marshall 2013-05-23
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
These references to MSVCR80.DLL vs. MSVCRT.DLL v6.x aren't helpful. Neither are suggestions that users should rely on MSVCR80.DLL, and redistribute it. The bottom line is that MSVCR80.DLL, AFAIK, cannot be legitimately redistributed by anyone who lacks a paid for licence for a version of Visual Studio which grants that privilege; this is a non-free burden, which MinGW.org will never impose on its users.
MinGW GCC links, by default, against MSVCRT.DLL, (the system DLL distributed as standard with every MS-Windows operating system). End of story: this is not negotiable. The issue here is that different versions of MS-Windows distribute incompatible variants of MSVCRT.DLL. It is these incompatibilities for which Earnie is attempting to implement a satisfactory work around -- I hesitate to say solution. One thing is certain: "redistribute MSVCR80.DLL" is not, and likely never will be, any part of that work around.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Apologies for the markup; I have not yet familiarized myself with SF's updated tracker.
I'm not suggesting that you should redistribute MSVCR80.DLL if you don't want to; just that your headers must match your DLL.
If you are linking against MSVCRT.DLL, then your headers must correctly translate gmtime() and friends into gmtime(), and not into gmtime32(). The (unpatched) headers attempt to call gmtime32() if _USE_32BIT_TIME_T is set, and gmtime64() otherwise, which is consistent with what you must do to link against MSVCRT80.DLL. When the C source asks for 'gmtime' your headers must supply a definition that links against your chosen runtime library (right now they don't).
The logic I suggest works in all cases: by default it will resolve to names that link against MSVCRT.DLL; but if you choose to link against MSVCR80.DLL (and define MSVCRT_VERSION to 800 or higher) that will work too.
If you don't use this logic then all projects that can be built with both MinGW GCC and MSVC need to have special handling for _USE_32BIT_TIME_T when building with MinGW GCC (because it doesn't respect the rule that headers must match the DLL) -- this is why Jan views this as a MinGW GCC bug and not a Tcl bug.
With regards to the legality of redistributing MSVCR80+: Microsoft makes the redist installers available for free download (search for 'vcredist_x86.exe'). MSVCR110.DLL is redistributable with your application if you have a (free) license for MSVS 2012 Express (see http://msdn.microsoft.com/en-US/vstudio/hh857605). I understand the situation for earlier MSVCR*.DLL was less clear.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
@Jan: "Or, if you assume Windows XP with 64-bit time_t by default:"
What? This is not what the Microsoft C runtime headers do (which is what MinGW GCC should be trying to emulate, since they want to link against Microsoft's runtime DLL(s)).
Moreover there is no support for 64-bit time_t on XP unless you redistribute MSVCR80+. MSVCRT.DLL does not have xxx64() functions, so your proposed change means that every project WITHOUT _USE_32BIT_TIME_T won't link. That's a hostile approach.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Moreover there is no support for 64-bit time_t on XP unless you redistribute MSVCR80+. MSVCRT.DLL does not have xxx64() functions ...
Err ... no, this is not so. On XP:
pexportsmsvcrt.dll|greptime
shows me that, e.g., both _time64() and time() are available; it is _time32() which is lacking -- presumably time() itself is equivalent to the _time32() of later versions.
OTOH, on Win7, the same command shows me that all three of time(), _time32(), and _time64() are exported. Reading between the lines of MSDN, it seems likely that by the time, (no pun intended), that MSVCRT.DLL had evolved to the Win7 version, (with the change apparently occurring in Vista), time() has become a trampoline for _time64(), unless a compile time redirection to _time32() is mandated by the inclusion of a definition for _USE_32BIT_TIME_T.
If you are linking against MSVCRT.DLL, then your headers must correctly translate gmtime() and friends into gmtime(), and not into gmtime32(). The (unpatched) headers attempt to call gmtime32() if _USE_32BIT_TIME_T is set, and gmtime64() otherwise, which is consistent with what you must do to link against MSVCRT80.DLL
Again ... no, not entirely; it is also consistent with the requirement, AIUI, when linking against MSVCRT.DLL, on Win7 (or perhaps Vista) and later. You are correct in respect of XP, however. The issue is that the goal posts move, depending on which MS-Windows version the end user has.
... Jan views this as a MinGW GCC bug and not a Tcl bug.
It's likely that Earnie hasn't got it right yet, but I'd contend that it is actually a bug in both; it will just manifest differently for the two products, depending on which version of MS-Windows the end user happens to deploy on.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
It's likely that Earnie hasn't got it right yet, but I'd contend that it is actually a bug in both; it will just manifest differently for the two products, depending on which version of MS-Windows the end user happens to deploy on.
And of greater concern are those that build with MSVCRT.DLL supporting _USE_32BIT_TIME_T's *32 functions and try to distribute to XP. That is why I consider it a TCL issue; maybe not a bug but it will be a big surprise to many projects using TCL as a library of choice. SourceNavigator comes to mind.
I'll review the code to determine if I can do some trickery for the case of MSVCRT_VERSION < 800 and _USE_32BIT_TIME_T is defined. I think it will be beneficial to any using GCC but that will not be equivalent to what MSVC does but we're not anywhere near equivalent anyway. The best thing will be to create __mingwrt_* equivalents so that we don't have any surprises for those distributing software; maybe for 5.x series.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Let me rephrase this from the perspective of a developer using MSVC. The last MS compiler that links against MSVCRT.DLL by default is MSVC6. The C runtime headers shipped with MSVC6 have no reference to *time64() at all.
Windows XP shipped with MSVCRT.DLL ver 7.0, which includes the time64() functions. To access them you need to use headers in the platform SDK. All platform SDKs that support Windows XP provide time64() definitions. The last platform SDK that works with MSVC6 is Feb 2003 (Win2003 SP1); all SDKs up to and including that one always use a 32-bit time_t for a 32-bit compile (and a 64-bit time_t for a 64-bit compile). So if you wanted to use a 64-bit time_t in a 32-bit app then you used __time64_t and explicitly called *time64().
If you are building with a compiler later than MSVC6 then you will link against an MSVCRxx.DLL (xx > 70), and the C runtime headers shipped with the compiler expect that you are going to link against that DLL.
So if I as an MSVC-based developer compile a source tree for a 32-bit app with MSVC6 (so that it will run on any XP+ PC without me redistributing an MSVCRxx.DLL) then (1) when I declare a time_t it will be 32-bit; (2) when I call localtime() it will resolve to MSVCRT.DLL:localtime; and (3) it doesn't matter whether or not _USE_32BIT_TIME_T is defined in the source.
If I compile the same source tree as a 32-bit app with MSVC2005-2012 then (1) when I declare a time_t it will be 32-bit if _USE_32BIT_TIME_T is defined and 64-bit otherwise; (2) when I call localtime() it will resolve to MSVCRxx.DLL:localtimeyy() where yy is 32 if _USE_32BIT_TIME_T is defined and 64 otherwise; and (3) I must redistribute MSVCRxx.DLL.
So sources with _USE_32BIT_TIME_T defined will compile and link correctly with all versions of the MSVC compiler. Sources without _USE_32BIT_TIME_T may behave differently between MSVC6 and MSVC2005+.
So if you want to maintain source compatibility for 32-bit builds (I'm assuming that's the goal) then (A) if the source has _USE_32BIT_TIME_T then you must use a 32-bit time_t; (B) if you're linking against MSVCRT.DLL then you must call time() for a 32-bit time_t, or time64() for a 64-bit time_t -- ignore the fact that some versions of MSVCRT export time32(), these aren't prototyped in any MS header for MSVCRT.DLL (in either VC6 or the platform SDK) so they don't officially exist ; (C) if the source doesn't have _USE_32BIT_TIME_T then you need to decide on whether you're going to emulate MSVC6 or MSVC2005+ behaviour (the former assumes 32-bit time_t, the latter assumes 64-bit time_t); (D) if you want to allow the option to link against MSVCR80+ then (D.1) time_t should default to 64-bits in the absence of _USE_32BIT_TIME_T and (D.2) time() should resolve to *time32() for 32-bit time_t.
Point (B) above assumes that newer versions of MSVCRT.DLL have time() equivalent to time32() (rather than *time64() ). This must be the case if apps built with MSVC6 run on Windows 7.
Depending on your answer to (C), Jan's proposal (that I previously argued against) may be right, assuming similar preprocessor logic around the definition of time_t.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Earnie: "And of greater concern are those that build with MSVCRT.DLL supporting _USE_32BIT_TIME_T's *32 functions and try to distribute to XP. That is why I consider it a TCL issue"
Is there a disconnect between who chooses the headers and who chooses the library? That is, does GCC supply the C runtime headers, and Tcl's Makefile explicitly links against 'msvcrt.lib' or something like that? Then I can characterise this as a Tcl build problem.
But as I see it, Tcl calls e.g. localtime(). The C runtime headers offered up by MinGW GCC turn that into _localtime32(). Then GCC links against msvcrt.lib (which may not have a _localtime32). That's quite clearly a MinGW GCC issue.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
So if my code says "_USE_32BIT_TIME_T" it means "make time_t 32-bit, unless I'm linking against an old version of MSVCRT, in which case make it 64-bit" ?
If I declare a function "extern int My_GetTime(time_t *t);" then:
(1) without _USE_32BIT_TIME_T: MinGW gives me a 64-bit time_t; MSVC6 gives a 32-bit time_t; MSVC2005+ gives a 64-bit time_t.
(2) with _USE_32BIT_TIME_T: MinGW gives me a 64-bit time_t with MSVCRT.DLL or a 32-bit time_t with MSVCR80.DLL; MSVC6 gives a 32-bit time_t; MSVC2005+ gives a 32-bit time_t.
Were I to link a DLL built with MinGW into an app built with MSVC, or vice versa, there would be a type size mismatch and memory overwriting. There is no source level compatibility here.
The define "_USE_32BIT_TIME_T" means "make time_t 32-bit". If you can't do that then raise a preprocessor error. I should never get a 64-bit time_t if "_USE_32BIT_TIME_T" is defined. A side-effect of the define is that the headers must arrange for localtime(), gmtime() and friends to be mapped only library functions that handle a 32-bit time_t (irrespective of what they are called; _USE_32BIT_TIME_T does NOT mean 'call the xxx32()' functions, that's just a consequence of MS's function naming choices in MSVCR80.DLL).
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Twylite, thanks for the insightful feedback. I'm close to stating that our minimum supported version is XP and MSVC version 7.10. If I do that then what we have works. Else I need to do some better digging of old data type structures. Or maybe just do
Here is my patch (mingw2.patch), based on current wsl-4.0. It removes the warning
"Your MSVCRT_VERSION does not support the use of _USE_32BIT_TIME_T",
and makes the necessary changes such that msvcrt.dll (version 7.1
or later) support _USE_32BIT_TIME_T just fine. The explanation why this
works, can be found in this earlier in this ticket (Thanks, Twylite, for
your very clear comments). This patch assumes the VS2005+ behavior:
time_t is 64-bit, unless _USE_32BIT_TIME_T is specified. By default
it will work on XP or higher, when _USE_32BIT_TIME_T is specified it
should work on Windows 95/98/ME as well.
I don't see how _utime() can ever be a wrapper around _utime32() and work on a basic install of Windows XP with no added libraries. _utime() only needs to resolve to _utime32() if you are linking against MSVCRT80+.
Jan & Earnie: I think the logic we are looking for is the following. I've introduced a define _TIME_T_IS_32BIT to help clarify that _USE_32BIT_TIME_T only determines whether time_t is 32 or 64 bits; in the case of a 32-bit time_t the MSVCRT version determines whether you link against xxx() or xxx32(). (The define _TIME_T_IS_32BIT is redundant, it can be replaced by _USE_32BIT_TIME_T)
typedeflong__time32_t;// No real change from what exists in // https://sourceforge.net/p/mingw/mingw-org-wsl/ci/master/tree/include/time.h// This is a linking issue, not a type definition issue#ifdef_USE_32BIT_TIME_Ttypedef__time32_ttime_t;#define_TIME_T_IS_32BIT#elsetypedef__time64_ttime_t#define_TIME_T_IS_64BIT#endif// New approach to link to the right library function in sys/utime.h#ifdef_TIME_T_IS_32BIT#ifMSVCRT_VERSION<800// prototype utime()#else// prototype utime32(), and inline utime() -> utime32()#endif#else/* if _TIME_T_IS_64BIT */// prototype utime64(), and inline utime() -> utime64()#endif
Dealing specifically with sys/utime.h:
_CRTIMPint__cdecl__MINGW_NOTHROW_utime64(constchar*,struct__utimbuf64*);_CRTIMPint__cdecl__MINGW_NOTHROW_wutime64(constwchar_t*,struct__utimbuf64*);_CRTIMPint__cdecl__MINGW_NOTHROW_futime64(int,struct__utimbuf64*);#if MSVCRT_VERSION < 800_CRTIMPint__cdecl__MINGW_NOTHROW_utime(constchar*,struct__utimbuf*);_CRTIMPint__cdecl__MINGW_NOTHROW_wutime(constwchar_t*,struct__utimbuf*);_CRTIMPint__cdecl__MINGW_NOTHROW_futime(int,struct__utimbuf32*);#else_CRTIMPint__cdecl__MINGW_NOTHROW_utime32(constchar*,struct__utimbuf32*);_CRTIMPint__cdecl__MINGW_NOTHROW_wutime32(constwchar_t*,struct__utimbuf32*);_CRTIMPint__cdecl__MINGW_NOTHROW_futime32(int,struct__utimbuf32*);#endif#ifndef _USE_32BIT_TIME_T_CRTALIASint__cdecl__MINGW_NOTHROW_utime(constchar*_v1,struct_utimbuf*_v2){return(_utime64(_v1,(struct__utimbuf64*)_v2));}_CRTALIASint__cdecl__MINGW_NOTHROW_wutime(constwchar_t*_v1,struct_utimbuf*_v2){return(_wutime64(_v1,(struct__utimbuf64*)_v2));}_CRTALIASint__cdecl__MINGW_NOTHROW_futime(int_v1,struct_utimbuf*_v2){return(_futime64(_v1,(struct__utimbuf64*)_v2));}#elif MSVCRT_VERSION >= 800_CRTALIASint__cdecl__MINGW_NOTHROW_utime(constchar*_v1,struct_utimbuf*_v2){return(_utime32(_v1,(struct__utimbuf32*)_v2));}_CRTALIASint__cdecl__MINGW_NOTHROW_wutime(constwchar_t*_v1,struct_utimbuf*_v2){return(_wutime32(_v1,(struct__utimbuf32*)_v2));}_CRTALIASint__cdecl__MINGW_NOTHROW_futime(int_v1,struct_utimbuf*_v2){return(_futime32(_v1,(struct__utimbuf32*)_v2));}//#else = defined _USE_32BIT_TIME_T and MSVCRT_VERSION < 800//donothing:_utime()andfriendsarealreadyprototypedabove#endif
Earnie, as I understand it you can compile with _USE_32BIT_TIME_T defined using any version of MSVC (linking against any version of MSVCRT) and you will get a 32-bit time_t.
If you don't have _USE_32BIT_TIME_T defined then compilers pre-MSVC-2005 will give you a 32-bit time_t by default (and I'm not aware of any header that will give you a 64-bit time_t when compiling a 32-bit app); compilers MSVC-2005 and later will give you a 64-bit time_t by default. MinGW already defaults to 64-bit time_t, so it would make sense to follow the MSVC-2005 approach.
If you link against MSVCRT.DLL 7.0 or lower then there are no xxx32() functions, only xxx() and xxx64(). If you link against MSVCR80.DLL or higher then there are no xxx() functions, only xxx32() and xxx64(). You must specialise your prototypes and inline functions according to the target MSVCRT DLL, not the definition of time_t (which is determined only by _USE_32BIT_TIME_T irrespective of the target runtime DLL).
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I looked at your patch, for example:
I don't think that any mingw header should set _USE_32BIT_TIME_T.
See: http://msdn.microsoft.com/en-us/library/1f4c8f33%28v=vs.80%29.aspx
From this page:
Regards,
Jan Nijtmans
Last edit: Earnie Boyd 2013-05-22
I agree. I have already removed it from my working area.
Following on from earnie's comments on the bug reported against Tcl: much of the confusion here seems to derive from not respecting the distinction between the C runtime library and headers, and the platform (WIN32/WIN64) libraries and header.
If you are linking against MSVCRT.DLL (which is the C runtime) then you must compile against the corresponding headers (which don't understand and thus ignore _USE_32BIT_TIME_T). If you are compiling against headers for MSVCRT80 then you must link against MSVCRT80.
Here (See: mingw.patch) is my attempt to bring this issue forward. With this patch against today's git dev-4.0, Tcl 8.6 and earlier again compile fine without problems. I'm sure that it still contains problems, but for Tcl it works. The patch
expects _HAVE_32BIT_TIME_T to be set when linking against MSVCRT80.dll or higher, _HAVE_32BIT_TIME_T should not be defined when linking against MSVCRT.dll. You will probably want to replace that with checks like "MSVCRT_VERSION >= 800".
Regards,
Jan Nijtmans
Did you test on XP, the executable?
I'm removing the _HAVE_32BIT_TIME_T in favor of MSVCRT_VERSION; that change is in my working area, yet to be committed.
Is this the same patch as you added to the TCL ticket? If so, I've already tested it on XP and it did not work. The only thing I received during testing were SEGV errors.
Re: Twylite,
2nd attempt, 500 error with the first.
The issue is MSVCRT.DLL on XP does not contain the functions for _USE_32BIT_TIME_T. If _USE_32BIT_TIME_T is defined and one builds on a system whose MSVCRT.DLL supports the functions of _USE_32BIT_TIME_T and distributes that executable to someone using XP the XP user will have a sad experience. The issue with TCL is that the win/tclWinPort.h file sets _USE_32BIT_TIME_T explicitly. This causes the XP user to need to add -lmsvcr80 to build the application on XP while the Vista user could build TCL and execute it without -lmsvcr80 assuming the imports for the functions are added to libmsvcrt.a.
Eventually I hope to resolve the issue in a later version of the MinGW runtime.
No, it's not the same patch as attached to the Tcl issue. I only tested it on Windows 7, but it should not use Win7-specific functions.
But _USE_32BIT_TIME_T is Vista and above specific for MSVCRT.DLL. If you take your executable to XP and try to execute it, you will not be able to.
With my patch (mingw.patch, attached here):
$ nm tcl84.dll|grep I|grep time
6297a6a0 I __impftime
6297a6b0 I _imp_timezone
6297a6b4 I __imputime
6297a6bc I _imp_wutime
6297a714 I __imp__gmtime
6297a71c I __imp__localtime
6297a734 I __imp__mktime
629719e0 b _timeInfo
Tcl doesn't use any of the "stat" functions, but this is
what I expect for the "time"-related functions.
Now, doing exactly the same with unpatched wsl-4.0-rc1 headers:
$ nm tcl84.dll|grep I|grep time
6297a6a0 I __impftime64
6297a6a4 I _imp_gmtime64
6297a6b0 I __implocaltime64
6297a6b4 I _imp_mktime64
6297a6bc I __imptimezone
6297a6c0 I _imp_utime
6297a6c8 I __imp___wutime
629719e0 b _timeInfo
Which means that _USE_32BIT_TIME_T didn't do what it's
supposed to do. It's wrong. mktime64 didn't exist in
Win95/ME, so Tcl 8.4 wouldn't run there any more.
_USE_32BIT_TIME_T is not supposed to do what you're expecting it to do. It's supposed to call MSVCRT80+ functions that use a 32-bit time_t (i.e. gmtime32), as opposed to MSVCRT80+ functions that use a 64-bit time_t (i.e. gmtime64). It's not supposed to call MSVCRT (6.x) functions (i.e. gmtime).
Defining _USE_32BIT_TIME_T doesn't magically allow your application to link against MSVCRT (6.x). It has nothing to do with MSVCRT 6.x. It's only understood by (the headers for) MSVCRT 8.x+ and will choose between two 8.x-specific behaviours (neither of which is backwards compatible with 6.x).
http://msdn.microsoft.com/en-us/library/0z9czt0w%28v=vs.80%29.aspx: "In Visual C++ 2005, gmtime is an inline function which evaluates to _gmtime64 and time_t is equivalent to __time64_t. If you need to force the compiler to interpret time_t as the old 32-bit time_t, you can define _USE_32BIT_TIME_T. Doing this will cause gmtime to be in-lined to _gmtime32. This is not recommended because your application may fail after January 18, 2038, and it is not allowed on 64-bit platforms."
This is implemented (in the MSVC headers) by include/time.inl.
To resolve this problem you HAVE to match the headers with the CRT you are linking against. That either means pointing at a different include path for the CRT, or telling the compiler (via a preprocessor define) which CRT you're linking against and modifying the headers to do the right thing.
Earnie: "The issue with TCL is that the win/tclWinPort.h file sets _USE_32BIT_TIME_T explicitly. This causes the XP user to need to add -lmsvcr80 to build the application on XP while the Vista user could build TCL and execute it without -lmsvcr80 assuming the imports for the functions are added to libmsvcrt.a."
No, the issue is that your compiler is supplying the headers for MSVCRT 8.x (or doing some trickery to achieve the equivalent), and linking against MSVCRT 6.x. The headers for MSVCRT 6.x don't understand _USE_32BIT_TIME_T and silently ignore it, so 'gmtime' resolves to the correct function in MSVCRT.DLL.
Looking at https://sourceforge.net/p/mingw/mingw-org-wsl/ci/master/tree/include/time.h#l159 I can see the wrong assumption that is being made on line 156: it is assumed that if _USE_32BIT_TIME_T is defined then you want to link against the xxx32() functions. If you were compiling with MSVC the distinction would be more nuanced: MSVC6 headers (for MSVCRT.DLL v6.x) don't understand _USE_32BIT_TIME_T and link against the xxx() functions; MSVC8 headers (for MSVCRT80.DLL v8.x+) link against xxx64() functions by default (if _USE_32BIT_TIME_T is not defined) or xxx32() functions is _USE_32BIT_TIME_T is defined.
The logic you want (in time.h) is:
This of course assumes that the compiler is given the correct MSVCRT_VERSION for the lib you are going to link against.
Last edit: Keith Marshall 2013-05-23
Then, a user who requires 95/98/ME compatibility has two
options to get it:
1) define _USE_32BIT_TIME_T, which gives back a 32-bit time_t
2) set MSVCRT_VERSION to 800 and link with -lmsvcrt80
(and distribute msvcrt80.dll with the application).
That would work fine too, although it's not 100%
compatible with what Microsoft does. For Tcl this
would work fine.
Last edit: Keith Marshall 2013-05-23
Guys,
Please use correct markup, for inlined code.
These references to MSVCR80.DLL vs. MSVCRT.DLL v6.x aren't helpful. Neither are suggestions that users should rely on MSVCR80.DLL, and redistribute it. The bottom line is that MSVCR80.DLL, AFAIK, cannot be legitimately redistributed by anyone who lacks a paid for licence for a version of Visual Studio which grants that privilege; this is a non-free burden, which MinGW.org will never impose on its users.
MinGW GCC links, by default, against MSVCRT.DLL, (the system DLL distributed as standard with every MS-Windows operating system). End of story: this is not negotiable. The issue here is that different versions of MS-Windows distribute incompatible variants of MSVCRT.DLL. It is these incompatibilities for which Earnie is attempting to implement a satisfactory work around -- I hesitate to say solution. One thing is certain: "redistribute MSVCR80.DLL" is not, and likely never will be, any part of that work around.
Apologies for the markup; I have not yet familiarized myself with SF's updated tracker.
I'm not suggesting that you should redistribute MSVCR80.DLL if you don't want to; just that your headers must match your DLL.
If you are linking against MSVCRT.DLL, then your headers must correctly translate gmtime() and friends into gmtime(), and not into gmtime32(). The (unpatched) headers attempt to call gmtime32() if _USE_32BIT_TIME_T is set, and gmtime64() otherwise, which is consistent with what you must do to link against MSVCRT80.DLL. When the C source asks for 'gmtime' your headers must supply a definition that links against your chosen runtime library (right now they don't).
The logic I suggest works in all cases: by default it will resolve to names that link against MSVCRT.DLL; but if you choose to link against MSVCR80.DLL (and define MSVCRT_VERSION to 800 or higher) that will work too.
If you don't use this logic then all projects that can be built with both MinGW GCC and MSVC need to have special handling for _USE_32BIT_TIME_T when building with MinGW GCC (because it doesn't respect the rule that headers must match the DLL) -- this is why Jan views this as a MinGW GCC bug and not a Tcl bug.
With regards to the legality of redistributing MSVCR80+: Microsoft makes the redist installers available for free download (search for 'vcredist_x86.exe'). MSVCR110.DLL is redistributable with your application if you have a (free) license for MSVS 2012 Express (see http://msdn.microsoft.com/en-US/vstudio/hh857605). I understand the situation for earlier MSVCR*.DLL was less clear.
@Jan: "Or, if you assume Windows XP with 64-bit time_t by default:"
What? This is not what the Microsoft C runtime headers do (which is what MinGW GCC should be trying to emulate, since they want to link against Microsoft's runtime DLL(s)).
Moreover there is no support for 64-bit time_t on XP unless you redistribute MSVCR80+. MSVCRT.DLL does not have xxx64() functions, so your proposed change means that every project WITHOUT _USE_32BIT_TIME_T won't link. That's a hostile approach.
Err ... no, this is not so. On XP:
shows me that, e.g., both _time64() and time() are available; it is _time32() which is lacking -- presumably time() itself is equivalent to the _time32() of later versions.
OTOH, on Win7, the same command shows me that all three of time(), _time32(), and _time64() are exported. Reading between the lines of MSDN, it seems likely that by the time, (no pun intended), that MSVCRT.DLL had evolved to the Win7 version, (with the change apparently occurring in Vista), time() has become a trampoline for _time64(), unless a compile time redirection to _time32() is mandated by the inclusion of a definition for _USE_32BIT_TIME_T.
Again ... no, not entirely; it is also consistent with the requirement, AIUI, when linking against MSVCRT.DLL, on Win7 (or perhaps Vista) and later. You are correct in respect of XP, however. The issue is that the goal posts move, depending on which MS-Windows version the end user has.
It's likely that Earnie hasn't got it right yet, but I'd contend that it is actually a bug in both; it will just manifest differently for the two products, depending on which version of MS-Windows the end user happens to deploy on.
And of greater concern are those that build with MSVCRT.DLL supporting _USE_32BIT_TIME_T's *32 functions and try to distribute to XP. That is why I consider it a TCL issue; maybe not a bug but it will be a big surprise to many projects using TCL as a library of choice. SourceNavigator comes to mind.
I'll review the code to determine if I can do some trickery for the case of MSVCRT_VERSION < 800 and _USE_32BIT_TIME_T is defined. I think it will be beneficial to any using GCC but that will not be equivalent to what MSVC does but we're not anywhere near equivalent anyway. The best thing will be to create __mingwrt_* equivalents so that we don't have any surprises for those distributing software; maybe for 5.x series.
Let me rephrase this from the perspective of a developer using MSVC. The last MS compiler that links against MSVCRT.DLL by default is MSVC6. The C runtime headers shipped with MSVC6 have no reference to *time64() at all.
Windows XP shipped with MSVCRT.DLL ver 7.0, which includes the time64() functions. To access them you need to use headers in the platform SDK. All platform SDKs that support Windows XP provide time64() definitions. The last platform SDK that works with MSVC6 is Feb 2003 (Win2003 SP1); all SDKs up to and including that one always use a 32-bit time_t for a 32-bit compile (and a 64-bit time_t for a 64-bit compile). So if you wanted to use a 64-bit time_t in a 32-bit app then you used __time64_t and explicitly called *time64().
If you are building with a compiler later than MSVC6 then you will link against an MSVCRxx.DLL (xx > 70), and the C runtime headers shipped with the compiler expect that you are going to link against that DLL.
So if I as an MSVC-based developer compile a source tree for a 32-bit app with MSVC6 (so that it will run on any XP+ PC without me redistributing an MSVCRxx.DLL) then (1) when I declare a time_t it will be 32-bit; (2) when I call localtime() it will resolve to MSVCRT.DLL:localtime; and (3) it doesn't matter whether or not _USE_32BIT_TIME_T is defined in the source.
If I compile the same source tree as a 32-bit app with MSVC2005-2012 then (1) when I declare a time_t it will be 32-bit if _USE_32BIT_TIME_T is defined and 64-bit otherwise; (2) when I call localtime() it will resolve to MSVCRxx.DLL:localtimeyy() where yy is 32 if _USE_32BIT_TIME_T is defined and 64 otherwise; and (3) I must redistribute MSVCRxx.DLL.
So sources with _USE_32BIT_TIME_T defined will compile and link correctly with all versions of the MSVC compiler. Sources without _USE_32BIT_TIME_T may behave differently between MSVC6 and MSVC2005+.
So if you want to maintain source compatibility for 32-bit builds (I'm assuming that's the goal) then (A) if the source has _USE_32BIT_TIME_T then you must use a 32-bit time_t; (B) if you're linking against MSVCRT.DLL then you must call time() for a 32-bit time_t, or time64() for a 64-bit time_t -- ignore the fact that some versions of MSVCRT export time32(), these aren't prototyped in any MS header for MSVCRT.DLL (in either VC6 or the platform SDK) so they don't officially exist ; (C) if the source doesn't have _USE_32BIT_TIME_T then you need to decide on whether you're going to emulate MSVC6 or MSVC2005+ behaviour (the former assumes 32-bit time_t, the latter assumes 64-bit time_t); (D) if you want to allow the option to link against MSVCR80+ then (D.1) time_t should default to 64-bits in the absence of _USE_32BIT_TIME_T and (D.2) time() should resolve to *time32() for 32-bit time_t.
Point (B) above assumes that newer versions of MSVCRT.DLL have time() equivalent to time32() (rather than *time64() ). This must be the case if apps built with MSVC6 run on Windows 7.
Depending on your answer to (C), Jan's proposal (that I previously argued against) may be right, assuming similar preprocessor logic around the definition of time_t.
Earnie: "And of greater concern are those that build with MSVCRT.DLL supporting _USE_32BIT_TIME_T's *32 functions and try to distribute to XP. That is why I consider it a TCL issue"
Is there a disconnect between who chooses the headers and who chooses the library? That is, does GCC supply the C runtime headers, and Tcl's Makefile explicitly links against 'msvcrt.lib' or something like that? Then I can characterise this as a Tcl build problem.
But as I see it, Tcl calls e.g. localtime(). The C runtime headers offered up by MinGW GCC turn that into _localtime32(). Then GCC links against msvcrt.lib (which may not have a _localtime32). That's quite clearly a MinGW GCC issue.
I've patched the runtime at the following revision https://sourceforge.net/p/mingw/mingw-org-wsl/ci/3e0359b62de2b083a9b208f8db61745080a21654/ which I've tested on Win7. I'll be testing on XP soon.
Hi,
So if my code says "_USE_32BIT_TIME_T" it means "make time_t 32-bit, unless I'm linking against an old version of MSVCRT, in which case make it 64-bit" ?
If I declare a function "extern int My_GetTime(time_t *t);" then:
(1) without _USE_32BIT_TIME_T: MinGW gives me a 64-bit time_t; MSVC6 gives a 32-bit time_t; MSVC2005+ gives a 64-bit time_t.
(2) with _USE_32BIT_TIME_T: MinGW gives me a 64-bit time_t with MSVCRT.DLL or a 32-bit time_t with MSVCR80.DLL; MSVC6 gives a 32-bit time_t; MSVC2005+ gives a 32-bit time_t.
Were I to link a DLL built with MinGW into an app built with MSVC, or vice versa, there would be a type size mismatch and memory overwriting. There is no source level compatibility here.
The define "_USE_32BIT_TIME_T" means "make time_t 32-bit". If you can't do that then raise a preprocessor error. I should never get a 64-bit time_t if "_USE_32BIT_TIME_T" is defined. A side-effect of the define is that the headers must arrange for localtime(), gmtime() and friends to be mapped only library functions that handle a 32-bit time_t (irrespective of what they are called; _USE_32BIT_TIME_T does NOT mean 'call the xxx32()' functions, that's just a consequence of MS's function naming choices in MSVCR80.DLL).
Twylite, thanks for the insightful feedback. I'm close to stating that our minimum supported version is XP and MSVC version 7.10. If I do that then what we have works. Else I need to do some better digging of old data type structures. Or maybe just do
What did the MSVCRT_VERSION 700 do with time_t?
Here is my patch (mingw2.patch), based on current wsl-4.0. It removes the warning
"Your MSVCRT_VERSION does not support the use of _USE_32BIT_TIME_T",
and makes the necessary changes such that msvcrt.dll (version 7.1
or later) support _USE_32BIT_TIME_T just fine. The explanation why this
works, can be found in this earlier in this ticket (Thanks, Twylite, for
your very clear comments). This patch assumes the VS2005+ behavior:
time_t is 64-bit, unless _USE_32BIT_TIME_T is specified. By default
it will work on XP or higher, when _USE_32BIT_TIME_T is specified it
should work on Windows 95/98/ME as well.
Hi Jan,
System32\MSVCRT.DLL 7.0.2600.0, 2001-Aug-17 (original Windows XP Pro release):
System32\MSVCRT.DLL 7.0.2600.5512, 2008-Apr-14 (XP SP3 revision):
There are no xxx32() functions in there.
I don't see how _utime() can ever be a wrapper around _utime32() and work on a basic install of Windows XP with no added libraries. _utime() only needs to resolve to _utime32() if you are linking against MSVCRT80+.
Jan & Earnie: I think the logic we are looking for is the following. I've introduced a define _TIME_T_IS_32BIT to help clarify that _USE_32BIT_TIME_T only determines whether time_t is 32 or 64 bits; in the case of a 32-bit time_t the MSVCRT version determines whether you link against xxx() or xxx32(). (The define _TIME_T_IS_32BIT is redundant, it can be replaced by _USE_32BIT_TIME_T)
Dealing specifically with sys/utime.h:
Earnie, as I understand it you can compile with _USE_32BIT_TIME_T defined using any version of MSVC (linking against any version of MSVCRT) and you will get a 32-bit time_t.
If you don't have _USE_32BIT_TIME_T defined then compilers pre-MSVC-2005 will give you a 32-bit time_t by default (and I'm not aware of any header that will give you a 64-bit time_t when compiling a 32-bit app); compilers MSVC-2005 and later will give you a 64-bit time_t by default. MinGW already defaults to 64-bit time_t, so it would make sense to follow the MSVC-2005 approach.
If you link against MSVCRT.DLL 7.0 or lower then there are no xxx32() functions, only xxx() and xxx64(). If you link against MSVCR80.DLL or higher then there are no xxx() functions, only xxx32() and xxx64(). You must specialise your prototypes and inline functions according to the target MSVCRT DLL, not the definition of time_t (which is determined only by _USE_32BIT_TIME_T irrespective of the target runtime DLL).