2013-07-11 9 views
6

Mam problem z wygenerowaniem konfiguracji kompilacji, która umożliwia budowanie bibliotek współdzielonych w systemach Linux i Windows przy użyciu odpowiednio gcc i MinGW. W systemie Linux wspólna biblioteka nie musi rozwiązywać wszystkich zależności podczas kompilacji; wydaje się, że tak jest w przypadku Windows. Oto konfiguracja Problem:Tworzenie wspólnej biblioteki za pomocą gcc w systemach Linux i MinGW w systemie 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 

Teraz w Linuksie, to kompiluje i działa dobrze:

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

W Windows, musimy zmienić tak, aby dll, które jest niewielkie i dobrze:

$ 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 

Jednak, gdy próbujemy budować, pojawia się następujący błąd:

$ 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 

Teraz możemy naprawić ten błąd, po prostu w tym obiekty z foo.o do 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! 

Ja jednak nie podoba to podejście od libbar.dll teraz zawiera symbole zarówno dla foo jak i baru. W Linuksie zawiera tylko symbole paska. Ten rozdział jest ważny w sytuacjach, w których biblioteka zależy od pewnej standardowej biblioteki numerycznej, takiej jak BLAS. Chciałbym móc wdrożyć współdzieloną bibliotekę i uzależnić ją od zoptymalizowanej wersji biblioteki numerycznej na komputerze użytkownika, a nie mojej własnej.

W każdym razie, jaka jest właściwa procedura tworzenia wspólnej biblioteki, w której nie wszystkie symbole są obecne podczas kompilacji?

Jeśli jest to ważne, skompilowałem te przykłady z gcc 4.6.3 na Linux i mingw-get-inst-20120426.exe z gcc 4.7.2 na Windows.

+0

Brakuje wymaganych '__declspec (dllimport)' i '__declspec (dllexport)' zarówno w 'foo.h', jak i' bar.h'. Coś jak: '#if defined __ELF__ #define API __attribute ((widoczność ("Default"))) #elif określonym EXPORT #define API __declspec (dllexport) #else #define API __declspec (dllimport) #endif ' Następnie' #define EXPORT' w 'foo.c' i' bar.c'. – bit2shift

+0

Coś jak [this] (https://github.com/bit2shift/r3dVoxel/blob/master/inc/r3dVoxel/r3vABI.hpp#L7-L13), ale bez 'extern" C "', który jest konstrukcją C++. – bit2shift

Odpowiedz

8

W systemie Windows do biblioteki DLL należy utworzyć bibliotekę importu . Biblioteka importu wygląda jak biblioteka statyczna, ponieważ definiuje wszystkie potrzebne symbole, ale nie ma rzeczywistych implementacji funkcji, ma tylko kody pośredniczące. Biblioteka importu rozwiąże błędy "niezdefiniowanego odniesienia", unikając statycznego łączenia.

Aby utworzyć bibliotekę importu za pomocą MinGW, postępuj zgodnie z instrukcjami: here. Kluczem jest to, że budując bibliotekę DLL, należy przekazać opcję -Wl,--out-implib,libexample_dll.a do linkera, aby wygenerować bibliotekę importu libexample_dll.a.

Następnie, podczas kompilowania głównego pliku wykonywalnego, należy użyć opcji -lexample_dll (wraz z -L.) w celu połączenia z biblioteką importu.Więc z kodem, myślę, że to powinno działać:

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 

Należy również pamiętać, że w systemie Windows, konwencja powołaniem eksportowanych funkcji w DLL jest prawie zawsze __stdcall, nie domyślny __cdecl, więc jeśli chcesz, aby pliki DLL do może być użyty przez inne oprogramowanie, zaleciłbym je __cdecl. Ale nie jest to ściśle wymagane, o ile zarówno kod w pliku DLL, jak i pliki nagłówkowe zgadzają się co do konwencji wywołującej.

+2

To działało świetnie. Jako niewielki komentarz użyłem: "gcc -shared bar.o -L. -lfoo -o libbar.dll -Wl, - out-implib, libbar.a" w celu użycia biblioteki importu. – wyer33

+0

Masz część konwencji wywołującej wszystko źle. Konwencja Win32 API to '__stdcall', ** ale nie jest to najczęściej używana konwencja **,' __cdecl'. Powinieneś użyć pustej konwencji wywoływania (inaczej niejawny '__cdecl') w połączeniu z' extern "C" ', aby uzyskać najlepsze wyniki: bez zniekształcania i bez potrzeby plików .def (które powinny być uważane za przestarzałe w dzisiejszych czasach, chyba że robisz paskudne rzeczy). – bit2shift

+0

'__stdcall' jest głównie używany jawnie w kodzie użytkownika podczas definiowania wywołań zwrotnych dla obsługi komunikatów w podsystemie okna Win32, używając makra' CALLBACK'. – bit2shift