web-dev-qa-db-fra.com

GNU gcc / ld - encapsuler un appel au symbole avec l'appelant et l'appelé définis dans le même fichier objet

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.

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

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?

35
luis.espinal

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
16
PeterHuewe
#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.

11
Ritesh

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;
}
6
MicroJoe

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.

5
Employed Russian

Vous pouvez obtenir ce que vous voulez si vous utilisez --undefined avec --wrap

  -u SYMBOL, --undefined SYMBOL
                              Start with undefined reference to SYMBOL
4
jhyoon

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$
1
Javier Escalada