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

时间:2015-05-26 17:37:55

标签: c++ windows mingw ld mingw-w64

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

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

.mcwindow{
position: fixed;
overflow: hidden;
border: 4px solid #80d0ff;
border-top:0px;
}

.wm{
background: #80d0ff;
width:100%;
height:30px;
}

现在,如果我使用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就可以了, 但Windows加载器(或至少是葡萄酒加载器)抱怨test.exe是一个dll。

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

即:

test.exe

那是超级hackish,但最后我确实有一个有效的插件......

来回答我的问题:

有没有更好的方法来实现这一点(不传递函数指针)?

我知道# 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

2 个答案:

答案 0 :(得分:3)

在这种情况下,你可能想要使用.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下运行可执行文件时所见,并使用本机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映射为驱动器E:在WinXP VM中,并从MSYS shell调用):

$ /e/foo.exe
16

FWIW,在将-shared属性添加到链接器调用时,我可以重现您创建可运行的可执行文件的失败;正如您所注意到的那样,它用于创建DLL(与标题格式的可执行文件不同,标题中嵌入了不同的幻数;否则它们基本相同)。

总结:

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

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

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

答案 1 :(得分:0)

与评论中所述的 Keith Marshall 一样,-Wl,--out-implib确实可以与以下两者结合使用:

  • -Wl,--export-all-symbols

  • 通过使用__declspec(dllexport)

  • 声明符号
  • 或提供.def文件

我选择了第三个选项并编写了一个bash脚本来动态生成def文件/版本脚本,以避免导出大量不需要的符号。

可以找到脚本here

使用它像:

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