Erstellen einer gemeinsam genutzten Bibliothek mit gcc unter Linux und MinGW unter Windows

Lesezeit: 6 Minuten

Ich habe Probleme beim Generieren eines Build-Setups, mit dem gemeinsam genutzte Bibliotheken sowohl unter Linux als auch unter Windows mit gcc bzw. MinGW erstellt werden können. Unter Linux muss eine gemeinsam genutzte Bibliothek nicht alle Abhängigkeiten zur Kompilierzeit auflösen; während dies in Windows der Fall zu sein scheint. Hier die Problemstellung:


$ 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

Nun, unter Linux wird dies kompiliert und läuft einwandfrei:

$ 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 müssen wir so zu dll ändern, was klein und fein ist:

$ 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

Wenn wir jedoch versuchen zu bauen, erhalten wir die folgende Fehlermeldung:

$ 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

Jetzt können wir den Fehler beheben, indem wir einfach die Objekte aus foo.o in libbar.dll einfügen:

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

Ich mag diesen Ansatz jedoch nicht, da libbar.dll jetzt Symbole für foo und bar enthält. Unter Linux enthält es nur Symbole für Balken. Diese Trennung ist wichtig für Situationen, in denen eine Bibliothek von einer standardmäßigen numerischen Bibliothek wie BLAS abhängt. Ich möchte in der Lage sein, die gemeinsam genutzte Bibliothek bereitzustellen und sie von der optimierten Version der numerischen Bibliothek auf dem Computer des Benutzers und nicht von meiner eigenen abhängig zu machen.

Was ist in jedem Fall das richtige Verfahren zum Erstellen einer gemeinsam genutzten Bibliothek, in der nicht alle Symbole zur Kompilierzeit vorhanden sind?

Falls es darauf ankommt, ich habe diese Beispiele mit gcc 4.6.3 unter Linux und mingw-get-inst-20120426.exe mit gcc 4.7.2 unter Windows kompiliert.

  • Ihnen fehlt das Erforderliche __declspec(dllimport) Und __declspec(dllexport) sowohl foo.h Und bar.h. Etwas wie: #if defined __ELF__ #define API __attribute((visibility("default"))) #elif defined EXPORT #define API __declspec(dllexport) #else #define API __declspec(dllimport) #endif Dann #define EXPORT In foo.c Und bar.c.

    – bit2shift

    13. September 2017 um 2:58 Uhr

  • Etwas wie Das aber ohne extern "C"bei dem es sich um ein C++-Konstrukt handelt.

    – bit2shift

    13. September 2017 um 3:01 Uhr

Unter Windows müssen Sie eine erstellen Bibliothek importieren für die DLL. Eine Importbibliothek sieht aus wie eine statische Bibliothek, da sie alle erforderlichen Symbole definiert, aber nicht über die tatsächlichen Funktionsimplementierungen verfügt, sondern nur über Stubs verfügt. Die Importbibliothek löst die Fehler „undefinierte Referenz“ und vermeidet statische Verknüpfungen.

Um eine Importbibliothek mit MinGW zu erstellen, folgen Sie den Anweisungen Hier. Der Schlüssel ist, dass Sie beim Erstellen der DLL die Option übergeben müssen -Wl,--out-implib,libexample_dll.a an den Linker, um die Importbibliothek zu generieren libexample_dll.a.

Wenn Sie dann Ihre ausführbare Hauptdatei kompilieren, verwenden Sie die -lexample_dll Möglichkeit (zusammen mit -L.) zum Verknüpfen mit der Importbibliothek. Also mit Ihrem Code, ich denke, das sollte funktionieren:

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

Beachten Sie außerdem, dass unter Windows die Aufrufkonvention für exportierte Funktionen in DLL fast immer lautet __stdcallnicht die Standardeinstellung __cdeclWenn Sie also möchten, dass Ihre DLLs von anderer Software verwendet werden können, würde ich empfehlen, sie zu erstellen __cdecl. Dies ist jedoch nicht unbedingt erforderlich, solange sowohl der Code in der DLL als auch die Header-Dateien sich auf die Aufrufkonvention einigen.

  • Das hat super funktioniert. Als kleinen Kommentar habe ich verwendet: “gcc -shared bar.o -L . -lfoo -o libbar.dll -Wl,–out-implib,libbar.a”, um die Importbibliothek zu verwenden.

    – wyer33

    11. Juli 2013 um 20:10 Uhr


  • Du hast den Calling-Convention-Teil komplett falsch verstanden. Die Win32-API-Konvention lautet __stdcall, aber dies ist nicht die am häufigsten verwendete Konvention, __cdecl Ist. Sie sollten eine leere Aufrufkonvention verwenden (auch bekannt als implizit __cdecl) gepaart mit extern "C" um die besten Ergebnisse zu erzielen: kein Verstümmeln und keine Notwendigkeit für .def-Dateien (die heutzutage als veraltet gelten sollten, es sei denn, Sie machen böse Sachen).

    – bit2shift

    13. September 2017 um 2:43 Uhr

  • __stdcall wird hauptsächlich explizit im Benutzercode verwendet, wenn Rückrufe für die Nachrichtenbehandlung im Win32-Fenstersubsystem definiert werden, indem die CALLBACK Makro.

    – bit2shift

    13. September 2017 um 2:47 Uhr


  • Mit MinGW wurde die Notwendigkeit von Importbibliotheken entfernt. Sie können eine ausführbare Datei direkt mit einer DLL verknüpfen, die mit MinGW kompiliert wurde.

    – bit2shift

    13. September 2017 um 2:52 Uhr

1443550cookie-checkErstellen einer gemeinsam genutzten Bibliothek mit gcc unter Linux und MinGW unter Windows

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy