问题
I need to use some symbols from the main executable in a plugin.
Linking against the executable causes the following linker errors:
i686-w64-mingw32-g++ example.cpp -shared -I.. -std=c++11 -o test.dll ../../test.exe -static-libgcc -static-libstdc++ -fvisibility=hidden
[..]/test.exe:cygming-crtbegin.c:(.text+0x500): multiple definition of `__gcc_register_frame'
/usr/lib/gcc/i686-w64-mingw32/5.1.0/crtbegin.o:cygming-crtbegin.c:(.text+0x0): first defined here
[..]/test.exe:cygming-crtbegin.c:(.text+0x560): multiple definition of `__gcc_deregister_frame'
/usr/lib/gcc/i686-w64-mingw32/5.1.0/crtbegin.o:cygming-crtbegin.c:(.text+0x60): first defined here
[..]/test.exe: In function `ZlsRSoRK5Color':
[..]src/tools.h:212: multiple definition of `operator<<(std::ostream&, Color const&)'
/tmp/ccC97Hkz.o:example.cpp:(.text$_ZlsRSoRK5Color[__ZlsRSoRK5Color]+0x0): first defined here
../../test.exe: In function `ZN7MessageILb0EElsIcEERS0_OT_':
[..]/src/tools.h:241: multiple definition of `Message<false>& Message<false>::operator<< <char>(char&&)'
/tmp/ccC97Hkz.o:example.cpp:(.text$_ZN7MessageILb0EElsIcEERS0_OT_[__ZN7MessageILb0EElsIcEERS0_OT_]+0x0): first defined here
[..]/test.exe:crtexe.c:(.idata+0x3f0): multiple definition of `_imp__GeoIP_country_code'
[..]/test.exe:crtexe.c:(.idata+0x3f0): first defined here
[..]/test.exe:crtexe.c:(.idata+0x3f4): multiple definition of `_imp__GeoIP_country_name'
[..]/test.exe:crtexe.c:(.idata+0x3f4): first defined here
/usr/lib/gcc/i686-w64-mingw32/5.1.0/crtbegin.o:cygming-crtbegin.c:(.text+0x22): undefined reference to `_Jv_RegisterClasses'
collect2: error: ld returned 1 exit status
Now if I build the main executable with -shared -Wl,--export-all-symbols
then linking against test.exe
works,
but the Windows loader (or at least the wine one) complains about test.exe
being a dll.
So I need to re-link test.exe
once again without -shared
so I am able to run test.exe
.
i.e.:
# produce the import executable
i686-w64-mingw32-g++ tools.o main.o [...] -o ../test.exe -shared -Wl,--export-all-symbols [...] -static-libgcc -static-libstdc++
# produce the real executable
i686-w64-mingw32-g++ tools.o main.o [...] -o ../test.exe -Wl,--export-all-symbols [...] -static-libgcc -static-libstdc++
That's super hackish, but at the end I do have a working plugin...
To come to my question:
Is there a better way to achive this (without passing around function pointers)?
I know MSVC
is able to output import libraries for executables, is there a similar way for MinGW
?
I have tried to add -Wl,--out-implib,test.a
to the linker flags to get an import library for the executable,
but --out-implib
seems to be ignored when linking an executable.
回答1:
This is a case where you probably do want to qualify the callback symbols, in the .exe
, with the __declspec(dllexport)
attribute. Cross-compiling on my Linux Mint Debian box, the following minimal example works for me:
$ cat foo.c
#include <stdio.h>
int __declspec(dllexport) foo( int bar ){ return bar << 2; }
int main(){ printf( "%d\n", foo( 4 ) ); return 0; }
$ mingw32-gcc -o ~/src/exp/foo.exe -Wl,--out-implib=libfoo.dll.a foo.c
This produces both a working executable, and an import library to map its exported symbols, for use when linking plug-ins, with just one invocation of the linker in the preceding commands, (as seen when running the executable under wine, and listing the import library using the native linux nm
tool):
$ ~/src/exp/foo.exe
16
$ nm -A libfoo.dll.a
libfoo.dll.a:d000002.o:00000000 I _foo_exe_iname
libfoo.dll.a:d000002.o:00000000 i .idata$4
libfoo.dll.a:d000002.o:00000000 i .idata$5
libfoo.dll.a:d000002.o:00000000 i .idata$7
libfoo.dll.a:d000000.o: U _foo_exe_iname
libfoo.dll.a:d000000.o:00000000 I __head_foo_exe
libfoo.dll.a:d000000.o:00000000 i .idata$2
libfoo.dll.a:d000000.o:00000000 i .idata$4
libfoo.dll.a:d000000.o:00000000 i .idata$5
libfoo.dll.a:d000001.o:00000001 a @feat.00
libfoo.dll.a:d000001.o:00000000 T _foo
libfoo.dll.a:d000001.o: U __head_foo_exe
libfoo.dll.a:d000001.o:00000000 i .idata$4
libfoo.dll.a:d000001.o:00000000 i .idata$5
libfoo.dll.a:d000001.o:00000000 i .idata$6
libfoo.dll.a:d000001.o:00000000 i .idata$7
libfoo.dll.a:d000001.o:00000000 I __imp__foo
libfoo.dll.a:d000001.o:00000000 t .text
Likewise, the executable runs just fine in WinXP, (running within VirtualBox on the LMDE box, with ~/src/exp mapped as drive E: in the WinXP VM, and invoked from the MSYS shell):
$ /e/foo.exe
16
FWIW, I can reproduce your failure to create a runnable executable, when adding the -shared
attribute to the linker invocation; as you note, that is intended for creating DLLs, (which differ from executables in format only in having a different magic number embedded in the header; otherwise they are fundamentally the same).
In summary:
Don't specify
-shared
when linking the executable.Do qualify symbols to be exported from the executable with the
__declspec(dllexport)
attribute.Do specify the
-Wl,--out-implib=lib<exename>.dll.a
attribute, when linking the executable.
回答2:
Like Keith Marshall stated in the comments, -Wl,--out-implib
indeed works in combination with either:
-Wl,--export-all-symbols
by declaring symbols with
__declspec(dllexport)
or by providing a .def file
I went with the third option and wrote a bash script to generate a def file / version scripts on-the-fly to avoid exporting a lot of unneeded symbols.
The script can be found here.
Use it like:
export SYMBOLS_TO_EXPORT="*tools* *network* _Z8compressPvRjPKvjib ..." # use mangled names and skip leading underscores on i686
export HOSTPREFIX=i686-w64-mingw32 # unneeded on Windows
i686-w64-mingw32-g++ $(OBJS) `./gen_export_file $(OBJS)` -Wl,--out-implib=foo.exe.a -o foo.exe
来源:https://stackoverflow.com/questions/30465275/whats-the-proper-way-to-link-against-an-executable-on-windows