2010-06-18 7 views
6

Wie ändere ich die Bibliothek, aus der eine Funktion während der Laufzeit geladen wird?Ändern der Bibliotheksladefolge zur Laufzeit (wie LD_PRELOAD, aber während der Ausführung)

Zum Beispiel möchte ich die Standardfunktion printf durch etwas Neues ersetzen, ich kann meine eigene Version schreiben und es in eine gemeinsame Bibliothek kompilieren, dann legte "LD_PRELOAD =/my/library.so" in die Umgebung vor läuft meine ausführbare Datei.

Aber lassen Sie uns stattdessen sagen, dass ich diese Verknüpfung innerhalb des Programms selbst ändern möchte. Das muss doch möglich sein ... oder?

EDIT
Und nein, wird die folgende nicht (aber wenn Sie mir sagen, wie es funktioniert, das wäre dann ausreichend sein).

void* mylib = dlopen("/path/to/library.so",RTLD_NOW); 
printf = dlsym(mylib,"printf"); 

Antwort

4

AFAIK funktionieren würde, ist dies nicht möglich. Die allgemeine Regel besagt, dass, wenn das gleiche Symbol in zwei Bibliotheken erscheint, ld.so die zuerst geladene Bibliothek favorisiert. LD_PRELOAD stellt sicher, dass die angegebenen Bibliotheken vor implizit geladenen Bibliotheken geladen werden.

Sobald die Ausführung gestartet wurde, wurden alle implizit geladenen Bibliotheken geladen und daher ist es zu spät, Ihre Bibliothek vor ihnen zu laden.

0

es eine Umgebungsvariable LD_LIBRARY_PATH ist, wo die Linker sucht zerkleinern Bibliotheken, prepend Ihren Weg zum LD_LIBRARY_PATH, ich das hoffen

0

Sie können das nicht ändern. Im Allgemeinen wird das NIX-Verknüpfungskonzept (oder besser gesagt das fehlende Konzept) vom ersten Objekt, an dem es gefunden wird, ausgewählt. (Außer Oddball AIX, das standardmäßig mehr wie OS/2 funktioniert.)

Programmgesteuert können Sie immer versuchen dlsym(RTLD_DEFAULT) und dlsym(RTLD_NEXT). man dlsym für mehr. Obwohl es ziemlich schnell außer Kontrolle gerät. Warum wird selten verwendet.

+0

Können Sie erklären, was Sie mit 'dlsym()' meinen? Weitere Informationen finden Sie unter Bearbeiten der ursprünglichen Frage. – tylerl

+1

Sie können nicht 'printf = dlsym (mylib," printf ");' offensichtlich sollten Sie jedoch mit einer globalen Variable 'int (* myprintf) (const char * fmt, ...) = dlsym (mylib, "printf") '' und dann '#define printf myprintf' in den benötigten Übersetzungseinheiten. Ich habe gedacht, dass 'printf' in Ihrem Fall eher ein Beispiel wäre. Wenn "printf" speziell Ihr Ziel ist (und eine Überschreibung zur Verbindungszeit ist auch eine Option), können Sie Linker auch dazu bringen, keine Standardbibliotheken zu verknüpfen, sondern Ihre Bibliothek mit printf() zuerst in der Bibliotheksliste - manuell - zu platzieren spezifizierte Standardbibliotheken, in denen sich orig printf() befindet. – Dummy00001

+0

BTW, LD_PRELOAD ist eine Möglichkeit, eine Bibliothek * vor * Standardbibliotheken hinzuzufügen. Es tut es nur während der Laufzeit. Während der Verbindungszeit ist die Reihenfolge der Bibliotheken, die Sie dem Linker geben, die Priorität der Symbole: Symbole werden von der ersten bis zur letzten Bibliothek gesucht. Normalerweise werden Standardbibliotheken zuerst in die Linkliste aufgenommen (durch Frontends wie cc). Sie können sie jedoch deaktivieren (-nostdlib) und sie manuell in der von Ihnen benötigten Reihenfolge angeben, z. indem Sie Ihre libmy.so zuerst vor allem anderen setzen. Dann würde printf() von libmy.so * printf von libc * überschreiben. – Dummy00001

1

Es sollte gesagt werden, dass der Versuch, Funktionen aus der libc in Ihrer Anwendung zu ersetzen, ein undefiniertes Verhalten gemäß ISO C/POSIX hat, unabhängig davon, ob Sie es statisch oder dynamisch tun. Es mag funktionieren (und wird größtenteils mit GNU/Linux funktionieren), aber es ist unklug, darauf zu vertrauen, dass es funktioniert. Wenn Sie nur den Namen "printf" verwenden möchten, aber etwas nicht standardmäßiges in Ihrem Programm tun, ist der beste Weg, um dies zu tun, nach #undef printf und #define printf my_printf nach dem Einbinden aller System-Header. Auf diese Weise stören Sie nicht die interne Verwendung der Funktion durch die von Ihnen verwendeten Bibliotheken ... und Ihre Implementierung von my_printf kann das System printf bei Bedarf sogar aufrufen.

Auf der anderen Seite, wenn Ihr Ziel ist, mit dem, was Bibliotheken tun zu stören, irgendwo auf der anderen Seite werden Sie wahrscheinlich auf Kompatibilitätsprobleme stoßen. Ein besserer Ansatz wäre wahrscheinlich, herauszufinden, warum die Bibliothek nicht das tut, was Sie wollen, ohne die Funktionen neu zu definieren, sie zu patchen und Patches upstream zu senden, wenn sie angemessen sind.

-2

Speichern Sie das Ergebnis dlsym() in einer Nachschlagetabelle (Array, Hashtabelle usw.). Dann #undef print und #define print, um Ihre Lookup-Table-Version zu verwenden.

+1

So funktioniert die C-Sprache nicht. – shoosh

+0

Das funktioniert gut. Es ist, wie QT OpenSSL verwendet, sowie etwas Code von mir, der Laufzeit-Linking statt Kompilierzeit/Ladezeit hat. –

2

Es gibt keine saubere Lösung, aber es ist möglich. Ich sehe zwei Optionen:

  1. Überschreiben printf Funktion Prolog mit Sprung zu Ihrer Ersatzfunktion.

    Es ist eine ziemlich populäre Lösung für das Funktionshooking in MS Windows. Sie können Beispiele für das Hooking von Funktionen durch Code-Umschreibung in Google finden.

  2. Umschreiben der ELF-Verschiebungs-/Verbindungstabellen.

    Siehe this article on codeproject das tut fast genau das, was Sie fragen, aber nur in einem Bereich von dlopen() 'ed-Modulen. In Ihrem Fall möchten Sie auch Ihr Hauptmodul (normalerweise Nicht-PIC) bearbeiten. Ich habe es nicht ausprobiert, aber vielleicht ist es so einfach wie mit bereitgestellten Code aufrufen:

    void* handle = dlopen(NULL, RTLD_LAZY); 
    void* original; 
    original = elf_hook(argv[0], LIBRARY_ADDRESS_BY_HANDLE(handle), printf, my_printf); 
    

    Wenn das fehlschlägt Sie Quelle Ihrer dynamischen Linker zu lesen, um herauszufinden, was angepasst werden muss.

Verwandte Themen