wake-up-neo.com

Einfache Suche nach ungelösten Symbolen in gemeinsam genutzten Bibliotheken?

Ich schreibe eine ziemlich große C++ - Objektbibliothek und bin auf ein kleines Problem gestoßen, das das Debuggen zu einem Problem macht:

Wenn ich eine Funktion/Methode in einer Header-Datei definiere und vergesse, einen Stub dafür zu erstellen (während der Entwicklung), werden beim Kompilieren keine Fehler angezeigt, die angeben, dass ich eine solche habe, da ich eine gemeinsam genutzte Objektbibliothek anstelle einer ausführbaren Datei erstelle vergessen, diese Funktion zu implementieren. Der einzige Weg, wie ich herausfinde, dass etwas nicht in Ordnung ist, ist die Laufzeit, wenn eine Anwendung, die mit dieser Bibliothek verknüpft ist, mit einem undefinierten Symbolfehler umfällt.

Ich suche nach einer einfachen Möglichkeit, um zu überprüfen, ob ich alle Symbole habe, die ich zum Kompilierzeitpunkt benötige, vielleicht etwas, das ich zu meinem Makefile hinzufügen kann.

Eine Lösung, die ich mir ausgedacht habe, besteht darin, die kompilierte Bibliothek über nm -C -U, um eine entwirrte Liste aller undefinierten Referenzen zu erhalten. Das Problem ist, dass dies auch mit der Liste aller Referenzen in anderen Bibliotheken wie GLibC einhergeht, die natürlich zusammen mit dieser Bibliothek verknüpft werden, wenn die endgültige Anwendung zusammengestellt wird. Es wäre möglich, die Ausgabe von nm bis grep in allen meinen Header-Dateien zu verwenden und zu prüfen, ob die entsprechenden Namen vorhanden sind. Dies scheint jedoch verrückt zu sein. Sicher ist dies kein ungewöhnliches Problem und es gibt einen besseren Weg, es zu lösen?

75
David Claridge

Überprüfen Sie die Linker-Option -z defs/--no-undefined. Wenn Sie ein freigegebenes Objekt erstellen, schlägt die Verknüpfung fehl, wenn nicht aufgelöste Symbole vorhanden sind.

Wenn Sie den Linker mit gcc aufrufen, verwenden Sie den Compiler -Wl Option, um die Option an den Linker zu übergeben:

gcc -shared ... -Wl,-z,defs

Betrachten Sie als Beispiel die folgende Datei:

#include <stdio.h>

void forgot_to_define(FILE *fp);

void doit(const char *filename)
{
    FILE *fp = fopen(filename, "r");
    if (fp != NULL)
    {
        forgot_to_define(fp);
        fclose(fp);
    }
}

Wenn Sie dies nun in ein freigegebenes Objekt integrieren, ist dies erfolgreich:

> gcc -shared -fPIC -o libsilly.so silly.c && echo succeeded || echo failed
succeeded

Aber wenn Sie hinzufügen -z defs, der Link schlägt fehl und informiert Sie über Ihr fehlendes Symbol:

> gcc -shared -fPIC -o libsilly.so silly.c -Wl,-z,defs && echo succeeded || echo failed
/tmp/cccIwwbn.o: In function `doit':
silly.c:(.text+0x2c): undefined reference to `forgot_to_define'
collect2: ld returned 1 exit status
failed
88

Unter Linux (das Sie anscheinend verwenden) sollte ldd -r a.out Genau die Antwort geben, nach der Sie suchen.

UPDATE: Eine einfache Möglichkeit, a.out Zu erstellen, anhand derer überprüft werden soll:

 echo "int main() { return 0; }" | g++ -xc++ - ./libMySharedLib.so
 ldd -r ./a.out
14

Was ist mit einer Testsuite? Sie erstellen nachgebildete ausführbare Dateien, die mit den von Ihnen benötigten Symbolen verknüpft sind. Wenn die Verknüpfung fehlschlägt, bedeutet dies, dass Ihre Bibliotheksschnittstelle unvollständig ist.

8
Stefano Borini

Ich hatte einmal das gleiche Problem. Ich habe ein Komponentenmodell in C++ entwickelt, und natürlich sollten Komponenten zur Laufzeit dynamisch geladen werden. Mir fielen drei Lösungen ein, die ich angewendet habe:

  1. Nehmen Sie sich etwas Zeit, um ein Build-System zu definieren, das statisch kompiliert werden kann. Sie verlieren etwas Zeit für das Engineering, sparen jedoch viel Zeit beim Auffinden dieser lästigen Laufzeitfehler.
  2. Gruppieren Sie Ihre Funktionen in wohlbekannten und verständlichen Abschnitten, sodass Sie Funktionen/Stubs gruppieren können, um sicherzustellen, dass jede entsprechende Funktion über einen Stub verfügt. Wenn Sie sich die Zeit nehmen, es gut zu dokumentieren, können Sie vielleicht ein Skript schreiben, das die Definitionen überprüft (z. B. über seine Sauerstoffkommentare) und die entsprechende CPP-Datei darauf überprüft.
  3. Führen Sie mehrere ausführbare Testdateien aus, die denselben Satz von Bibliotheken laden, und geben Sie das RTLD_NOW-Flag zum Öffnen an (wenn Sie sich unter * NIX befinden). Sie signalisieren die fehlenden Symbole.

Ich hoffe, das hilft.

3
Diego Sevilla