I'm having trouble with generating a build setup that allows shared libraries to be built in both Linux and Windows using gcc and MinGW, respectively. In Linux, a shared library doesn't have to resolve all dependencies at compile time; whereas, this appears to the case in Windows. Here is the problem setup:
我在生成构建设置方面遇到了麻烦,该构建设置允许分别使用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
Now, in Linux, this compiles and runs just fine:
现在,在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!
In Windows, we need to change so to dll, which is minor and fine:
在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
However, when we try to build, we get the following error:
但是,当我们尝试构建时,会出现以下错误:
$ 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
Now, we can fix the error by simply including the objects from foo.o into libbar.dll:
现在,我们可以通过简单地将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!
However, I don't like this approach since libbar.dll now contains symbols for both foo and bar. In Linux, it only contains symbols for bar. This separation is important for situations where a library depends on some standard numerical library like BLAS. I'd like to be able to deploy the shared library and have it depend on the optimized version of the numerical library on the user's machine and not my own.
但是,我不喜欢这种方法,因为libbar.dll现在包含foo和bar的符号。在Linux中,它只包含bar的符号。这种分离对于库依赖于某些标准数值库(如BLAS)的情况非常重要。我希望能够部署共享库并使其依赖于用户计算机上的数值库的优化版本,而不是我自己的。
In any case, what's the proper procedure to create a shared library where not all of the symbols are present at compile time?
在任何情况下,创建共享库的正确过程是什么,在编译时并不是所有符号都存在?
In case it matters, I compiled these examples with gcc 4.6.3 on Linux and mingw-get-inst-20120426.exe with gcc 4.7.2 on Windows.
如果重要的话,我在Linux上使用gcc 4.6.3编译这些示例,在Windows上使用gcc 4.7.2编译mingw-get-inst-20120426.exe。
1 个解决方案
#1
8
On Windows, you need to create an import library for the DLL. An import library looks like a static library, in that it defines all of the needed symbols, but it doesn't have the actual function implementations, it just has stubs. The import library will resolve the "undefined reference" errors while avoiding static linking.
在Windows上,您需要为DLL创建导入库。导入库看起来像一个静态库,因为它定义了所有需要的符号,但它没有实际的函数实现,它只有存根。导入库将解决“未定义的引用”错误,同时避免静态链接。
To create an import library with MinGW, follow the instructions here. The key is that when building the DLL, you must pass the option -Wl,--out-implib,libexample_dll.a
to the linker to generate the import library libexample_dll.a
.
要使用MinGW创建导入库,请按照此处的说明进行操作。关键是在构建DLL时,必须将选项-Wl, - out-implib,libexample_dll.a传递给链接器以生成导入库libexample_dll.a。
Then, when you compile your main executable, you use the -lexample_dll
option (along with -L.
) to link against the import library. So with your code, I think this should work:
然后,在编译主可执行文件时,使用-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
Also, note that on Windows, the calling convention for exported functions in DLL is almost always __stdcall
, not the default __cdecl
, so if you want your DLLs to be usable by other software, I'd recommend making them __cdecl
. But that's not strictly requires, as long as both the code in the DLL and the header files agree on what the calling convention is.
另请注意,在Windows上,DLL中导出函数的调用约定几乎总是__stdcall,而不是默认的__cdecl,因此如果您希望其他软件可以使用您的DLL,我建议将它们设为__cdecl。但这并不严格要求,只要DLL中的代码和头文件都同意调用约定。
#1
8
On Windows, you need to create an import library for the DLL. An import library looks like a static library, in that it defines all of the needed symbols, but it doesn't have the actual function implementations, it just has stubs. The import library will resolve the "undefined reference" errors while avoiding static linking.
在Windows上,您需要为DLL创建导入库。导入库看起来像一个静态库,因为它定义了所有需要的符号,但它没有实际的函数实现,它只有存根。导入库将解决“未定义的引用”错误,同时避免静态链接。
To create an import library with MinGW, follow the instructions here. The key is that when building the DLL, you must pass the option -Wl,--out-implib,libexample_dll.a
to the linker to generate the import library libexample_dll.a
.
要使用MinGW创建导入库,请按照此处的说明进行操作。关键是在构建DLL时,必须将选项-Wl, - out-implib,libexample_dll.a传递给链接器以生成导入库libexample_dll.a。
Then, when you compile your main executable, you use the -lexample_dll
option (along with -L.
) to link against the import library. So with your code, I think this should work:
然后,在编译主可执行文件时,使用-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
Also, note that on Windows, the calling convention for exported functions in DLL is almost always __stdcall
, not the default __cdecl
, so if you want your DLLs to be usable by other software, I'd recommend making them __cdecl
. But that's not strictly requires, as long as both the code in the DLL and the header files agree on what the calling convention is.
另请注意,在Windows上,DLL中导出函数的调用约定几乎总是__stdcall,而不是默认的__cdecl,因此如果您希望其他软件可以使用您的DLL,我建议将它们设为__cdecl。但这并不严格要求,只要DLL中的代码和头文件都同意调用约定。