c++ - 在 Windows 上链接可执行文件的正确方法是什么?

标签 c++ windows mingw ld mingw-w64

我需要在插件中使用主可执行文件中的一些符号。

链接可执行文件会导致以下链接器错误:

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

现在,如果我使用 -shared -Wl,--export-all-symbols 构建主要可执行文件,然后链接到 test.exe 即可, 但是 Windows 加载程序(或者至少是 wine 加载程序)提示 test.exe 是一个 dll。

因此,我需要在没有 -shared 的情况下再次重新链接 test.exe,以便我能够运行 test.exe

即:

# 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++

这太黑客了,但最后我确实有了一个可以工作的插件......

回答我的问题:

是否有更好的方法来实现此目的(无需传递函数指针)?

我知道MSVC能够输出可执行文件的导入库,MinGW有类似的方法吗?

我尝试将 -Wl,--out-implib,test.a 添加到链接器标志中以获取可执行文件的导入库, 但链接可执行文件时 --out-implib 似乎被忽略。

最佳答案

在这种情况下,您可能确实想要限定 .exe 中的回调符号。 ,与 __declspec(dllexport)属性。在我的 Linux Mint Debian 机器上进行交叉编译,以下最小示例适用于我:

$ 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

这会生成两者一个工作可执行文件,一个导入库来映射其导出符号,以便在链接插件时使用只需在前面的命令中一次调用链接器即可(如在 wine 下运行可执行文件并使用 native linux nm 工具列出导入库时所见):

$ ~/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

同样,可执行文件在 WinXP 中运行得很好(在 LMDE 机器上的 VirtualBox 中运行,〜/src/exp 映射为 WinXP VM 中的驱动器 E:,并从 MSYS shell 调用):

$ /e/foo.exe
16

FWIW,在添加 -shared 时,我可以重现您创建可运行可执行文件的失败。链接器调用的属性;正如您所注意到的,这是为了创建 DLL(它与格式中的可执行文件的不同之处在 header 中嵌入了不同的魔数(Magic Number);否则它们基本上是相同的)。

总结:

  • 不要指定-shared链接可执行文件时。

  • 使用以下命令限定要从可执行文件导出的符号 __declspec(dllexport)属性。

  • 请指定 -Wl,--out-implib=lib<exename>.dll.a属性,当 链接可执行文件。

关于c++ - 在 Windows 上链接可执行文件的正确方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30465275/

相关文章:

c# - 如何找出哪个进程启动了我的exe?

g++ - i386-mingw32-g++ : error trying to exec 'cc1plus' : execvp: No such file or directory

c++ - Cpp/Cli 项目中的 Boost::Log 错误

c++ - "error : a nonstatic member reference must be relative to a specific object"是什么意思?

c++ - CUDA:有没有更快的写入全局内存的方法?

python - 如何使用 setuptools 为 windows 命令行安装 python 脚本?

c++ - 将字符串文字传递给 std::map::find(..)

windows - Jenkins 奴隶位置

windows - 如何在Windows上使用makefile创建libjson.lib?

assembly - 运行用 NASM 编写的 Win32 应用程序会导致 'This app cant run on your pc' 错误