Eine der wichtigsten Regeln und Best Practices beim Schreiben einer Bibliothek besteht darin, alle Symbole der Bibliothek in einen bibliotheksspezifischen Namespace zu stellen. C ++ macht dies aufgrund des namespace
Schlüsselworts einfach . In C besteht der übliche Ansatz darin, den Bezeichnern ein bibliotheksspezifisches Präfix voranzustellen.
Die Regeln des C-Standards legen einige Einschränkungen für diese fest (für eine sichere Kompilierung): Der AC-Compiler betrachtet möglicherweise nur die ersten 8 Zeichen eines Bezeichners foobar2k_eggs
und foobar2k_spam
kann daher als dieselben Bezeichner gültig interpretiert werden. Jeder moderne Compiler lässt jedoch beliebig lange Bezeichner zu In unserer Zeit (dem 21. Jahrhundert) sollten wir uns also nicht darum kümmern müssen.
Aber was ist, wenn Sie mit Bibliotheken konfrontiert sind, deren Symbolnamen / Identifikatoren Sie nicht ändern können? Vielleicht haben Sie nur eine statische Binärdatei und die Header oder möchten nicht oder dürfen sich nicht anpassen und neu kompilieren.
Antworten:
Zumindest bei statischen Bibliotheken können Sie dies ganz bequem umgehen.
Betrachten Sie die Überschriften der Bibliotheken foo und bar . Für dieses Tutorial gebe ich Ihnen auch die Quelldateien
Beispiele / ex01 / foo.h.
examples / ex01 / foo.c (dies ist möglicherweise undurchsichtig / nicht verfügbar)
Beispiel / ex01 / bar.h.
Beispiele / ex01 / bar.c (dies ist möglicherweise undurchsichtig / nicht verfügbar)
Wir wollen diese in einer Programm-Foobar verwenden
Beispiel / ex01 / foobar.c
Ein Problem wird sofort deutlich: C kennt keine Überladung. Wir haben also zwei mal zwei Funktionen mit identischem Namen, aber unterschiedlicher Signatur. Wir brauchen also einen Weg, um diese zu unterscheiden. Wie auch immer, mal sehen, was ein Compiler dazu zu sagen hat:
Okay, das war keine Überraschung, es hat uns nur gesagt, was wir bereits wussten oder zumindest vermuteten.
Können wir diese Identifikatorkollision also irgendwie beheben, ohne den Quellcode oder die Header der Originalbibliotheken zu ändern? In der Tat können wir.
Lassen Sie uns zunächst die Probleme mit der Kompilierungszeit beheben. Zu diesem Zweck umgeben wir die Header-Includes mit einer Reihe von Präprozessor-
#define
Direktiven, die allen von der Bibliothek exportierten Symbolen vorangestellt sind. Später machen wir das mit einem netten, gemütlichen Wrapper-Header, aber nur um zu demonstrieren, was los ist, haben wir es wörtlich in der foobar.c- Quelldatei gemacht:Beispiel / ex02 / foobar.c
Nun, wenn wir dies kompilieren ...
... es sieht zunächst so aus, als wäre es schlimmer geworden. Aber schauen Sie genau hin: Eigentlich lief die Kompilierungsphase ganz gut. Es ist nur der Linker, der sich jetzt darüber beschwert, dass Symbole kollidieren, und der uns den Ort (Quelldatei und Zeile) mitteilt, an dem dies geschieht. Und wie wir sehen können, sind diese Symbole nicht fixiert.
Schauen wir uns die Symboltabellen mit dem Dienstprogramm nm an :
Jetzt müssen wir diese Symbole in einer undurchsichtigen Binärdatei voranstellen. Ja, ich weiß im Verlauf dieses Beispiels, dass wir die Quellen haben und dies dort ändern könnten. Aber für den Moment nehmen Sie einfach an, dass Sie nur diese .o- Dateien oder eine .a-Datei haben (was eigentlich nur ein Haufen von .o ist ).
Objekt zur Rettung
Es gibt ein Werkzeug, das für uns besonders interessant ist: die Objektkopie
objcopy funktioniert mit temporären Dateien, sodass wir es so verwenden können, als ob es direkt funktioniert. Es gibt eine Option / Operation namens --prefix-Symbole und Sie haben 3 Vermutungen, was es tut.
Also werfen wir diesen Kerl auf unsere hartnäckigen Bibliotheken:
nm zeigt uns, dass dies zu funktionieren schien:
Versuchen wir, diese ganze Sache zu verknüpfen:
Und tatsächlich hat es funktioniert:
Jetzt überlasse ich es dem Leser als Übung, ein Tool / Skript zu implementieren, das die Symbole einer Bibliothek automatisch mit nm extrahiert und eine Wrapper-Header-Datei der Struktur schreibt
und wendet das Symbolpräfix mithilfe von objcopy auf die Objektdateien der statischen Bibliothek an .
Was ist mit gemeinsam genutzten Bibliotheken?
Im Prinzip könnte das gleiche mit gemeinsam genutzten Bibliotheken gemacht werden. Wie der Name schon sagt, werden gemeinsam genutzte Bibliotheken von mehreren Programmen gemeinsam genutzt. Daher ist es keine gute Idee, auf diese Weise mit einer gemeinsam genutzten Bibliothek herumzuspielen.
Sie werden nicht darum herumkommen, eine Trampolinverpackung zu schreiben. Schlimmer noch, Sie können keine Verknüpfung mit der gemeinsam genutzten Bibliothek auf der Ebene der Objektdatei herstellen, müssen jedoch dynamisch laden. Dies verdient jedoch einen eigenen Artikel.
Bleiben Sie dran und viel Spaß beim Codieren.
quelle
objcopy
.objcopy --prefix-symbols
... +1!Dies ist nicht nur eine Erweiterung moderner Compiler. Der aktuelle C-Standard verlangt auch, dass der Compiler relativ lange externe Namen unterstützt. Ich vergesse die genaue Länge, aber wenn ich mich recht erinnere, sind es jetzt ungefähr 31 Zeichen.
Dann steckst du fest. Beschweren Sie sich beim Autor der Bibliothek. Ich bin einmal auf einen solchen Fehler gestoßen, bei dem Benutzer meiner Anwendung aufgrund der
libSDL
Verknüpfung von Debianlibsoundfile
, die (zumindest zu der Zeit) den globalen Namespace mit Variablen wiedsp
(ich mache dir nichts vor!) Schrecklich verschmutzte, nicht in der Lage war, ihn auf Debian aufzubauen . Ich habe mich bei Debian beschwert, und sie haben ihre Pakete repariert und das Update stromaufwärts gesendet, wo ich davon ausgehe, dass es angewendet wurde, da ich nie wieder von dem Problem gehört habe.Ich denke wirklich, dass dies der beste Ansatz ist, weil er das Problem für alle löst . Jeder lokale Hack, den Sie ausführen, lässt das Problem in der Bibliothek, damit der nächste unglückliche Benutzer erneut auf ihn stoßen und mit ihm kämpfen kann.
Wenn Sie wirklich eine schnelle Lösung benötigen und über eine Quelle verfügen, können Sie
-Dfoo=crappylib_foo -Dbar=crappylib_bar
dem Makefile eine Reihe von usw. hinzufügen , um diese zu beheben. Wenn nicht, verwendenobjcopy
Sie die gefundene Lösung.quelle
Wenn Sie GCC verwenden, ist der Linker-Schalter --allow-multiple-definition ein praktisches Debugging-Tool. Dies bringt den Linker dazu, die erste Definition zu verwenden (und nicht darüber zu jammern). Mehr dazu hier .
Dies hat mir bei der Entwicklung geholfen, wenn ich die Quelle für eine vom Hersteller bereitgestellte Bibliothek zur Verfügung habe und aus irgendeinem Grund auf eine Bibliotheksfunktion zurückgreifen muss. Mit dem Schalter können Sie eine lokale Kopie einer Quelldatei kompilieren und verknüpfen und dennoch eine Verknüpfung mit der unveränderten statischen Herstellerbibliothek herstellen. Vergessen Sie nicht, den Schalter wieder aus den Make-Symbolen zu ziehen, sobald die Entdeckungsreise abgeschlossen ist. Versandversandcode mit absichtlichen Namensraumkollisionen ist anfällig für Fallstricke, einschließlich unbeabsichtigter Namensraumkollisionen.
quelle