Frage

Ich will override bestimmte Funktion aufruft, um verschiedene APIs für die sake der Protokollierung der Anrufe, aber ich habe auch vielleicht möchten Sie Daten manipulieren, bevor Sie an die tatsächliche Funktion.

Sagen wir zum Beispiel, ich benutze eine Funktion, die aufgerufen wird getObjectName Tausende Male in meinem source-code.Ich möchte Sie vorübergehend überschreiben Sie diese Funktion, manchmal weil ich will, ändern Sie das Verhalten der Funktion zu sehen, die zu verschiedenen Ergebnissen führt.

Ich erstelle eine neue source-Datei wie folgt:

#include <apiheader.h>    

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

Ich zum Beispiel alle meine anderen Quelle, wie ich normalerweise würde, aber ich link es gegen diese Funktion zuerst, bevor Sie die Verknüpfung mit der API-Bibliothek.Dies funktioniert gut, außer ich kann natürlich nicht rufen Sie die Reale Funktion in meinem übergeordneten Funktion.

Gibt es einen einfacheren Weg, zu "überschreiben" Funktion ohne Verbindung/kompilieren Fehler/Warnungen?Im Idealfall möchte ich in der Lage sein, um die Funktion überschreiben, indem Sie einfach kompilieren und verknüpfen eine extra-Datei oder zwei, anstatt herumzufummeln mit Verlinkung Optionen oder ändern des aktuellen source code von mein Programm.

War es hilfreich?

Lösung

Wenn es nur für Ihre Quelle ist, die Sie erfassen möchten / ändern die Anrufe, ist die einfachste Lösung zusammen eine Header-Datei zu setzen (intercept.h) mit:

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

und die Funktion implementieren wie folgt (in intercept.c der nicht ist intercept.h):

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

Dann sicher jede Quelldatei, wo Sie den Anruf abfangen wollen, hat:

#include "intercept.h"

an der Spitze.

Wenn Sie dann mit „-DINTERCEPT“ kompilieren, werden alle Dateien Ihre Funktion aufrufen, anstatt die reale und Ihre Funktion kann die reale man noch nennen.

Kompilieren ohne die "-DINTERCEPT" wird verhindert, dass das Abfangen von dem Auftreten.

Es ist ein bisschen schwieriger, wenn Sie alle Anrufe abfangen wollen, (nicht nur diejenigen, die aus Ihrer Quelle) - dies kann in der Regel mit dynamischer Belastung und die Auflösung der realen Funktion (mit dlload- und dlsym-type Anrufen) getan werden, aber ich glaube nicht, dass ist es notwendig, in Ihrem Fall.

Andere Tipps

Mit gcc unter Linux Sie den --wrap Linker-Flag wie folgt verwendet werden:

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
}

Dadurch wird sichergestellt, dass alle Anrufe zu getObjectName() auf Ihre Wrapper-Funktion (zur Verbindungszeit) umgeleitet werden. Dieser sehr nützliche Flag ist in gcc unter Mac OS X jedoch nicht gefunden.

Beachten Sie die Wrapper-Funktion mit extern "C" zu erklären, wenn Sie mit g ++ kompiliert sind aber.

Sie können eine Funktion mit LD_PRELOAD Trick außer Kraft setzen - siehe man ld.so. Sie kompilieren gemeinsamen lib mit Ihrer Funktion und starten Sie die binäre (Sie selbst brauchen nicht die binäre! Ändern) wie LD_PRELOAD=mylib.so myprog.

Im Körper Ihrer Funktion (in gemeinsamer lib) Sie so schreiben:

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 eine beliebige Funktion von Shared Library außer Kraft setzen, auch von stdlib, ohne Änderung / das Programm neu zu kompilieren, so könnte man den Trick auf Programme, die Sie nicht eine Quelle haben für. Ist es nicht schön?

Wenn Sie GCC verwenden, können Sie Ihre Funktion weak machen. Diejenigen können außer Kraft gesetzt werden von nicht-schwachen Funktionen:

test.c :

#include <stdio.h>

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

int main() {
    test();
}

Was ist zu tun?

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

test1.c :

#include <stdio.h>

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

Was ist zu tun?

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

Leider das wird nicht für andere Compiler arbeiten. Aber Sie können die schwachen Erklärungen haben, die overridable Funktionen in einer eigenen Datei enthalten, platzieren eine nur in die API-Implementierung-Dateien enthalten, wenn Sie GCC kompilieren mit:

weakdecls.h :

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

functions.c :

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

void test(void) { 
    ...
}

... other functions ...

Kehrseite der Medaille ist, dass es nicht funktioniert vollständig ohne etwas zu den api-Dateien zu tun (um diese drei Linien und die weakdecls). Aber sobald Sie diese Änderung hat, können Funktionen leicht außer Kraft gesetzt werden, indem eine globale Definition in einer Datei zu schreiben und die Verknüpfung, dass in.

  

Es ist oft wünschenswert, die zu modifizieren   Verhalten der bestehenden Codebasen durch   Einwickeln oder Ersetzen Funktionen. Wann   Editieren des Quellcodes von denen,   Funktionen ist ein gangbarer Weg, dies kann   sein ein einfacher Prozess. Wann   die Quelle der Funktionen kann nicht sein,   wenn die Funktionen bearbeitet (beispielsweise sind   durch das System C-Bibliothek zur Verfügung gestellt),   dann alternative Techniken sind   erforderlich. Hier präsentieren wir eine solche   Techniken für UNIX, Windows und   Macintosh OS X-Plattformen.

Dies ist ein großer PDF-Abdeckung, wie dies auf O X, Linux und Windows durchgeführt wurde.

Es hat keine erstaunlichen Tricks, die hier nicht dokumentiert sind (dies ist eine erstaunliche Reihe von Antworten BTW) ... aber es ist eine schöne Lese.

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

Sie können laden Sie das PDF direkt von einem anderen Speicherort (für Redundanz) .

Und schließlich sollten die letzten beiden Quellen irgendwie in Flammen gehen, hier ist ein Google-Suchergebnis für sie .

Sie können einen Funktionszeiger als globalen Variable definieren. Die Anrufer-Syntax würde sich nicht ändern. Wenn Ihr Programm startet, könnte es prüfen, ob einige Kommandozeilen-Flag oder Umgebungsvariable gesetzt ist die Protokollierung aktivieren, speichern Sie dann den ursprünglichen Wert des Funktionszeiger und ersetzen Sie es mit Ihren Logging-Funktion. Sie würden nicht eine spezielle „Protokollierung aktiviert“ Build müssen. Benutzer konnten Protokollierung „im Feld“ aktivieren.

Sie müssen in der Lage sein die Anrufer-Quellcode zu ändern, aber nicht die Angerufenen (so würde dies funktionieren, wenn Bibliotheken von Drittanbietern aufrufen).

foo.h:

typedef const char* (*GetObjectNameFuncPtr)(object *anObject);
extern GetObjectNameFuncPtr GetObjectName;

foo.cpp:

const char* GetObjectName_real(object *anObject)
{
    return "object name";
}

const char* GetObjectName_logging(object *anObject)
{
    if (anObject == null)
        return "(null)";
    else
        return GetObjectName_real(anObject);
}

GetObjectNameFuncPtr GetObjectName = GetObjectName_real;

void main()
{
    GetObjectName(NULL); // calls GetObjectName_real();

    if (isLoggingEnabled)
        GetObjectName = GetObjectName_logging;

    GetObjectName(NULL); // calls GetObjectName_logging();
}

Es gibt auch ein heikles Verfahren davon in dem Linker mit zwei Stub-Bibliotheken zu tun.

In der Bibliothek # 1 gegen die Host-Bibliothek verknüpft ist, und setzt das Symbol unter einem anderen Namen neu definiert.

In der Bibliothek # 2 verknüpft gegen Bibliothek # 1, den Anruf interecepting und die neu definierte Version in der Bibliothek # 1 aufrufen.

Seien Sie sehr vorsichtig mit Link Aufträge hier oder es wird nicht funktionieren.

Aufbauend auf @Johannes Schaub Antwort mit einer Lösung eignet sich für code, den Sie nicht besitzen.

Alias-die Funktion, die Sie außer Kraft setzen möchten, um eine schwach definierte Funktion, und dann implementieren Sie es selbst.

überschreiben.h

#define foo(x) __attribute__((weak))foo(x)

foo.c

function foo() { return 1234; }

überschreiben.c

function foo() { return 5678; }

Verwenden Muster-spezifische variable Werte in Ihrem Makefile zum hinzufügen von compiler-flag -include override.h.

%foo.o: ALL_CFLAGS += -include override.h

Beiseite:Vielleicht könntest du auch verwenden -D 'foo(x) __attribute__((weak))foo(x)' definieren Sie Ihre Makros.

Kompilieren und verknüpfen Sie die Datei mit Ihrem Neuimplementierung (override.c).

  • Dies ermöglicht Ihnen das überschreiben einer Funktion aus einer beliebigen Quelle Datei, ohne den code ändern.

  • Der Nachteil ist, dass müssen Sie eine separate header-Datei für jede Datei, die Sie überschreiben möchten.

Sie können eine gemeinsam genutzte Bibliothek verwenden (Unix) oder eine DLL (Windows), dies auch zu tun (wäre ein bisschen eine Leistungseinbuße sein). Anschließend können Sie die DLL ändern / damit wird (eine Version für Debug, eine Version für Nicht-debug) geladen.

Ich habe eine ähnliche Sache in der Vergangenheit getan hat (nicht zu erreichen, was Sie erreichen wollen, aber die grundlegende Prämisse ist die gleiche), und es hat gut funktioniert.

[Bearbeiten basierend auf OP comment]

  

In der Tat einer der Gründe, warum ich will   Funktionen zum Überschreiben ist, weil ich   vermuten, dass sie sich anders verhalten auf   verschiedene Betriebssysteme.

Es gibt zwei Möglichkeiten (die ich kenne) von mit dem Umgang, die gemeinsamen lib / dll Art und Weise oder Schreiben von verschiedenen Implementierungen, die Sie gegen verknüpfen.

Für beide Lösungen (gemeinsam Libs oder anderes Anknüpfungs) Sie müssten foo_linux.c, foo_osx.c, foo_win32.c (oder ein besserer Weg ist linux / foo.c, osx / foo.c und win32 / foo.c ) und dann mit dem geeigneten kompilieren und verknüpfen.

Wenn Sie suchen, sowohl anderen Code für verschiedene Plattformen und Debug--vs- löst ich wahrscheinlich wäre geneigt, mit der gemeinsamen lib / DLL-Lösung zu gehen, wie es die flexibelste ist.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top