2013-07-11 42 views
6

Tôi đang gặp sự cố khi tạo thiết lập xây dựng cho phép các thư viện được chia sẻ trong cả Linux và Windows sử dụng gcc và MinGW tương ứng. Trong Linux, một thư viện chia sẻ không phải giải quyết tất cả các phụ thuộc tại thời gian biên dịch; trong khi đó, điều này có vẻ như trong Windows. Đây là thiết lập vấn đề:Tạo thư viện dùng gcc trên Linux và MinGW trên 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 

Bây giờ, trong Linux, đây biên dịch và chạy tốt:

$ 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! 

Trong Windows, chúng ta cần phải thay đổi như vậy để dll, đó là nhỏ và mịn:

$ 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 

Tuy nhiên, khi chúng tôi cố gắng xây dựng, chúng tôi nhận được lỗi sau:

$ 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 

Bây giờ, chúng ta có thể sửa lỗi bằng cách đơn giản bao gồm các đối tượng từ foo.o và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! 

Tuy nhiên, tôi không thích cách tiếp cận này kể từ libbar.dll bây giờ chứa ký tự cho cả foo và bar. Trong Linux, nó chỉ chứa các biểu tượng cho thanh. Sự tách biệt này rất quan trọng đối với các tình huống mà thư viện phụ thuộc vào một số thư viện số chuẩn như BLAS. Tôi muốn có thể triển khai thư viện được chia sẻ và có nó phụ thuộc vào phiên bản được tối ưu hóa của thư viện số trên máy của người dùng chứ không phải của riêng tôi.

Trong mọi trường hợp, thủ tục thích hợp để tạo thư viện được chia sẻ là nơi không có tất cả các ký hiệu có mặt tại thời gian biên dịch là gì?

Trong trường hợp quan trọng, tôi đã biên soạn các ví dụ này với gcc 4.6.3 trên Linux và mingw-get-inst-20120426.exe với gcc 4.7.2 trên Windows.

+0

Bạn đang thiếu '__declspec (dllimport) bắt buộc' và '__declspec (dllexport)' trong cả 'foo.h' và' bar.h'. Một cái gì đó như: '# nếu định nghĩa __ELF__ #define __attribute API ((tầm nhìn ("mặc định"))) #elif định nghĩa KHẨU #define API __declspec (dllexport) #else #define API __declspec (dllimport) #endif ' Sau đó' #define EXPORT' trong 'foo.c' và' bar.c'. – bit2shift

+0

Giống như [this] (https://github.com/bit2shift/r3dVoxel/blob/master/inc/r3dVoxel/r3vABI.hpp#L7-L13) nhưng không có 'extern" C "', là một cấu trúc C++. – bit2shift

Trả lời

8

Trên Windows, bạn cần tạo một thư viện nhập cho DLL. Một thư viện nhập khẩu trông giống như một thư viện tĩnh, ở chỗ nó định nghĩa tất cả các ký hiệu cần thiết, nhưng nó không có các thực thi chức năng thực tế, nó chỉ có sơ khai. Thư viện nhập sẽ giải quyết lỗi "không xác định tham chiếu" trong khi tránh liên kết tĩnh.

Để tạo thư viện nhập bằng MinGW, hãy làm theo hướng dẫn here. Điều quan trọng là khi xây dựng DLL, bạn phải chuyển tùy chọn -Wl,--out-implib,libexample_dll.a đến trình liên kết để tạo thư viện nhập libexample_dll.a.

Sau đó, khi bạn biên dịch tệp thực thi chính của mình, bạn sử dụng tùy chọn -lexample_dll (cùng với -L.) để liên kết với thư viện nhập.Vì vậy, với mã của bạn, tôi nghĩ rằng điều này sẽ làm việc:

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 

Ngoài ra, lưu ý rằng trên Windows, quy ước gọi cho các chức năng xuất khẩu trong DLL là hầu như luôn luôn __stdcall, không phải là mặc định __cdecl, vì vậy nếu bạn muốn DLL của bạn để có thể sử dụng được bởi các phần mềm khác, tôi khuyên bạn nên làm cho chúng __cdecl. Nhưng đó không phải là yêu cầu nghiêm ngặt, miễn là cả hai mã trong DLL và các tập tin tiêu đề đồng ý về những gì quy ước gọi là.

+2

Điều này làm việc tuyệt vời. Là một nhận xét nhỏ, tôi đã sử dụng: "gcc -shared bar.o -L. -lfoo -o libbar.dll -Wl, - out-implib, libbar.a" để sử dụng thư viện nhập. – wyer33

+0

Bạn đã nhận được phần quy ước gọi điện thoại là tất cả sai. Quy ước API Win32 là '__stdcall', ** nhưng đây không phải là quy ước được sử dụng nhiều nhất **,' __cdecl' là. Bạn nên sử dụng quy ước gọi trống (aka implicit '__cdecl') được ghép nối với' extern "C" 'để có kết quả tốt nhất: không mangling và không cần tệp .def (nên được coi là lỗi thời trong những ngày này trừ khi bạn đang làm thứ dơ bẩn). – bit2shift

+0

'__stdcall' chủ yếu được sử dụng một cách rõ ràng trong mã người dùng khi xác định callbacks để xử lý tin nhắn trong hệ thống phụ Win32 cửa sổ, sử dụng macro' CALLBACK'. – bit2shift