bug-gnulib
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: gettimeofday.c windows version?


From: Eli Zaretskii
Subject: Re: gettimeofday.c windows version?
Date: Mon, 12 Dec 2022 15:06:21 +0200

> Date: Sun, 11 Dec 2022 23:17:32 -0800
> Cc: bug-gnulib@gnu.org, rogerdpack@gmail.com
> From: Paul Eggert <eggert@cs.ucla.edu>
> 
> That being said, I'm not quite understanding the underlying issue here. 

I'll try to explain.

> Is it that MinGW has "#define _WIN32_WINNT 0x502", which as I understand 
> from [1] means MinGW says it assumes MS-Windows Server 2003 and later, 
> whereas Eli wants Emacs 30+ to be portable even further back to 
> MS-Windows XP (i.e., _WIN32_WINNT == 0x0501)? Or is it more complicated 
> than that?

First, some background (and please forgive me, you and others, if what
I say is well known).

The Windows system headers that declare Windows specific APIs
(a.k.a. "Win32 APIs") have some of their parts guarded by the likes of

  #if _WIN32_WINNT >= NNNN
  ...
  #endif

These guard the prototypes of the APIs which are only supported in
Windows versions NNNN or higher, and the struct's and macros which
support them.  Thus, compiling a program while _WIN32_WINNT is defined
to a lower value will not see those parts of the system headers, and
at best you will get compilation warnings.  But usually, you can also
link-time errors, because Win32 API functions use the Pascal calling
convention, and the linker cannot resolve the external calls unless it
sees the prototype.  (This is no longer a problem for 64-bit Windows
programs, AFAIK, but 32-bit programs still need the prototype to
successfully link.)

The next piece of this puzzle is that programs which are supposed to
be able to run on a wide variety of Windows versions, some of which do
not support a particular API (because it was introduced starting from
Windows version X), don't call these APIs directly, because calling
them directly will cause the program to refuse to start on older
Windows.  This failure to start happens because dynamic linking on
Windows records in the binary that it must import a given symbol from
a certain shared library, and if that shared library lacks that
symbol, the Windows program loader fails to load the program.  So
instead such programs call these APIs through function pointers, and
populate the pointer at run time by the moral equivalent of
dlopen+dlsym.  If dlsym fails, the program knows that the API is
unavailable, and falls back to other methods, or even flatly rejects
the requests for functionality based on those APIs.  But it does run
and can be functional, albeit only partially so.

However, even these programs with run-time test for the API
availability need the proper prototypes and struct/macro definitions
in order to compile.  So one technique is to temporarily set
_WIN32_WINNT to a higher value than needed during compilation, just to
have the system headers reveal the necessary declarations, and then
still use the "call through function pointer" technique.
Alternatively, the guarded prototypes and macros are manually added to
the program source (this is what gettimeofday.c does).

Paul, you should be able to see this technique in many places in
Emacs, in the w32*.c files, where we call many APIs that are only
available on recent Windows systems.

Now, mingw.org's MinGW has _WIN32_WINNT set to the Windows 9X version
by default, because that's the base target system of that environment.
MinGW64, by contrast, uses Vista or Windows 7 as its base, and doesn't
support older systems.  Moreover, programs that have good reasons
could set _WIN32_WINNT to higher or lower values, as they please.
Thus, the value of _WIN32_WINNT in effect when compiling Gnulib is not
known in advance.  In gettimeofday.c we see code which assumes that a
program that sets _WIN32_WINNT to Windows 8 means that the binary will
only run on Windows 8 and later, so it links directly to an API which
is unavailable on older systems.  If _WIN32_WINNT is set to a lower
value than Windows 8, gettimeofday.c falls back to the technique I
described above: it probes the availability at run time, and prefers
the modern API if it can use it, but uses an older, less accurate API
if the more modern one is unavailable.

Now, to answer your question: my suggestion is not to deliberately
break gettimeofday.c for older systems.  Gnulib already has there what
I consider a superior technique: check the availability of an API at
run time, and use fallback if it is not available.  So forcing
programs which happened to have _WIN32_WINNT defined to Windows 8 or
higher when Gnulib was compiled (which could be for a reason utterly
unrelated to the intended target system of the entire program) to fail
to start on older Windows systems is IMO gratuitous and error-prone.
It is error-prone because the fact that _WIN32_WINNT is set to some
value could be for a reason other than the target system.  And Gnulib
is a library, so it is IMO wrong for it to decide for the applications
on what OS they can and cannot run.

> If it's merely the abovementioned issue, Gnulib already in some cases 
> ports to _WIN32_WINNT <= 0x501 in some cases, so would it be painful for 
> it to do so here too?

As I tried to explain above, just setting _WIN32_WINNT could be due to
reasons other than forcing the lowest version of the target OS.

> (But then, I haven't seen a specific patch 
> proposed that meets Eli's concerns - perhaps I missed it?)

I didn't post a patch, but my suggestion is to remove the #else branch
of this part:

  # if !(_WIN32_WINNT >= _WIN32_WINNT_WIN8)

  /* Avoid warnings from gcc -Wcast-function-type.  */
  #  define GetProcAddress \
      (void *) GetProcAddress

  /* GetSystemTimePreciseAsFileTime was introduced only in Windows 8.  */
  typedef void (WINAPI * GetSystemTimePreciseAsFileTimeFuncType) (FILETIME 
*lpTime);
  static GetSystemTimePreciseAsFileTimeFuncType 
GetSystemTimePreciseAsFileTimeFunc = NULL;
  static BOOL initialized = FALSE;

  static void
  initialize (void)
  {
    HMODULE kernel32 = LoadLibrary ("kernel32.dll");
    if (kernel32 != NULL)
      {
        GetSystemTimePreciseAsFileTimeFunc =
          (GetSystemTimePreciseAsFileTimeFuncType) GetProcAddress (kernel32, 
"GetSystemTimePreciseAsFileTime");
      }
    initialized = TRUE;
  }

  # else

  #  define GetSystemTimePreciseAsFileTimeFunc GetSystemTimePreciseAsFileTime

  # endif

and make this part unconditional:

  # if !(_WIN32_WINNT >= _WIN32_WINNT_WIN8)
    if (!initialized)
      initialize ();
  # endif

IOW, I suggest to use the cautious run-time test on all versions of
Windows, not only on those older than Windows 8.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]