On Sun, 9 Oct 2022, LIU Hao wrote:

在 2022/10/9 18:05, Pali Rohár 写道:

Ok, I see.

Anyway, I quite do not understand why some crt exported functions are
marked with _CRTIMP (as it is empty during CRT build) and some exported
variables use __MINGW_IMP_SYMBOL. I would expect that all exported
functions and variables (from dll) would use same "syntax" and same
"behavior". Is not this a bug?

And why is not used directly __desclspec(dllimport)? I think that
specifying dllimport is same as using __MINGW_IMP_SYMBOL and then
defining a new macro with pointer dereferencing of defined symbol.
Or not and is there something special why "more complicated" approach
via __MINGW_IMP_SYMBOL is required?


Because sometimes we would like to override them, for example, in 'ucrtbase_compat.c'.


This

 ```
 void __attribute__((__dllimport__)) _tzset(void);
 void (*__imp__tzset)(void) = _tzset;

 int
 main(void)
   {
     _tzset();
   }
 ```

will cause an error, as the function call to dllimport'd function in `main()` pulls it from 'libmsvcrt.a', which also brings its own pointer:

 ```
 $ x86_64-w64-mingw32-gcc -Wall -Wextra test.c
 /usr/bin/x86_64-w64-mingw32-ld: /usr/lib/gcc/x86_64-w64-mingw32/10-w
 in32/../../../../x86_64-w64-mingw32/lib/libmsvcrt.a(libmsvcrt-oss007
 17.o):(.idata$5+0x0): multiple definition of `__imp__tzset'; /tmp/cc
 1GlQ7e.o:test.c:(.data+0x0): first defined here
 collect2: error: ld returned 1 exit status
 ```

I think this example kinda misses the point; here you already do have an __imp__tzset elsewhere (from the import library) - but we only do manually define e.g. __imp__tzset when we're in a configuration where the import library doesn't provide it (or we've commented it out from the def file).


Historically, the _CRTIMP macro comes from how it is used in upstream msvcrt; you can link the CRT either statically or dynamically. If intending to link statically, _CRTIMP expands to nothing, while if intending to link dyanmically, _CRTIMP expands to dllimport. For mingw-w64, there's no option to link the full CRT statically, so from user code point of view, there's only the dllimport case to care about.

But when building code within mingw-w64-crt, _CRTIMP expands to nothing. I guess this is generally desireable when implementing some of the functions that are marked _CRTIMP (we generally shouldn't have a dllimport declaration of the function visible when we're implementing it).

However this can also be tricky, because if it indeed is a data symbol that we're overriding/providing in mingw-w64-crt, then having it visible without dllimport is fine (when other object files in mingw-w64-crt reference it, and both of them will be linked statically, but if it is a symbol that will be imported from a DLL file, via the def files, then accessing it without dllimport is going to cause unnecessary auto imports and runtime pseudo relocs.


So in short, the following two are functionally exactly equivalent - they generate the same code in a compiler when you reference it:

    extern int * __MINGW_IMP_SYMBOL(someDataSymbol);
  #define someDataSymbol (* __MINGW_IMP_SYMBOL(someDataSymbol))
and
    extern int __declspec(dllimport) someDataSymbol;

However if you make it
    extern int _CRTIMP someDataSymbol;

then you lose the dllimport for any code within mingw-w64-crt that is accessing it - leading to unnecessary auto imports and runtime pseudo relocs.


I think the common pattern within mingw-w64-headers is to simply use the first form via the explicit __MINGW_IMP_SYMBOL redirection for any data symbols, while _CRTIMP is used for functions only. Exported data symbols in the CRT interface generally are problematic anyway. And the define form, while kinda unwieldy, gives you a bit more freedoms to check for it with ifdef, work around it with undef, etc.

// Martin

_______________________________________________
Mingw-w64-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to