pour clarifier, ma question se réfère à l'habillage/interception des appels d'une fonction/symbole à une autre fonction/symbole lorsque l'appelant et l'appelé sont définis dans la même unité de compilation avec le compilateur et l'éditeur de liens GCC.
J'ai une situation semblable à la suivante:
/* foo.c */
void foo(void)
{
/* ... some stuff */
bar();
}
void bar(void)
{
/* ... some other stuff */
}
Je voudrais encapsuler les appels à ces fonctions, et je peux le faire (jusqu'à un certain point) avec ld's --wrap
option (puis j'implémente __wrap_foo et __wrap_bar qui à leur tour appellent __real_foo et __real_bar comme prévu par le résultat de ld's --wrap
option).
gcc -Wl,--wrap=foo -Wl,--wrap=bar ...
Le problème que j'ai est que cela ne prend effet que pour les références à foo et bar de en dehors de cette unité de compilation (et résolu au moment du lien). Autrement dit, les appels à foo et bar à partir d'autres fonctions dans foo.c ne sont pas encapsulés.
J'ai essayé d'utiliser objcopy --redefine-sym , mais cela ne renomme que les symboles et leurs références.
Je voudrais remplacer les appels à foo
et bar
(dans foo.o) à __wrap_foo
et __wrap_bar
(tout comme ils sont résolus dans d'autres fichiers objets par le lieur --wrap
option) AVANT de transmettre les fichiers * .o à l'éditeur de liens --wrap
options, et sans avoir à modifier le code source de foo.c.
De cette façon, l'habillage/interception a lieu pour tous les appels à foo
et bar
, et pas seulement ceux qui se déroulent en dehors de foo.o.
Est-ce possible?
Vous devez affaiblir et globaliser le symbole en utilisant 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.
Cela a fonctionné pour moi
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();
}
Compilez-le
$ gcc -c foo.c bar.c
Affaiblissez le symbole foo et rendez-le global, il est donc à nouveau disponible pour l'éditeur de liens.
$ objcopy foo.o --globalize-symbol=foo --weaken-symbol=foo foo2.o
Vous pouvez maintenant lier votre nouvel obj avec l'habillage de bar.c
$ gcc -o nowrap foo.o #for reference
$ gcc -o wrapme foo2.o bar.o
Tester
$ ./nowrap
main
foo
Et celui enveloppé:
$ ./wrapme
main
Wrap-FU
#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);
}
Et puis il suffit de compiler ce code et de déboguer. Lorsque vous appelez le reall malloc, la fonction appelée will __wrap_malloc et __real_malloc appellera malloc.
Je pense que c'est la façon d'intercepter les appels.
Fondamentalement, c'est l'option --wrap fournie par ld.
Vous pouvez utiliser __attribute__((weak))
avant l'implémentation de l'appelé afin de permettre à quelqu'un de le réimplémenter sans que GCC ne crie à propos de plusieurs définitions.
Par exemple, supposons que vous souhaitiez simuler la fonction world
dans l'unité de code hello.c suivante. Vous pouvez ajouter l'attribut pour pouvoir le remplacer.
#include "hello.h"
#include <stdio.h>
__attribute__((weak))
void world(void)
{
printf("world from lib\n");
}
void hello(void)
{
printf("hello\n");
world();
}
Et vous pouvez ensuite le remplacer dans un autre fichier d'unité. Très utile pour les tests unitaires/les moqueries:
#include <stdio.h>
#include "hello.h"
/* overrides */
void world(void)
{
printf("world from main.c"\n);
}
void main(void)
{
hello();
return 0;
}
Cela semble fonctionner comme documenté:
--wrap=symbol
Use a wrapper function for symbol.
Any undefined reference to symbol will be resolved to "__wrap_symbol". ...
Notez le non défini ci-dessus. Lorsque l'éditeur de liens traite foo.o
, La bar()
n'est pas non indéfinie, de sorte que l'éditeur de liens ne l'encapsule pas. Je ne sais pas pourquoi c'est fait de cette façon, mais il y a probablement un cas d'utilisation qui l'exige.
Vous pouvez obtenir ce que vous voulez si vous utilisez --undefined
avec --wrap
-u SYMBOL, --undefined SYMBOL
Start with undefined reference to SYMBOL
J'ai essayé la solution de @ PeterHuewe et cela fonctionne mais cela ne permet pas d'appeler la fonction d'origine depuis le wrapper. Pour permettre cela, ma solution est la suivante:
foo.c
#include <stdio.h>
void foo(){
printf("This is real foo\n");
}
int main(){
printf("main\n");
foo();
}
foo_hook.c
#include <stdio.h>
void real_foo();
int foo(){
printf("HOOK: BEFORE\n");
real_foo();
printf("HOOK: AFTER\n");
}
Makefile
all: link
link: hook
gcc -o wo_hook foo.o
gcc -o w_hook foo_hooked.o foo_hook.o
hook: build_o
objcopy \
foo.o \
--add-symbol real_foo=.text:$(Shell objdump -t foo.o | grep foo | grep .text | cut -d' ' -f 1),function,global \
--globalize-symbol=foo \
--weaken-symbol=foo \
foo_hooked.o
build_o:
gcc -c foo.c foo_hook.c
clean:
-rm w_hook wo_hook *.o
Exemple
virtualuser@virtualhost:~/tmp/link_time_hook$ make
gcc -c foo.c foo_hook.c
objcopy foo.o \
--add-symbol real_foo=.text:0000000000000000,function,global \
--globalize-symbol=foo \
--weaken-symbol=foo \
foo_hooked.o
gcc -o wo_hook foo.o
gcc -o w_hook foo_hooked.o foo_hook.o
virtualuser@virtualhost:~/tmp/link_time_hook$ ls
Makefile foo.c foo.o foo_hook.c foo_hook.o foo_hooked.o w_hook wo_hook
virtualuser@virtualhost:~/tmp/link_time_hook$ ./w_hook
main
HOOK: BEFORE
This is real foo
HOOK: AFTER
virtualuser@virtualhost:~/tmp/link_time_hook$
virtualuser@virtualhost:~/tmp/link_time_hook$ ./wo_hook
main
This is real foo
virtualuser@virtualhost:~/tmp/link_time_hook$