GNU GCC / ld-zawijanie wywołania do symbolu z wywołaniem i wywołaniem zdefiniowanym w tym samym pliku obiektowym

Dla wyjaśnienia, moje pytanie odnosi się do zawijania/przechwytywania połączeń z jednej funkcji/symbolu do innej funkcji/symbolu , gdy wywołujący i wywołujący są zdefiniowane w tej samej jednostce kompilacji za pomocą kompilatora GCC i linkera.

Mam sytuację przypominającą:

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

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

Chciałbym zawijać wywołania tych funkcji i mogę to zrobić (do pewnego punktu) za pomocą LD ' s --wrap option (a następnie implementuję _ _ wrap _ foo i_ _ wrap _ bar, które z kolei wywołanie _ _ real _ foo i _ _ real _ bar zgodnie z oczekiwaniami wynikającymi z opcji --wrap ld).

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

Problem, który mam polega na tym, że to działa tylko dla odniesień do foo i bar z poza tą jednostką kompilacji (i rozwiązane w czasie łącza). Oznacza to, że wywołania foo i bar z innych funkcji w foo.c nie daj się zapakować.

wywołania z jednostki kompilacji są rozwiązywane przed zawinięciem linkera

Próbowałem użyć objcopy --redefine-sym , ale to tylko zmienia nazwy symboli i ich referencje.

Chciałbym zamienić wywołania na foo i bar (w Foo.o) do __wrap_foo i __wrap_bar (tak jak zostaną rozwiązane w innych plikach obiektowych przez opcję --wrap linkera), zanim przekażę *.o pliki do opcji --wrap linkera i bez konieczności modyfikowania foo.kod źródłowy C.

W ten sposób owijanie/przechwytywanie odbywa się dla wszystkich połączeń do foo i bar, a nie tylko tych odbywających się poza foo.o. Czy to możliwe?
Author: luis.espinal, 2012-12-20

5 answers

Musisz osłabić i globalizować symbol za pomocą 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.

To zadziałało dla mnie

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

Skompiluj to

$ gcc -c foo.c bar.c 

Osłabić symbol foo i uczynić go globalnym, więc będzie znowu dostępny dla linkera.

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

Teraz możesz połączyć swój nowy obj z owijką z paska.c

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

Test

$ ./nowrap 
main
foo

I owinięty:

$ ./wrapme 
main
Wrap-FU
 9
Author: PeterHuewe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-07 19:28:19
#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);
}

A następnie skompiluj ten kod i debuguj. Kiedy wywołasz reall malloc, funkcja o nazwie will _ _ wrap _ malloc i _ _ real _ malloc wywoła malloc.

Myślę, że to jest sposób na przechwycenie połączeń.

W zasadzie jest to opcja --wrap dostarczana przez ld.

 6
Author: Ritesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-27 22:23:07

Wygląda na to, że działa to zgodnie z dokumentacją:

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

Zwróć uwagę na undefined powyżej. Gdy linker przetwarza foo.o, bar() jest Nie niezdefiniowany, więc linker go nie zawija. Nie jestem pewien dlaczego jest to zrobione w ten sposób, ale prawdopodobnie istnieje przypadek użycia, który tego wymaga.

 4
Author: Employed Russian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-04-28 08:04:15

Możesz osiągnąć to, czego chcesz, jeśli używasz --undefined z --wrap

  -u SYMBOL, --undefined SYMBOL
                              Start with undefined reference to SYMBOL
 3
Author: jhyoon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-11-18 03:12:01

Możesz użyć __attribute__((weak)) przed implementacją callee, aby ktoś mógł ją ponownie zaimplementować bez krzyczenia GCC o wielu definitonach.

Na przykład załóżmy, że chcesz wyśmiewać funkcję world w następującej hello.C jednostka kodu. Możesz dodać atrybut, aby móc go zastąpić.

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

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

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

I można go zastąpić w innym pliku jednostki. Bardzo przydatne do testów jednostkowych/wyśmiewania:

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

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

void main(void)
{
    hello();
    return 0;
}
 3
Author: MicroJoe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-09 12:05:49