79 Stimmen

Einen Funktionsaufruf in C außer Kraft setzen

Ich möchte bestimmte Funktionsaufrufe an verschiedene APIs überschreiben, um die Aufrufe zu protokollieren, aber ich möchte möglicherweise auch Daten manipulieren, bevor sie an die eigentliche Funktion gesendet werden.

Ein Beispiel: Ich verwende eine Funktion namens getObjectName Tausende von Malen in meinem Quellcode. Ich möchte diese Funktion manchmal vorübergehend außer Kraft setzen, weil ich das Verhalten dieser Funktion ändern möchte, um ein anderes Ergebnis zu sehen.

Ich erstelle eine neue Quelldatei wie folgt:

#include <apiheader.h>    

const char *getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return "name should be here";
}

Ich kompiliere alle anderen Quelltexte wie gewohnt, aber ich verknüpfe sie zuerst mit dieser Funktion, bevor ich sie mit der API-Bibliothek verknüpfe. Dies funktioniert gut, außer ich kann offensichtlich nicht die echte Funktion innerhalb meiner überschreibenden Funktion aufrufen.

Gibt es einen einfacheren Weg zu "überschreiben" eine Funktion ohne Verknüpfung/Kompilierung Fehler/Warnungen zu erhalten? Idealerweise möchte ich in der Lage sein, die Funktion zu überschreiben, indem Sie nur kompilieren und verknüpfen eine zusätzliche Datei oder zwei statt fiddle um mit Verknüpfung Optionen oder Ändern der tatsächlichen Quellcode meines Programms.

93voto

codelogic Punkte 68703

Mit gcc können Sie unter Linux die --wrap Linker-Flagge wie folgt:

gcc program.c -Wl,-wrap,getObjectName -o program

und definieren Sie Ihre Funktion als:

const char *__wrap_getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return __real_getObjectName( anObject ); // call the real function
}

Damit wird sichergestellt, dass alle Anrufe an getObjectName() werden zu Ihrer Wrapper-Funktion umgeleitet (zur Link-Zeit). Dieses sehr nützliche Flag fehlt jedoch in gcc unter Mac OS X.

Denken Sie daran, die Wrapper-Funktion mit extern "C" wenn Sie allerdings mit g++ kompilieren.

82voto

paxdiablo Punkte 809679

Wenn Sie die Aufrufe nur für Ihren Quelltext erfassen/ändern wollen, ist die einfachste Lösung, eine Header-Datei ( intercept.h ) mit:

#ifdef INTERCEPT
    #define getObjectName(x) myGetObjectName(x)
#endif

Dann implementieren Sie die Funktion wie folgt (in intercept.c die nicht einschließen. intercept.h ) :

const char *myGetObjectName (object *anObject) {
    if (anObject == NULL) return "(null)";
    return getObjectName(anObject);

Vergewissern Sie sich dann, dass jede Quelldatei, in der Sie den Aufruf abfangen wollen, am Anfang Folgendes enthält:

#include "intercept.h"

Beim Kompilieren mit " -DINTERCEPT ", rufen alle Dateien Ihr Funktion und nicht die echte, während Ihre Funktion weiterhin die echte aufruft.

Kompilieren ohne das " -DINTERCEPT " verhindert das Abfangen.

Etwas kniffliger wird es, wenn man alle Aufrufe abfangen will (nicht nur die aus der Quelle) - das kann man in der Regel mit dynamischem Laden und Auflösen der eigentlichen Funktion erreichen (mit dlload- y dlsym- Typs), aber ich glaube nicht, dass dies in Ihrem Fall notwendig ist.

39voto

qrdl Punkte 32507

Sie können eine Funktion außer Kraft setzen, indem Sie LD_PRELOAD Trick - siehe man ld.so . Sie kompilieren die Shared Lib mit Ihrer Funktion und starten die Binärdatei (Sie brauchen die Binärdatei nicht einmal zu ändern!) wie LD_PRELOAD=mylib.so myprog .

Im Hauptteil Ihrer Funktion (in der Shared Lib) schreiben Sie wie folgt:

const char *getObjectName (object *anObject) {
  static char * (*func)();

  if(!func)
    func = (char *(*)()) dlsym(RTLD_NEXT, "getObjectName");
  printf("Overridden!\n");     
  return(func(anObject));    // call original function
}

Sie können jede Funktion aus der Shared Library, sogar aus der stdlib, überschreiben, ohne das Programm zu ändern/neu zu kompilieren, so dass Sie den Trick bei Programmen anwenden können, für die Sie keine Quelle haben. Ist das nicht schön?

27voto

Wenn Sie GCC verwenden, können Sie Ihre Funktion weak . Diese kann außer Kraft gesetzt werden durch nicht-schwache Funktionen:

test.c :

#include <stdio.h>

__attribute__((weak)) void test(void) { 
    printf("not overridden!\n"); 
}

int main() {
    test();
}

Was bewirkt es?

$ gcc test.c
$ ./a.out
not overridden!

test1.c :

#include <stdio.h>

void test(void) {
    printf("overridden!\n");
}

Was bewirkt es?

$ gcc test1.c test.c
$ ./a.out
overridden!

Leider funktioniert das bei anderen Compilern nicht. Aber Sie können die schwachen Deklarationen, die überschreibbare Funktionen enthalten, in einer eigenen Datei haben und einfach ein Include in die API-Implementierungsdateien einfügen, wenn Sie mit GCC kompilieren:

weakdecls.h :

__attribute__((weak)) void test(void);
... other weak function declarations ...

funktionen.c :

/* for GCC, these will become weak definitions */
#ifdef __GNUC__
#include "weakdecls.h"
#endif

void test(void) { 
    ...
}

... other functions ...

Der Nachteil dabei ist, dass es nicht funktioniert vollständig ohne etwas an den Api-Dateien zu ändern (diese drei Zeilen und die weakdecls werden benötigt). Aber sobald Sie diese Änderung vorgenommen haben, können Funktionen leicht überschrieben werden, indem Sie eine globale Definition in eine Datei schreiben und diese einbinden.

12voto

Oft ist es wünschenswert, die b

Hier finden Sie eine großartige PDF-Datei, die zeigt, wie dies unter OS X, Linux und Windows gemacht wurde.

Es enthält keine verblüffenden Tricks, die nicht schon hier dokumentiert wurden (dies ist übrigens ein erstaunlicher Satz von Antworten)... aber es ist eine nette Lektüre.

Abfangen beliebiger Funktionen auf Windows-, UNIX- und Macintosh OS X-Plattformen (2004), von Daniel S. Myers und Adam L. Bazinet .

Sie können die PDF-Datei direkt von einem alternativen Speicherort herunterladen (zur Redundanz) .

Und schließlich, falls die beiden vorgenannten Quellen irgendwie in Flammen aufgehen sollten, Hier ist ein Google-Suchergebnis dazu .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X