Tíquete #42344

Implement a generic infrastructure to facilitate run-time linking of API functions

: 2021-05-22 04:46 Última Atualização: 2021-05-26 00:48

Relator:
Dono:
Estado:
Aberto [Owner assigned]
Componente:
Marcos:
(Nenhum)
Prioridade:
5 - Medium
Gravidade:
5 - Medium
Resolução:
Accepted
Arquivo:
1
Vote
Score: 1
100.0% (1/1)
0.0% (0/1)

Details

As support for new (Windows version-dependent) APIs is added to MinGW, there is increased potential for creating an application which will not load, much less run, on legacy Windows versions; the typical manifestation of such a load failure is that the process crashes with an "entry point not found" exception, for the unsupported DLL function, before the main() function even gets control, so there's no opportunity to handle the exception.

The only way to mitigate such exceptions, such that the application retains control, is to always link potentially unsupported API functions dynamically, at run-time, by retrieval of an entry-point reference, using GetProcAddress(), and to always invoke the function via the reference pointer, (and never call it directly, by name). The mechanism for dynamically linking such functions is mostly the same, in each case, often resulting in significant repetitive coding; it would be a great convenience, if MinGW were to implement a generic API infrastructure, to encapsulate the common features of the required run-time linking interface.

Ticket History (3/5 Histories)

2021-05-22 04:46 Updated by: keith
  • New Ticket "Implement a generic infrastructure to facilitate run-time linking of API functions" created
2021-05-23 01:03 Updated by: keith
Comentário

Consider the (trivial) program, (contrived in conjunction with development related to feature request #41567):

  1. #define _MINGW_LEGACY_SUPPORT
  2. #define NTDDI_VERSION NTDDI_WIN10_RS5
  3. #include <stdio.h>
  4. #include <winbase.h>
  5. #include <winerror.h>
  6. #include <wincon.h>
  7. int conapi_test( void )
  8. { HPCON hPC; COORD size = { 80, 25 };
  9. if( CreatePseudoConsole( size, 0, 0, 0, &hPC ) == ERROR_OLD_WIN_VERSION )
  10. fprintf( stderr,
  11. "Pseudo-consoles not supported; Win10-RS5 (or later) is required.\n"
  12. );
  13. ResizePseudoConsole( hPC, size );
  14. ClosePseudoConsole( hPC );
  15. return 0;
  16. }
  17. int main()
  18. { printf( "Main program started\n" );
  19. return conapi_test();
  20. }

Ignoring the effect of line 1, for the time being — because its influence is, as yet, unimplemented — this will not compile, with the currently released W32API version; compilation will fail, because HPCON is an undefined data type, and furthermore, CreatePseudoConsole(), ResizePseudoConsole(), and ClosePseudoConsole() are undeclared, so implicit declaration warnings are raised for each:
$ mingw32-gcc -Wall -O3 conapi-test.c
pc.c: In function 'conapi_test':
pc.c:10:3: error: unknown type name 'HPCON'; did you mean 'HICON'?
   10 | { HPCON hPC; COORD size = { 80, 25 };
      |   ^~~~~
      |   HICON
pc.c:11:7: warning: implicit declaration of function 'CreatePseudoConsole' [-Wimplicit-function-declaration]
   11 |   if( CreatePseudoConsole( size, 0, 0, 0, &hPC ) == ERROR_OLD_WIN_VERSION )
      |       ^~~~~~~~~~~~~~~~~~~
pc.c:15:3: warning: implicit declaration of function 'ResizePseudoConsole' [-Wimplicit-function-declaration]
   15 |   ResizePseudoConsole( hPC, size );
      |   ^~~~~~~~~~~~~~~~~~~
pc.c:16:3: warning: implicit declaration of function 'ClosePseudoConsole' [-Wimplicit-function-declaration]
   16 |   ClosePseudoConsole( hPC );
      |   ^~~~~~~~~~~~~~~~~~

OTOH, if I implement the changes proposed in the first tentative implementation patch, attached to #41567, and add "-I ..." and "-L ..." references, to locate the uninstalled headers and import libraries within my local MinGW-WSL build tree, this does compile, and link successfully:
$ mingw32-gcc -Wall -O3 -I ../w32api/include/ conapi-test.c -L ./w32api/
$ cp a.exe ~/VirtualBox/share/

So outwardly, all looks good. However, if I try to run this on any Windows version pre-dating Win10-RS5, (I tried WinXP, in a virtual machine), it crashes before it ever gets to that first printf statement in main(); I see a message box telling me that entry point CreatePseudoConsole could not be found, in kernel32.dll, and the process dies; (I guess it could equally well have failed on either of the other two pseudo-console API entry points, depending on the order in which the run-time loader attempts to resolve them).


Curiously, if I run it under Wine, (configured to emulate Win7 behaviour), it runs successfully:

$ ./a.exe
Main program started
which should not have happened; I guess this is a bug in Wine-6.7, insofar as it fails to reject API calls which should not be supported in its configured Windows version emulation.

2021-05-23 08:08 Updated by: keith
Comentário

I've attached a proposed implementation.

With this in place, and with the <wincon.h> update, as proposed for feature request #41567, augmented by the further addition of:

  1. diff --git a/w32api/include/wincon.h b/w32api/include/wincon.h
  2. --- a/w32api/include/wincon.h
  3. +++ b/w32api/include/wincon.h
  4. @@ -423,10 +423,49 @@ WINAPI HRESULT CreatePseudoConsole (COOR
  5. WINAPI HRESULT ResizePseudoConsole (HPCON, COORD);
  6. WINAPI void ClosePseudoConsole (HPCON);
  7. #define PSEUDOCONSOLE_INHERIT_CURSOR (DWORD)(1)
  8. +#ifdef _MINGW_LEGACY_SUPPORT
  9. +/* For MinGW legacy platform support, provide inline redirector stubs,
  10. + * to facilate graceful fallback action, in the event that any program,
  11. + * which has been linked with the pseudo-console API, is run on an older
  12. + * version of Windows.
  13. + */
  14. +#include "legacy.h"
  15. +
  16. +__CRT_ALIAS WINAPI HRESULT CreatePseudoConsole
  17. +( COORD size, HANDLE input, HANDLE output, DWORD flags, HPCON *con )
  18. +{
  19. + typedef WINAPI HRESULT (*api)( COORD, HANDLE, HANDLE, DWORD, HPCON * );
  20. +
  21. + static void *call = API_UNCHECKED;
  22. + return ((call = __kernel32_entry_point( call, __FUNCTION__ )) != NULL)
  23. + ? ((api)(call))( size, input, output, flags, con )
  24. + : __legacy_support( ERROR_OLD_WIN_VERSION );
  25. +}
  26. +
  27. +__CRT_ALIAS WINAPI HRESULT ResizePseudoConsole (HPCON con, COORD size)
  28. +{
  29. + typedef WINAPI HRESULT (*api)( HPCON, COORD );
  30. +
  31. + static void *call = API_UNCHECKED;
  32. + return ((call = __kernel32_entry_point( call, __FUNCTION__ )) != NULL)
  33. + ? ((api)(call))( con, size ) : __legacy_support( ERROR_OLD_WIN_VERSION );
  34. +}
  35. +
  36. +__CRT_ALIAS WINAPI void ClosePseudoConsole (HPCON con)
  37. +{
  38. + typedef WINAPI void (*api)( HPCON );
  39. +
  40. + static void *call = API_UNCHECKED;
  41. + if( (call = __kernel32_entry_point( call, __FUNCTION__ )) == NULL )
  42. + (void)__legacy_support( ERROR_OLD_WIN_VERSION );
  43. + else ((api)(call))( con );
  44. +}
  45. +#endif /* _MINGW_LEGACY_SUPPORT */
  46. +
  47. #endif /* Win10 Redstone 5 and later */
  48. #endif /* Vista and later */
  49. #endif /* WinXP and later */
  50. #endif /* Win2K and later */
my example program, from my previous comment, not only compiles and links successfully, but when I run it on the WinXP virtual machine, I now see the expected output:
$ /e/a.exe
Main program started
Pseudo-consoles not supported; Win10-RS5 (or later) is required.

I propose adding this for W32API version 5.5; notice that it does now attribute meaning to the proposed new _MINGW_LEGACY_SUPPORT feature test macro.

2021-05-26 00:48 Updated by: keith
  • Resolução Update from Nenhum to Accepted
Comentário

Well it appeared to be working, so I committed 7b37e9a. I then moved on to ticket #42383, which unfortunately revealed a careless logic error ... corrected by commit e5e5f06.

Attachment File List

Editar

Please login to add comment to this ticket » Login