*Markus: Dynamische Bibliotheken fest einkompilieren?

Moin,

Ich habe mich gefragt wie man es anstellen kann, .cpp- oder .c-Dateien so zu kompilieren, damit ein Benutzer, der in meinem Fall SDL nicht auf seinem System verfügbar hat, die gewünschte Datei trotzdem ausführen kann.
Dabei fand ich durch Google Hinweise auf "-static", welches man vor die Eingabe der dynamische Bibliothek setzen muss. Wie auch immer, ich fand keine Hinweise auf die Syntax, wie es zu verwenden ist. Durch Herumprobieren entstanden immer nur Fehlermeldungen und in der g++-Hilfe stand auch nichts über static.
Wie kann man also SDL fest einkompilieren? Dabei würde mich der Lösungsweg für Linux und Windows interessieren.

Markus.

--
http://www.apostrophitis.at
Maschiene währe Standart Gallerie vorraus Packete Objeckte tollerant vieleicht
  1. Hallo Markus,

    zu Deinem speziellen Problem kann ich Dir leider nichts sagen.

    Allgemein gilt jedoch, daß man .DLLs nicht einfach statisch einbinden kann.

    Wenn man aber eine Linkerbibiliothek (die hat unter Windows üblicherweise die Endung .lib) hat, die (mindestens) die in der .DLL enthaltenen Routinen als Objektmodule enthält, kann man die .lib in die Parameterliste des Linkers aufnehmen. Der holt sich dann nur diejenigen Module heraus, die vom jeweiligen Programm tatsächlich benötigt werden und bindet sie statisch dazu.

    Einzelheiten dazu sind den jeweiligen Linkerbeschreibungen zu entnehmen.

    Gruß,

    Houyhnhnm

    1. Hallo,

      Allgemein gilt jedoch, daß man .DLLs nicht einfach statisch einbinden kann.

      jein - wenn man sie nur in compilierter Form vorliegen hat, dann hast du natürlich Recht. Hat man die Bibliothek dagegen im Quellcode, kann man sie selbstverständlich statisch eincompilieren, anstatt sie separat als DLL zu übersetzen/linken.

      Wenn man aber eine Linkerbibiliothek (die hat unter Windows üblicherweise die Endung .lib) hat, die (mindestens) die in der .DLL enthaltenen Routinen als Objektmodule enthält, kann man die .lib in die Parameterliste des Linkers aufnehmen. Der holt sich dann nur diejenigen Module heraus, die vom jeweiligen Programm tatsächlich benötigt werden und bindet sie statisch dazu.

      Korrekt. Der eigentliche Programmcode bleibt aber in der DLL, deshalb ist diese nachher für die Programmausführung notwendig. Denn der Linker erzeugt nur einen "stub" für jede Funktion in der DLL, quasi eine Art Referenz. Er kann nicht den tatsächlichen Programmcode aus der DLL nehmen und in das erzeugte Programm einbauen.

      Schönen Sonntag noch,
       Martin

      --
      Ich liebe Politiker auf Wahlplakaten.
      Sie sind tragbar, geräuschlos, und leicht wieder zu entfernen.
        (Loriot, deutscher Satiriker)
      1. Hallo Martin

        Korrekt. Der eigentliche Programmcode bleibt aber in der DLL, deshalb ist diese nachher für die Programmausführung notwendig. Denn der Linker erzeugt nur einen "stub" für jede Funktion in der DLL, quasi eine Art Referenz. Er kann nicht den tatsächlichen Programmcode aus der DLL nehmen und in das erzeugte Programm einbauen.

        Das ist nicht richtig.

        Auch wenn die Bibliothek als .dll vorliegt, braucht der Linker natürlich eine .lib, in der die Stubs für den Laufzeitlinker (der zur Laufzeit des Programmes dynamisch die .DLLs anbindet) enthalten sind. Dies ist der Fall der dynamischen Bindung.

        Statische Bindung der Bibliothek bedeutet jedoch etwas anderes:

        Der Hersteller der Bibliothek erzeugt einen .lib-File, in dem keine Stubs stehen, sondern vollständige Objektmodule mit dem (relokierbaren) Code der Bibliothek. Der Linker durchsucht dann, nachdem er die .obj-Module eingelesen hat, die Bibliothek nach Modulen, die Referenzen definieren, die noch nicht aufgelöst wurden und bindet dann den Modul komplett in das ausführbare Programm ein.

        Der Vorteil dieser Methode gegenüber der einfachen Übernahme der Liste der Bibliotheks-Quellmodule in die Compilerparameter besteht darin, daß der Programmierer nicht erst mühsam herausfinden muß, welche Module der Bibliothek tatsächlich benötigt werden, um eine kompakte ausführbare Datei zu bekommen und daß die Übersetzungszeit für die Bibliothek - die bei umfangreichen Exemplaren leicht ein vielfaches der Übersetzungszeit für eine Anwendung erreicht - entfällt.

        Gruß,

        Houyhnhnm

        1. Moin,

          Korrekt. Der eigentliche Programmcode bleibt aber in der DLL, deshalb ist diese nachher für die Programmausführung notwendig. Denn der Linker erzeugt nur einen "stub" für jede Funktion in der DLL, quasi eine Art Referenz. Er kann nicht den tatsächlichen Programmcode aus der DLL nehmen und in das erzeugte Programm einbauen.
          Das ist nicht richtig.

          'tschuldigung, ich sprach ganz unbewusst von Windows-Executables bzw. vom DLL-Prinzip unter Windows. Unter der Voraussetzung ist das Gesagte durchaus richtig.

          Auch wenn die Bibliothek als .dll vorliegt, braucht der Linker natürlich eine .lib, in der die Stubs für den Laufzeitlinker (der zur Laufzeit des Programmes dynamisch die .DLLs anbindet) enthalten sind. Dies ist der Fall der dynamischen Bindung.

          Ja, richtig. Das ist noch kein Widerspruch zu dem, was ich gesagt habe. Die Stubs, die er in der Importbibliothek findet, baut er dann in das Executable ein.

          Statische Bindung der Bibliothek bedeutet jedoch etwas anderes:

          Der Hersteller der Bibliothek erzeugt einen .lib-File, in dem keine Stubs stehen, [...] und bindet dann den Modul komplett in das ausführbare Programm ein.

          Auch völlig richtig. Ist aber auch kein Widerspruch zu meiner Darstellung. Ich ging vom typischen Fall unter Windows aus, dass man eine Bibliothek als fertig übersetzte DLL hat, und zum Linken nur eine Importbibliothek dazu.

          Der Vorteil dieser Methode gegenüber der einfachen Übernahme [...]

          Ist mir alles bekannt, danke. Jedoch sind mir in meiner nun bald zehnjährigen Erfahrung mit Win16/Win32-Programmierung nur immer die beiden von mir geschilderten Fälle begegnet:
           a) Codebibliothek als DLL mit Importbibliothek als .lib (nur Stubs)
           b) Bibliothek komplett im Quelltext
          Im Fall a) habe ich als Programmierer keine Wahl - ich *muss* dynamische Bindung verwenden. Ich kann nur noch wählen, ob ich die DLL schon beim Start meines Programms durch Windows laden lasse, oder ob ich das selbst mit LoadLibrary(), GetProcAddress() usw. mache.
          Bei b) habe ich alle Freiheiten, wie ich die Bibliothek in mein Projekt einbinde. Ich kann sie getrennt übersetzen und eine .lib draus machen, ich kann eine DLL erzeugen und dann nach Fall a) verfahren, oder ich kann sie im Quellcode direkt in mein eigenes Programm einbinden.

          Sind damit unsere unterschiedlichen Ansichten geklärt? Oder stimmt deiner Ansicht nach immer noch irgendwas nicht? ;-)

          Schönen Sonntag noch,
           Martin

          --
          Schildkröten können mehr über den Weg berichten als Hasen.
          1. Hallo Martin,

            das sind keine unterschiedlichen Ansichten und ich will Dir Deine auch nicht ausreden. Aber Dein Wissen in der Sache ist unvollständig.

            Ich bin auf diesem Gebiet wirklich ein Experte und kenne das Innenleben von .o, .obj und .lib-Files aus eigener Anschauung.

            In Zeiten, als Assemblerprogrammierung noch üblich war, wußte jeder Informatiker über das Thema recht gut bescheid. Mittlerweile wurden aber mehrere Sofwareschichten über diese Primitivebene gelegt und das Wissen über die Grundlagen geht bei den Anwendern der Entwicklungspakete verloren.

            Nichts desto trotz findet man die Uraltstrukturen überall 'ganz tief unten drin' wieder - im Gegensatz zu den zugehörigen Dokumentationen; die gibts bestenfalls noch antiquarisch.

            Gruß,

            Houyhnhnm

            1. Hi,

              das sind keine unterschiedlichen Ansichten und ich will Dir Deine auch nicht ausreden. Aber Dein Wissen in der Sache ist unvollständig.

              das klingt für mich alarmierend, denn ich bin normalerweise auch jemand, der sich nach Möglichkeit bis zum letzten Bit in eine Sache reinkniet, und ich schreibe mir eigentlich auch fundierte Kenntnisse über Windows-Interna wie auch über Assembler-Programmierung auf die Fahnen.
              Wenn du sagst, mein Wissen sei unvollständig, dann sag mir bitte auch, in welchen Punkten. Versuche, meine Wissenslücken aufzufüllen bzw. mich an Quellen zu verweisen, die das können. Denn nur die Antwort "dein Wissen ist unvollständig" ist absolut unbefriedigend.

              Ich bin auf diesem Gebiet wirklich ein Experte und kenne das Innenleben von .o, .obj und .lib-Files aus eigener Anschauung.

              Das hätte ich von mir auch behauptet - zumindest auf der DOS/Windows-Schiene.

              In Zeiten, als Assemblerprogrammierung noch üblich war, wußte jeder Informatiker über das Thema recht gut bescheid. Mittlerweile wurden aber mehrere Sofwareschichten über diese Primitivebene gelegt und das Wissen über die Grundlagen geht bei den Anwendern der Entwicklungspakete verloren.

              Ja, leider. Trotzdem greife auch ich noch häufig zum Assembler und habe keine Hemmungen, tief ins System einzugreifen. Früher auch gern mit undokumentierten Tricks, heute machen mir die eher Bauchschmerzen, weil man sich damit bezüglich der Kompatibilität und Wartbarkeit auf sehr dünnes Eis begibt.

              Nichts desto trotz findet man die Uraltstrukturen überall 'ganz tief unten drin' wieder - im Gegensatz zu den zugehörigen Dokumentationen; die gibts bestenfalls noch antiquarisch.

              Ja, leider. ;-)

              So long,
               Martin

              --
              Die meisten Menschen werden früher oder später durch Computer ersetzt.
              Für manche würde aber auch schon ein einfacher Taschenrechner genügen.
              1. Hallo Martin,

                die Sache ist eigentlich sehr einfach: .LIBs bestehen aus einem Library Header und einer einfachen Konkatenation von .obj-Modulen (in dem Format, wie sie z.B. mit -c aus dem Compiler kommen). Optional steht am Ende noch ein Symbolverzeichnis, das dem Linker erspart, alle enthaltenen Module sequentiell zu durchsuchen.

                Eine Import-Library enthält eigentlich nur für jedes Codesymbol einen Sprung auf ein externes Symbol und die zusätzliche Angabe für den Linker, in welcher .DLL das Symbol zu finden ist. Der Stub besteht also meist nur aus einem einzelnen jmp-Maschinenbefehl. Man findet den, wenn man mit einem Assembler-Debugger maschinenbefehlsweise den Aufruf einer externen Funktion verfolgt. Die interne Struktur ist wie oben beschrieben.

                Eine statische Bibiliothek enthält - statt der Stubs (Stümpfe) - vollständige .obj-Module, wie sie beim Compilieren einer .cpp-Datei entstehen.

                Bibliotheken werden von Bibliotheksverwaltern aus .obj-Modulen erzeugt. Importbibliotheken werden meist mit speziellen Programmen erzeugt, die z.B. eine .DLL einlesen und die zugehörige Importbibliothek erzeugen; zuweilen werden sie aber auch vom Linker zusammen mit der .DLL produziert.

                Das heißt insbesondere, man kann Teile seiner eigenen Programme, die sich nur selten ändern, oder die in mehreren Programmen identisch benötigt werden in eine Bibliothek packen und spart sich dann sowohl die Übersetzungszeiten, als auch eventuell lange Modullisten für den Linker.

                Die Sache wird dadurch noch weiter vereinfacht und optimiert, daß der Linker sich aus jeder Bibliothek nur diejenigen Module herauspickt, die auch tatsächlich benötigt werden. Man muß sich also nicht überlegen, welche Teile einer Bibliothek tatsächlich benötigt werden.

                Das .obj-Format selbst enthält unübersehbar noch die alten Segmentierungsmechanismen aus der Zeit der 16-Bit-Segmente, nach und nach aufgebohrt für die die moderneren Hardware-Anforderungen - alte Zöpfe, die die Sache für jemanden, der damit nicht vertraut ist, ziemlich unübersichtlich machen und ungeahnte Fehlermöglichkeiten bergen. (Zum Glück muß sich ein normaler Entwickler mit sowas nie befassen.)

                Ansonsten ist es einfach eine Konkatenation von einzelnen Records, die verschiedene Arten von Information enthalten. Man kann die Teile zerlegen (dazu gibt es Programme) und untersuchen.

                Gruß,

                Houyhnhnm

                1. Hallo Houyhnhnm,

                  danke für deine sehr ausführliche Darstellung.
                  Allerdings enthielt sie nichts, was mir nicht auch schon vertraut war.

                  Was war jetzt der Grund unseres Missverständnisses?
                  Etwa das hier: Du hattest eingangs behauptet, dass man DLLs nicht statisch einbinden kann, und ich hatte dir zugestimmt, jedoch mit der Ergänzung, dass man das natürlich könnte, wenn man den Quelltext zu dieser DLL hat. Im nächsten Absatz hatte ich nur noch darauf hingewiesen, dass der Linker nicht den fertigen, in einer DLL vorliegenden Code extrahieren und statisch einbinden kann. Hattest du an dieser Stelle eine .lib anstatt einer DLL im Sinn, als du im Folgeposting sagtest, das sei nicht richtig?

                  Denn eigentlich scheinen wir die ganze Zeit das gleiche zu meinen, deshalb würde ich gern wissen, wo unser Verständnisproblem liegt.

                  So long,
                   Martin

                  --
                  Computer lösen für uns Probleme, die wir ohne sie gar nicht hätten.
                  1. Hallo Martin,

                    ich hatte ursprünglich geschrieben:

                    Wenn man aber eine Linkerbibiliothek (die hat unter Windows üblicherweise die Endung .lib) hat, die (mindestens) die in der .DLL enthaltenen Routinen als Objektmodule enthält, kann man die .lib in die Parameterliste des Linkers aufnehmen. Der holt sich dann nur diejenigen Module heraus, die vom jeweiligen Programm tatsächlich benötigt werden und bindet sie statisch dazu.

                    Das bezieht sich auf den statischen Bindeprozeß mit einer .lib-Datei.

                    Deine Antwort scheint dazu nicht zu passen:

                    Korrekt. Der eigentliche Programmcode bleibt aber in der DLL, deshalb ist diese nachher für die Programmausführung notwendig. Denn der Linker erzeugt nur einen "stub" für jede Funktion in der DLL, quasi eine Art Referenz. Er kann nicht den tatsächlichen Programmcode aus der DLL nehmen und in das erzeugte Programm einbauen.

                    Das beschreibt die Vorbereitungen des Linkers für die dynamische Bindung zur Laufzeit der erzeugten Codedatei - nicht die statische Bindung, denn die benötigt zur Laufzeit eben gerade keine .DLL.

                    Vielleicht hat mein Einschub in der Klammer, gefolgt vom Nebensatz zu einem Parserstackoverflow geführt... Im zweiten Satz könnte der Bezug als Folgefehler verloren gegangen sein...

                    Gruß,

                    Houyhnhnm

                    1. Hallo,

                      jetzt ist mir der Knackpunkt klar, danke.

                      ich hatte ursprünglich geschrieben:

                      Wenn man aber eine Linkerbibiliothek (die hat unter Windows üblicherweise die Endung .lib) hat, die (mindestens) die in der .DLL enthaltenen Routinen ...

                      Dieser Satz von dir folgte aber auf eine Anmerkung über DLLs, so dass ich deine "Linkerbibliothek (.lib)" hier ganz automatisch als Importbibliothek zur DLL verstanden habe, nicht als vollständige statische Lib.
                      Deswegen lief unsere Argumentation ab diesem Punkt auseinander.

                      Das bezieht sich auf den statischen Bindeprozeß mit einer .lib-Datei.
                      Deine Antwort scheint dazu nicht zu passen:

                      Korrekt. Der eigentliche Programmcode bleibt aber in der DLL, deshalb ist diese nachher für die Programmausführung notwendig ...

                      Doch, die Antwort hätte gepasst - für die zur DLL gehörende Import-Bibliothek, die ihrerseits ja beim statischen Linken mit eingebunden wird. ;-)

                      Vielleicht hat mein Einschub in der Klammer, gefolgt vom Nebensatz zu einem Parserstackoverflow geführt... Im zweiten Satz könnte der Bezug als Folgefehler verloren gegangen sein...

                      Nee, das war kein Stack Overflow; ich bin beim Parsen auch komplexer deutscher Sätze -soweit sie syntaktisch keine groben Fehler aufweisen- eigentlich recht treffsicher. Hier war es eher ein verpasster Context Switch.

                      Schönen Sonntag noch,
                       Martin

                      --
                      Die meisten Menschen werden früher oder später durch Computer ersetzt.
                      Für manche würde aber auch schon ein einfacher Taschenrechner genügen.
    2. Hallo,

      aha, ich verstehe. Ich wusste nicht, dass es doch so kompliziert ist. Was ich dabei nicht verstehe, ist, warum beispielsweise <iostream>, <stdio.h>, o.ä. Bibliotheken in das Programm einkompiliert werden, ohne dass sich der Endbenutzer des Programms explizit darum kümmern muss, dass er dieses erforderlichen Bibliotheken auf seinem System hat, wohingegen das bei SDL.h nicht funktioniert. Würde ich versuchen mit g++ programm.cpp -o programm das Programm zu kompilieren, kann der Compiler SDL.h ohne direkte Angabe von -lSDL nicht einkompilieren.
      Ich verstehe den Unterschied hierbei nicht?

      Markus.

      --
      http://www.apostrophitis.at
      Maschiene währe Standart Gallerie vorraus Packete Objeckte tollerant vieleicht
      1. Hallo Markus,

        Was ich dabei nicht verstehe, ist, warum beispielsweise <iostream>, <stdio.h>, o.ä. Bibliotheken in das Programm einkompiliert werden, ohne dass sich der Endbenutzer des Programms explizit darum kümmern muss, ...

        weil das sogenannte Standardbibliotheken sind, die der C-Compiler mitbringt und die vermutlich in einer Default-Konfigurationsdatei oder Make-Datei mit drinstehen.

        wohingegen das bei SDL.h nicht funktioniert.

        Das scheint etwas zu sein, was nicht zum Standardumfang gehört. Mir sagt SDL nichts, und auf die Schnelle konnte ich auch nicht herausfinden, was es damit auf sich hat.

        Würde ich versuchen mit g++ programm.cpp -o programm das Programm zu kompilieren, kann der Compiler SDL.h ohne direkte Angabe von -lSDL nicht einkompilieren.

        Die Headerdatei (sdl.h) wird er schon mit eincompilieren, der Bezug ist ja durch die #include-Anweisung offensichtlich. Aber die Headerdatei enthält ja nur die Deklarationen der in diesem Modul enthaltenen Funktionen und Daten. Die Definitionen (also die Implementierungen) stecken entweder in einer vorcompilierten Bibliothek (beispielsweise als .lib), oder die Bibliothek liegt im Quellcode vor und muss mit übersetzt werden, dann würde der Linker sie in Form von Object-Dateien sehen. In beiden Fällen muss man es ihm aber mitteilen, da der Linker keinen Einblick mehr in den Quellcode hat.

        Schönen Sonntag noch,
         Martin

        --
        Butterkeksverteiler zu werden ist vermutlich eine der wenigen beruflichen Perspektiven, die sich noch bieten, wenn man einen an der Waffel hat.
        1. Hallo Martin.

          Mir sagt SDL nichts, und auf die Schnelle konnte ich auch nicht herausfinden, was es damit auf sich hat.

          Siehe Wikipedia.
          Dank SDL hat mein per QEMU emuliertes WinXP eine Soundausgabe.

          Einen schönen Sonntag noch.

          Gruß, Ashura

          --
          sh:( fo:} ch:? rl:( br: n4:~ ie:{ mo:| va:) de:> zu:} fl:( ss:) ls:[ js:|
          „It is required that HTML be a common language between all platforms. This implies no device-specific markup, or anything which requires control over fonts or colors, for example. This is in keeping with the SGML ideal.“
          [HTML Design Constraints: Logical Markup]
          1. Moin Ashura,

            Mir sagt SDL nichts, und auf die Schnelle konnte ich auch nicht herausfinden, was es damit auf sich hat.
            Siehe Wikipedia.
            Dank SDL hat mein per QEMU emuliertes WinXP eine Soundausgabe.

            danke, der Hinweis hilft mir schon weiter. Ja, einige Referenzen auf "Simple DirectMedia Layer" hatte ich wohl gefunden; ich hatte das aber für etwas gehalten, was auf DirectX aufsetzt. Da Markus nicht Windows verwendet, sondern Gentoo, konnte das nicht sein. Ergo: Richtig gefunden, aber nicht für richtig gehalten. ;-)

            Ciao,
             Martin

            --
            Die meisten Menschen werden früher oder später durch Computer ersetzt.
            Für manche würde aber auch schon ein einfacher Taschenrechner genügen.
      2. Hallo Markus,

        Was ich dabei nicht verstehe, ist, warum beispielsweise <iostream>, <stdio.h>, o.ä. Bibliotheken in das Programm einkompiliert werden, ohne dass sich der Endbenutzer des Programms explizit darum kümmern muss, dass er dieses erforderlichen Bibliotheken auf seinem System hat, wohingegen das bei SDL.h nicht funktioniert.

        Das hat einen ganz banalen Grund: Der Linker liest automatisch eine Liste von .LIBs ein, dabei natürlich die C-Laufzeitbibliothek. Um dieses Verhalten zu unterdrücken, gibt es i.d.R. einen extra Schalter - den braucht man aber nur in ganz speziellen Fällen.

        Würde ich versuchen mit g++ programm.cpp -o programm das Programm zu kompilieren, kann der Compiler SDL.h ohne direkte Angabe von -lSDL nicht einkompilieren.

        Wenn ich mich recht erinnere, ist -l der Parameter zur Angabe zusätzlicher Bibliotheken an den Linker. Der Compiler kommt auch ohne -l aus - er erzeugt einfach eine externe Referenz auf die Bibliothekssymbole. Nur der Linker stellt hinterher fest, daß nicht alle Referenzen aufgelöst werden konnten und beendet mit Fehler.

        Ich finde, das Verfahren ist wirklich nicht kompliziert - man schreibt einmal die entsprechenden Parameter in den Make-File, oder wie immer das Ding heißt und die Sache ist erledigt.

        Gruß,

        Houyhnhnm