gcc linker mocking ld interceptor

GNU gcc/ld-envolviendo una llamada al símbolo con el llamador y el destinatario definidos en el mismo archivo de objeto



linker mocking (5)

Debes debilitar y globalizar el símbolo 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.

Esto funcionó para mí

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

Debilite el símbolo foo y hágalo global, de modo que esté disponible para el vinculador de nuevo.

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

Ahora puedes vincular tu nuevo obj con el wrap de bar.c

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

Prueba

$ ./nowrap main foo

Y el envuelto:

$ ./wrapme main Wrap-FU

para aclarar, mi pregunta se refiere a envolver / interceptar llamadas de una función / símbolo a otra función / símbolo cuando la persona que llama y la persona que llama se definen en la misma unidad de compilación con el compilador y el vinculador de GCC.

Tengo una situación parecida a la siguiente:

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

Me gustaría envolver las llamadas a estas funciones, y puedo hacerlo (hasta cierto punto) con la opción ld''s --wrap (y luego implemento __wrap_foo y __wrap_bar que a su vez llaman a __real_foo y __real_bar como esperaba el resultado de ld''s --wrap opción de --wrap ).

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

El problema que tengo es que esto solo tiene efecto para las referencias a foo y bar desde fuera de esta unidad de compilación (y se resuelven en el momento del enlace). Es decir, las llamadas a foo y bar de otras funciones dentro de foo.c no se envuelven.

Intenté usar objcopy --redefine-sym , pero eso solo cambia el nombre de los símbolos y sus referencias.

Me gustaría reemplazar las llamadas a foo y bar (dentro de foo.o) a __wrap_foo y __wrap_bar (igual que se resuelven en otros archivos de objeto mediante la opción --wrap del enlazador) ANTES de pasar los archivos * .o al enlazador --wrap opciones de --wrap y sin tener que modificar el código fuente de foo.c

De esta forma, la envoltura / interceptación tiene lugar para todas las llamadas a foo y bar , y no solo a las que tienen lugar fuera de foo.o.

es posible?


Esto parece estar funcionando como está documentado:

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

Tenga en cuenta lo indefinido anterior. Cuando el enlazador procesa foo.o , la bar() no está indefinida, por lo que el enlazador no lo ajusta. No estoy seguro de por qué se hace de esa manera, pero probablemente exista un caso de uso que lo requiera.


Puede usar __attribute__((weak)) antes de la implementación del llamado para permitirle a alguien volver a implementarlo sin que GCC grite sobre múltiples definiciones.

Por ejemplo, supongamos que quiere simular la función world en la siguiente unidad de código hello.c . Puede anteponer el atributo para poder anularlo.

#include "hello.h" #include <stdio.h> __attribute__((weak)) void world(void) { printf("world from lib/n"); } void hello(void) { printf("hello/n"); world(); }

Y luego puede anularlo en otro archivo de unidad. Muy útil para pruebas unitarias / burlas:

#include <stdio.h> #include "hello.h" /* overrides */ void world(void) { printf("world from main.c"/n); } void main(void) { hello(); return 0; }


Puedes lograr lo que quieras si usas --undefined con - --wrap

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


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

Y luego simplemente compila este código y depura. Cuando llame al reall malloc, la función llamada será __wrap_malloc y __real_malloc llamada malloc.

Creo que esta es la forma de interceptar las llamadas.

Básicamente es la opción --wrap proporcionada por ld.