在Linux上使用gcc和在Windows上使用MinGW构建共享库

时间:2013-07-11 19:38:36

标签: c linux windows gcc mingw

我在生成构建设置时遇到问题,该构建设置允许分别使用gcc和MinGW在Linux和Windows中构建共享库。在Linux中,共享库不必在编译时解析所有依赖项;然而,在Windows中出现这种情况。以下是问题设置:


$ cat foo.h 
#ifndef FOO_H
#define FOO_H
void printme();
#endif

$ cat foo.c
#include "foo.h"
#include <stdio.h>
void printme() {
    printf("Hello World!\n");
}

$ cat bar.h
#ifndef BAR_H
#define BAR_H
void printme2();
#endif

$ cat bar.c
#include "bar.h"
#include "foo.h"
void printme2() {
    printme();
    printme();
}

$ cat main.c
#include "bar.h"
int main(){
    printme2();
}

$ cat Makefile 
.c.o:
        gcc -fPIC -c $<

all: foo.o bar.o main.o
        gcc -shared foo.o -o libfoo.so
        gcc -shared bar.o -o libbar.so
        gcc main.o  -Wl,-rpath=. -L . -lbar -lfoo -o main

现在,在Linux中,这个编译并运行得很好:

$ make
gcc -fPIC -c foo.c
gcc -fPIC -c bar.c
gcc -fPIC -c main.c
gcc -shared foo.o -o libfoo.so
gcc -shared bar.o -o libbar.so
gcc main.o  -Wl,-rpath=. -L . -lbar -lfoo -o main

$ ./main 
Hello World!
Hello World!

在Windows中,我们需要将其更改为dll,这是次要和精细的:

$ cat Makefile 
.c.o:
        gcc -fPIC -c $<

all: foo.o bar.o main.o
        gcc -shared foo.o -o libfoo.dll
        gcc -shared bar.o -o libbar.dll
        gcc main.o  -Wl,-rpath=. -L . -lbar -lfoo -o main

但是,当我们尝试构建时,会出现以下错误:

$ make
gcc -fPIC -c foo.c
foo.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -fPIC -c bar.c
bar.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -fPIC -c main.c
main.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -shared foo.o -o libfoo.dll
gcc -shared bar.o -o libbar.dll
bar.o:bar.c:(.text+0x7): undefined reference to `printme'
bar.o:bar.c:(.text+0xc): undefined reference to `printme'
collect2.exe: error: ld returned 1 exit status
make: *** [all] Error 1

现在,我们可以通过简单地将foo.o中的对象包含到libbar.dll中来修复错误:

$ cat Makefile 
.c.o:
        gcc -fPIC -c $<

all: foo.o bar.o main.o
        gcc -shared foo.o -o libfoo.dll
        gcc -shared bar.o foo.o -o libbar.dll
        gcc main.o  -Wl,-rpath=. -L . -lbar -lfoo -o main

$ make
gcc -fPIC -c foo.c
foo.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -fPIC -c bar.c
bar.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -fPIC -c main.c
main.c:1:0: warning: -fPIC ignored for target (all code is position independent) [enabled by default]
gcc -shared foo.o -o libfoo.dll
gcc -shared bar.o foo.o -o libbar.dll
gcc main.o -Wl,-rpath=. -L . -lbar -lfoo -o main 

$ ./main 
Hello World!
Hello World!

但是,我不喜欢这种方法,因为libbar.dll现在包含foo和bar的符号。在Linux中,它只包含bar的符号。这种分离对于库依赖于某些标准数值库(如BLAS)的情况非常重要。我希望能够部署共享库并使其依赖于用户计算机上的数值库的优化版本而不是我自己的版本。

在任何情况下,创建共享库的正确步骤是什么,而编译时并不存在所有符号?

如果重要,我在Linux上使用gcc 4.6.3编译这些示例,在Windows上使用gcc 4.7.2编译mingw-get-inst-20120426.exe。

1 个答案:

答案 0 :(得分:12)

在Windows上,您需要为DLL创建导入库。导入库看起来像一个静态库,因为它定义了所有需要的符号,但它没有实际的函数实现,它只是有存根。导入库将解决“未定义的引用”错误,同时避免静态链接。

要使用MinGW创建导入库,请按照here的说明操作。关键是在构建DLL时,必须将选项-Wl,--out-implib,libexample_dll.a传递给链接器以生成导入库libexample_dll.a

然后,在编译主可执行文件时,使用-lexample_dll选项(以及-L.)链接导入库。因此,使用您的代码,我认为这应该有效:

all: foo.o bar.o main.o
    gcc -shared foo.o -o libfoo.dll -Wl,--out-implib,libfoo.a
    gcc -shared bar.o foo.o -o libbar.dll -Wl,--out-implib,libbar.a
    gcc main.o  -Wl,-rpath=. -L. -lbar -lfoo -o main

另外,请注意,在Windows上,DLL中导出函数的调用约定几乎总是__stdcall,而不是默认__cdecl,因此如果您希望其他软件可以使用您的DLL,我建议他们__cdecl。但这并不严格要求,只要DLL中的代码和头文件都同意调用约定即可。