2012-12-19 9 views
21

um zu klären, meine Frage bezieht sich auf Wrapping/Abfangen von Anrufen von einer Funktion/Symbol zu einer anderen Funktion/Symbol wenn der Anrufer und die Aufgerufene sind in der gleichen Kompilierungseinheit mit dem GCC-Compiler und -Linker definiert.GNU gcc/ld - Wrapping einen Anruf auf Symbol mit Anrufer und Aufgerufenen in der gleichen Objektdatei definiert

Ich habe eine Situation ähnlich der folgenden:

/* foo.c */ 
void foo(void) 
{ 
    /* ... some stuff */ 
    bar(); 
} 

void bar(void) 
{ 
    /* ... some other stuff */ 
} 

Ich möchte Anrufe auf diese Funktionen wickeln, und das kann ich (bis zu einem Punkt) mit ld's --wrap option (und dann __wrap_foo und __wrap_bar ich implementieren die Rufen Sie wiederum __real_foo und __real_bar wie erwartet durch das Ergebnis von lds --wrap Option).

gcc -Wl,--wrap=foo -Wl,--wrap=bar ... 

Das Problem ist, ich habe, dass dieser Effekt dauert nur für Referenzen und von außerhalb dieser Übersetzungseinheit bar foo (und zur Verknüpfungszeit aufgelöst). Das heißt, Anrufe zu foo und bar von anderen Funktionen innerhalb foo.c nicht eingewickelt werden.

calls from within the compilation unit get resolved before the linker's wrapping

Ich versuchte objcopy --redefine-sym verwenden, aber das umbenennt nur die Symbole und ihre Referenzen.

Ich mag würde Anrufe foo und bar (innerhalb foo.o) zu __wrap_foo und __wrap_bar ersetzen (wie sie durch den --wrap Option des Linkers in anderen Objektdateien aufgelöst werden), bevor ich die * .o-Dateien in den Pass Linker --wrap Optionen, und ohne den Quellcode von foo.c zu ändern.

Auf diese Weise findet das Wrapping/Interception für alle Aufrufe an foo und bar statt, und nicht nur diejenigen, die außerhalb von foo.o stattfinden.

Ist das möglich?

+0

Sie wahrscheinlich Ihr Problem mit Fund lösen könnten/in Ihrem Editor ersetzen oder mit sed ... –

+0

Behaupten Sie einfach die obj mit einem Editor zu hacken? –

+0

Ich schlage vor, dass Sie den Quellcode in Großbuchstaben ändern, um die Aufrufe der Funktion durch solche an einen Wrapper zu ersetzen, oder mit etwas, das Sie entweder als echte Funktion oder als Wrapper definieren können. –

Antwort

5

Sie müssen das Symbol mit objcopy schwächen und globalisieren.

-W symbolname 
--weaken-symbol=symbolname 
    Make symbol symbolname weak. This option may be given more than once. 
--globalize-symbol=symbolname 
    Give symbol symbolname global scoping so that it is visible outside of the file in which it is defined. This option may be given more than once. 

Dieser arbeitete für mich

bar.c:

#include <stdio.h> 
int foo(){ 
    printf("Wrap-FU\n"); 
} 

foo.c:

#include <stdio.h> 

void foo(){ 
printf("foo\n"); 
} 

int main(){ 
printf("main\n"); 
foo(); 
} 

Kompilieren es

$ gcc -c foo.c bar.c 

Schwächere das foo-Symbol und mache es global, so dass es wieder für den Linker verfügbar ist.

$ objcopy foo.o --globalize-symbol=foo --weaken-symbol=foo foo2.o 

Jetzt können Sie Ihre neue obj mit der Wrap von bar.c Link

$ gcc -o nowrap foo.o #for reference 
$ gcc -o wrapme foo2.o bar.o 

-Test

$ ./nowrap 
main 
foo 

Und das umwickelte ein:

$ ./wrapme 
main 
Wrap-FU 
+0

Das würde funktionieren. Vielen Dank! –

+0

Ich habe diesen Trick in dem folgenden Fall versucht: 1- Ich habe ein SDK für eine eingebettete Plattform, die eine Funktion hat, die ich durch eine andere Verzögerung ersetzen muss. 2- Ich machte das Symbol schwach und global wieder aus der Objektdatei in der Zielbibliothek mit gcc-objcopy nach der Kompilierung. Das Problem, dass der Erstellungsprozess eine Archivdatei (core.a) erstellt, die die alte Bibliotheksobjektdatei enthält. 3- Ich habe einen Schritt hinzugefügt, um die Objektdatei zu löschen und sie mit der neuen Datei (mit schwachem Symbol) zu ersetzen, indem ich gcc-ar von cora.a. Als Ergebnis der Trick war nicht erfolgreich (mehrere Definition von ..) Hilfe? –

4

Dies erscheint als dokumentiert zu arbeiten:

--wrap=symbol 
     Use a wrapper function for symbol. 
     Any undefined reference to symbol will be resolved to "__wrap_symbol". ... 

Notiere die undefined oben. Wenn der Linker foo.o verarbeitet, ist der bar()nicht undefined, so dass der Linker es nicht wickelt. Ich bin mir nicht sicher warum es ist so gemacht, aber es ist wahrscheinlich ein Anwendungsfall, der dies erfordert.

+0

Ich verwende dies, um Anrufe über Kompilierungseinheiten einzubinden (siehe meine ursprüngliche Frage für ein Beispiel). Es funktioniert jedoch nicht zum Abfangen/Umbrechen von Alls innerhalb von Kompilierungseinheiten (was ich abfangen möchte.) Offenbar ** sind die Referenzen innerhalb der Kompilierungseinheiten aufgelöst. Zu dem Zeitpunkt, zu dem der Linker hereinkommt, ist es bereits zu spät, um diese Aufrufe mit der Link-Option "--wrap" zu umbrechen. –

+0

@ luis.espinal "es ist schon zu spät" - nein, ist es nicht. Der Linker * könnte das Rufziel leicht ändern; es tut es einfach nicht (aus Gründen, die ich nicht weiß). –

+0

Nun, wenn ich sage "es ist zu spät", sage ich das im Kontext von GNU ld (nicht im Kontext von Linkern im Allgemeinen.) Ja, * a * -Linker könnte dieses Aufrufziel leicht ändern. Aber ** der fragliche ** Linker (GNU ld) nicht. Und der Grund ist, dass es sich darauf beschränkt, die Referenzen, die nicht aufgelöst werden, innerhalb der Kompilierungseinheit ** zu ersetzen/neu zu schreiben. Aufgrund dieses letzten Schritts sage ich, dass die Verknüpfungsstufe für GN ld bereits zu spät ist (obwohl es für einen intelligenteren Linker nicht zu spät wäre). –

3

können Sie erreichen, was Sie wollen, wenn Sie --undefined mit --wrap

-u SYMBOL, --undefined SYMBOL 
           Start with undefined reference to SYMBOL 
+0

Versucht dies, aber ID hat auch nicht funktioniert ... – Ale

5
#include <stdio.h> 
#include <stdlib.h> 

//gcc -ggdb -o test test.c -Wl,-wrap,malloc 
void* __real_malloc(size_t bytes); 

int main() 
{ 
    int *p = NULL; 
    int i = 0; 

    p = malloc(100*sizeof(int)); 

    for (i=0; i < 100; i++) 
     p[i] = i; 

    free(p); 
    return 0; 
} 

void* __wrap_malloc(size_t bytes) 
{ 
     return __real_malloc(bytes); 
} 

Und dann kompilieren nur diesen Code und Debug verwenden. Wenn Sie das Reall-Malloc aufrufen, ruft die aufgerufene Funktion __wrap_malloc und __real_malloc malloc auf.

Ich denke, das ist der Weg, um die Anrufe abzufangen.

Grundsätzlich ist es die --wrap Option von ld bereitgestellt.

+4

Ich weiß diese Option. Es ist ziemlich genau das, was ich benutze. Dies funktioniert nicht in dem von mir erwähnten Szenario. Sehen Sie sich meine ursprüngliche Frage noch einmal an. –

1

Sie können Verwenden Sie __attribute__((weak)) vor der Implementierung Entlassung des Angerufenen, damit jemand ihn ohne GCC, der über mehrere Definitionen brüllt, neu implementieren kann.

Angenommen, Sie möchten die world-Funktion in der folgenden hello.c Codeeinheit verspotten. Sie können das Attribut voranstellen, um es überschreiben zu können.

#include "hello.h" 
#include <stdio.h> 

__attribute__((weak)) 
void world(void) 
{ 
    printf("world from lib\n"); 
} 

void hello(void) 
{ 
    printf("hello\n"); 
    world(); 
} 

Und Sie können es dann in einer anderen Unit-Datei überschreiben. Sehr nützlich für die Unit-Tests/spöttisch:

#include <stdio.h> 
#include "hello.h" 

/* overrides */ 
void world(void) 
{ 
    printf("world from main.c"\n); 
} 

void main(void) 
{ 
    hello(); 
    return 0; 
} 
+0

Das ist eine nette Idee. Werde das nächste Mal benutzen. Leider hatte ich zu der Zeit, als ich die Frage stellte, eine Software, die ich nicht ändern konnte, um ein solches Attribut hinzuzufügen. Dies ist jedoch gut und wird in Zukunft sicher in meiner Toolbox verwendet werden. –

+0

Nun ja, wenn Sie die Quelle nicht ändern können, dann ist @ PeterHuewes Antwort die Lösung, die objcpy verwendet. Wenn Sie die Quelle ändern können, scheint diese einfacher einzurichten. – MicroJoe