2012-12-19 17 views
21

per chiarire, la mia domanda si riferisce a avvolgere/intercettare le chiamate da una funzione/simbolo a un'altra funzione/simbolo quando il chiamante e il i callee sono definiti nella stessa unità di compilazione con il compilatore GCC e il linker.GNU gcc/ld - il wrapping di una chiamata a simbolo con chiamante e chiamato nello stesso file oggetto

Ho una situazione simile al seguente:

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

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

Vorrei avvolgere le chiamate a queste funzioni, e posso farlo (a un punto) con ld's --wrap option (e poi ho implementare __wrap_foo e __wrap_bar che a sua volta chiama __real_foo e __real_bar come previsto dal risultato dell'opzione --wrap di ld).

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

Il problema che sto avendo è che questo ha effetto solo per i riferimenti a foo e bar da al di fuori di tale unità di compilazione (e risolti in fase di collegamento). Ovvero, le chiamate a foo e bar da altre funzioni all'interno di foo.c non vengono incapsulate.

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

Ho provato ad utilizzare objcopy --redefine-sym, ma che rinomina solo i simboli ed i loro riferimenti.

Vorrei sostituire le chiamate a foo e bar (entro foo.o) per __wrap_foo e __wrap_bar (proprio come si risolvono in altri file oggetto dall'opzione del linker --wrap) prima di passare i file * .o al linker's --wrap e senza dover modificare il codice sorgente di foo.c.

In questo modo, l'avvolgimento/intercettazione avviene per tutte le chiamate a foo e bar, e non solo quelle che si svolgono al di fuori di foo.o.

È possibile?

+0

Probabilmente si potrebbe risolvere il problema con find/sostituire nel vostro editor, o utilizzando sed ... –

+0

stai suggerendo di incidere semplicemente l'obj con un editor? –

+0

Ti sto suggerendo di modificare il codice sorgente in blocco per sostituire le chiamate alla funzione con quelle di un wrapper o con qualcosa che puoi definire come la vera funzione o il wrapper. –

risposta

5

Devi indebolire e globalizzare il simbolo usando objcopy.

-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. 

questo ha lavorato per me

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(); 
} 

compilarlo

$ gcc -c foo.c bar.c 

Indebolire il simbolo foo e renderlo globale, quindi è nuovamente disponibile per il linker.

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

Ora è possibile collegare il vostro nuovo obj con l'involucro da bar.c

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

prova

$ ./nowrap 
main 
foo 

E il avvolto uno:

$ ./wrapme 
main 
Wrap-FU 
+0

Ciò funzionerebbe. Grazie! –

+0

Ho provato questo trucco nel seguente caso: 1- Ho un SDK per una piattaforma integrata che ha una funzione che devo sostituire con un'altra decelerazione. 2- Ho reso il simbolo debole e globale di nuovo dal file oggetto nella libreria di destinazione utilizzando gcc-objcopy dopo la compilazione. Il problema che il processo di costruzione include la creazione di un file di archivio (chiamato core.a) che include il vecchio file dell'oggetto libreria. 3- Ho aggiunto un passaggio per eliminare il file oggetto e sostituirlo con quello nuovo (con il simbolo debole) utilizzando gcc-ar da cora.a. Come risultato Il trucco non ha avuto successo (definizione multipla di ..) Aiuto? –

4

Questo sembra funzionare come documentato:

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

Annotare il indefinita sopra. Quando il linker elabora foo.o, lo è non indefinito, quindi il linker non lo avvolge. Non sono sicuro che sia perché è fatto in questo modo, ma probabilmente c'è un caso d'uso che richiede questo.

+0

Lo uso per racchiudere le chiamate su unità di compilazione (vedere la mia domanda originale per un esempio). Tuttavia, non funziona per intercettare/avvolgere tutti da unità di compilazione (che è ciò che mi interessa intercettare). Apparentemente ** all'interno ** delle unità di compilazione, i riferimenti sono risolti. Quando arriva il linker, è già troppo tardi per ** avvolgere ** quelle chiamate usando l'opzione di collegamento '--wrap'. –

+0

@ luis.espinal "è già troppo tardi" - no, non lo è. Il linker * potrebbe * cambiare facilmente il target della chiamata; semplicemente non (per ragioni che non conosco). –

+0

Bene, quando dico "è troppo tardi", lo dico nel contesto di GNU ld (non nel contesto dei linker in generale). Sì, un linker * a * potrebbe facilmente cambiare quell'obiettivo di chiamata. Ma ** il ** linker in questione (GNU ld) non lo fa. E la ragione è che si limita a sostituire/riscrivere i riferimenti che non sono risolti ** all'interno dell'unità di compilazione **. È a causa di quell'ultimo passo che dico che il livello di collegamento è già troppo tardi per GN ld (anche se non sarebbe troppo tardi per un linker più intelligente.) –

3

è possibile ottenere ciò che si vuole, se si utilizza --undefined con --wrap

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

Ho provato questo, ma id non ha funzionato neanche ... – 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); 
} 

E poi basta compilare il codice ed eseguire il debug. Quando chiamate il malloc reall, la funzione chiamata will __wrap_malloc e __real_malloc chiamerà malloc.

Penso che questo sia il modo di intercettare le chiamate.

Fondamentalmente l'opzione --wrap fornita da ld.

+4

Lo so questa opzione. È praticamente quello che uso. Questo non funziona nello scenario che ho citato. Vedi di nuovo la mia domanda originale. –

1

Puoi utilizzare __attribute__((weak)) prima dell'implementazione entizione del callee per permettere a qualcuno di reimplementarlo senza che GCC lanci più definizioni.

Ad esempio, si supponga di voler prendere in giro la funzione world nella seguente unità di codice ciao.c. Puoi anteporre l'attributo per poterlo sovrascrivere.

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

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

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

E quindi è possibile sovrascriverlo in un altro file di unità. Molto utile per il test delle unità/beffardo:

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

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

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

Questa è una buona idea. Userò la prossima volta. Sfortunatamente, nel momento in cui ho posto la domanda, avevo a che fare con software che non potevo modificare per aggiungere un attributo simile. Questo è buono, tuttavia, e verrà sicuramente utilizzato nella mia casella degli strumenti in futuro. –

+0

Ebbene sì, se non è possibile modificare l'origine, la risposta di @ PeterHuewe è la soluzione che utilizza objcpy. Se è possibile modificare la fonte, questa sembra più facile da configurare. – MicroJoe