Issue with dllexport/dllimport inline function
Hi, I'm trying to dllexport/dllimport an inline function, but I get this warning: ``` inline function ‘g_strcmp0’ declared as dllimport: attribute ignored [- Wattributes] ``` This is when cross compiling my code on Linux for Windows using mingw. The relevant code is ``` GLIB_API inline int g_strcmp0 (const char *str1, const char *str2) { if (!str1) return -(str1 != str2); if (!str2) return str1 != str2; return strcmp (str1, str2); } ``` Where GLIB_API is defined to `__declspec(dllexport)` when compiling the DLL and `__declspec(dllimport)` when including the header from application. The goal is to get that function inlined when doing `if (g_strcmp0(a, b))`, but get address from instance in the DLL when doing `do_sort(array, g_strcmp0)`. I believe dllimport of inline function should be supported, it compiles fine with MSVC. It is even documented there: https://learn.microsoft.com/en-us/cpp/cpp/defining-inline-cpp-functions-with-dllexport-and-dllimport?view=msvc-170 I believe (but not tested myself) that clang had the same issue but got fixed years ago: https://reviews.llvm.org/D3772 For context, I'm hitting this issue when working on that GLib patch: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2926 Is there a trick to get that working with GCC? Or should that issue be reported somewhere? Regards, Xavier Claessens
Issue with dllexport/dllimport inline function
Hi, I'm trying to dllexport/dllimport an inline function, but I get this warning: ``` inline function ‘g_strcmp0’ declared as dllimport: attribute ignored [- Wattributes] ``` This is when cross compiling my code on Linux for Windows using mingw. The relevant code is ``` GLIB_API inline int g_strcmp0 (const char *str1, const char *str2) { if (!str1) return -(str1 != str2); if (!str2) return str1 != str2; return strcmp (str1, str2); } ``` Where GLIB_API is defined to `__declspec(dllexport)` when compiling the DLL and `__declspec(dllimport)` when including the header from application. The goal is to get that function inlined when doing `if (g_strcmp0(a, b))`, but get address from instance in the DLL when doing `do_sort(array, g_strcmp0)`. I believe dllimport of inline function should be supported, it compiles fine with MSVC. It is even documented there: https://learn.microsoft.com/en-us/cpp/cpp/defining-inline-cpp-functions-with-dllexport-and-dllimport?view=msvc-170 I believe (but not tested myself) that clang had the same issue but got fixed years ago: https://reviews.llvm.org/D3772 For context, I'm hitting this issue when working on that GLib patch: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2926 Is there a trick to get that working with GCC? Or should that issue be reported somewhere? Regards, Xavier Claessens
Re: Issue with dllexport/dllimport inline function
Le mardi 11 octobre 2022 à 13:00 +0800, LIU Hao a écrit : > you may try > > ``` > __attribute__((__gnu_inline__)) > extern inline > int g_strcmp0(const char*, const char*) > { ... > ``` Thanks, I gave that a try but I get many: multiple definition of `g_strcmp0'. But at least the warning about dllimport is now gone. > In contrast to C, you don't need the `extern` in C++. > > When this function is being defined in a DLL, `__dllexport__` should > be used in place of > `__gnu_inline__`. This may require another macro, similar to > `GLIB_API`.
Re: Issue with dllexport/dllimport inline function
Le mardi 11 octobre 2022 à 21:35 +0800, LIU Hao a écrit : > 在 2022/10/11 21:28, LIU Hao via Gcc 写道: > > > > Did you have any declarations that had no `__gnu_inline__`? GNU > > inline functions are supposed to be > > emitted out of line, even when its address is taken. > > > > This should be 'to be never emitted out of line'. Sorry for that. I have one .c file, part of libglib-2.0-0.dll, that has that line: ``` extern int g_strcmp0 (const char *str1, const char *str2); ``` The header file has that: ``` GLIB_INLINE int g_strcmp0 (const char *str1, const char *str2) { if (!str1) return -(str1 != str2); if (!str2) return str1 != str2; return strcmp (str1, str2); } ``` I now define GLIB_INLINE that way: ``` #if (defined(_WIN32) || defined(__CYGWIN__)) && !defined(GLIB_STATIC_COMPILATION) # define _GLIB_EXPORT __declspec(dllexport) # define _GLIB_IMPORT __declspec(dllimport) #elif __GNUC__ >= 4 # define _GLIB_EXPORT __attribute__((visibility("default"))) # define _GLIB_IMPORT #else # define _GLIB_EXPORT # define _GLIB_IMPORT #endif #ifdef GLIB_COMPILATION # define _GLIB_API _GLIB_EXPORT # define GLIB_INLINE __attribute__((__dllexport__)) __attribute__((__gnu_inline__)) extern inline #else # define _GLIB_API _GLIB_IMPORT # if defined(__has_attribute) && __has_attribute(__gnu_inline__) // https://gcc.gnu.org/pipermail/gcc/2022-October/239592.html #define GLIB_INLINE __attribute__((__gnu_inline__)) extern inline # else #define GLIB_INLINE _GLIB_EXPORT extern inline # endif #endif ``` `GLIB_COMPILATION` is defined when compiling libglib-2.0-0.dll but not when compiling other dlls or applications. The linker issue actually happens when linking libglib-2.0-0.dll, multiple definition happens for all .c files that actually uses g_strcmp0: ``` /usr/bin/x86_64-w64-mingw32-ld: glib/libglib-2.0- 0.dll.p/deprecated_grel.c.obj: in function `g_strcmp0': /home/xclaesse/programmation/glib/builddir/../glib/gtestutils.h:245: multiple definition of `g_strcmp0'; glib/libglib-2.0- 0.dll.p/deprecated_gcache.c.obj:/home/xclaesse/programmation/glib/build dir/../glib/gtestutils.h:245: first defined here ```
Re: Issue with dllexport/dllimport inline function
Le mercredi 12 octobre 2022 à 01:42 +0800, LIU Hao a écrit : > 在 2022-10-11 22:26, xclae...@gmail.com 写道: > > #ifdef GLIB_COMPILATION > > # define _GLIB_API _GLIB_EXPORT > > # define GLIB_INLINE __attribute__((__dllexport__)) > > __attribute__((__gnu_inline__)) extern inline > > This is not correct. Typically, `dllexport` indicates that 'I want an > external definition' but > `gnu_inline` indicates that 'I do not want an external definition', > so it's a contradiction: you get > either multiple definitions, or none (which causes undefined > references). Sorry, that was a wrong copy/paste when writing the email (leftover of things I tried previously). I can confirm the multiple definition error I have is when that line is `# define GLIB_INLINE _GLIB_EXPORT extern inline`, which expands to `__declspec(dllexport) extern inline`. > To user code which imports such a function, it should be seen as > > ``` > __attribute__((__gnu_inline__)) > extern inline > int g_strcmp0(const char*, const char*) > ``` > > This has the desired effect: The function can be inlined where > appropriate. And if not, a call to > the external thunk is emitted; no weak definition is emitted in > either case. > > > To the library itself which exports it, it should be seen as > > ``` > __attribute__((__dllexport__)) > extern inline > int g_strcmp0(const char*, const char*) > ``` > > There is no `gnu_inline` in this case. GCC always generates a > COMDAT/weak/linkonce definition, which > will not cause multiple definition errors. Right, that's what I do and it fails. I made a simplified example: https://gitlab.gnome.org/xclaesse/inline-example Build it with: meson setup builddir --cross-file cross_file_mingw64.txt ninja -C builddir/ And it fails with: ``` x86_64-w64-mingw32-gcc -o libfoo.dll libfoo.dll.p/foo.c.obj libfoo.dll.p/bar.c.obj -Wl,--allow-shlib-undefined -shared -Wl,--start- group -Wl,--out-implib=libfoo.dll.a -fstack-protector -lkernel32 - luser32 -lgdi32 -lwinspool -lshell32 -lole32 -loleaut32 -luuid - lcomdlg32 -ladvapi32 -Wl,--end-group /usr/bin/x86_64-w64-mingw32-ld: libfoo.dll.p/bar.c.obj: in function `g_strcmp0': /home/xclaesse/programmation/inline-example/builddir/../foo.h:5: multiple definition of `g_strcmp0'; libfoo.dll.p/foo.c.obj:/home/xclaesse/programmation/inline- example/builddir/../foo.h:5: first defined here ``` Thanks for your help!
Re: Issue with dllexport/dllimport inline function
Thanks a lot for your help. Sorry for late reply, but I gave your trick a try and it did not work: ``` /home/xclaesse/programmation/inline-example/builddir/../foo.h:7: multiple definition of `g_strcmp0'; libfoo.dll.p/foo.c.obj:/home/xclaesse/programmation/inline- example/builddir/../foo.h:7: first defined here ``` Updated the test case there: https://gitlab.gnome.org/xclaesse/inline-example Regards, Xavier Claessens. Le mercredi 12 octobre 2022 à 11:03 +0800, LIU Hao a écrit : > 在 2022/10/12 02:37, xclae...@gmail.com 写道: > > > > Build it with: > > meson setup builddir --cross-file cross_file_mingw64.txt > > ninja -C builddir/ > > > > And it fails with: > > That's because you compiled it as C. I used g++ to compile these, and > there was no such error. > > > For C, this inline issue is going to be much more messy due to lack > of COMDAT support about > `inline`. Your will need a dedicated macro for each translation unit. > A dirty solution looks like this: > > ``` > #ifndef INSIDE_FOO_C > __attribute__((__dllexport__)) inline > #endif > extern > int > g_strcmp0(const char*str1, const char*str2) > ... > ``` > > and in 'foo.c': > > ``` > #define INSIDE_FOO_C 1 > #include "foo.h" > > extern int g_strcmp0 (const char *str1, const char *str2); > ``` > > > Further reading: > > https://github.com/lhmouse/mcfgthread/wiki/Differences-between-GNU,-C99-and-C---%60inline%60 > > > -- > Best regards, > LIU Hao
Re: Issue with dllexport/dllimport inline function
Oh, that seems to be working now. Still got a small issue, glib compiles with -Werror=missing- prototypes: ../foo.h:7:5: error: no previous prototype for ‘g_strcmp0’ [- Werror=missing-prototypes] I'm a bit lost with what the prototype would look like in this case. I think `__attribute__((__dllexport__))` will be needed when building with MSVC at least. Seems gcc, clang and msvc each wants a different trick :/ Thanks, Xavier Claessens. Le mercredi 02 novembre 2022 à 00:06 +0800, LIU Hao a écrit : > 在 2022-11-01 23:38, xclae...@gmail.com 写道: > > Thanks a lot for your help. > > > > Sorry for late reply, but I gave your trick a try and it did not > > work: > > ``` > > /home/xclaesse/programmation/inline-example/builddir/../foo.h:7: > > multiple definition of `g_strcmp0'; > > libfoo.dll.p/foo.c.obj:/home/xclaesse/programmation/inline- > > example/builddir/../foo.h:7: first defined here > > ``` > > > > Apologies for the typo. The `__dllexport__` in 'foo.h' should have > been `__gnu_inline__`. If, for > some reason, it needs to be specified explicitly, you may do that in > 'foo.c' instead. > > 'foo.h': > ``` > #ifndef INSIDE_FOO_C > __attribute__((__gnu_inline__)) __inline__ > #endif > extern > int g_strcmp0(const char*str1, const char*str2) { > ... > ``` > > 'foo.c': > ``` > __attribute__((__dllexport__)) /* This is optional. */ > extern int g_strcmp0 (const char *str1, const char *str2); > ``` > > > > > -- > Best regards, > LIU Hao >