Подтвердить что ты не робот

GNU gcc/ld - завершение вызова символа с вызывающим и вызываемым, определенным в том же объектном файле

чтобы уточнить, мой вопрос относится к переносу/перехвату вызовов с одной функции/символа на другую функцию/символ , когда вызывающий и вызываемый определяются в одном модуле компиляции с помощью компилятора и компоновщика GCC.

У меня есть ситуация, напоминающая следующее:

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

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

Я хотел бы обернуть вызовы к этим функциям, и я могу сделать это (до точки) с помощью ld --wrap option (и затем я реализую __wrap_foo и __wrap_bar, которые, в свою очередь, вызывают __real_foo и __real_bar, как и ожидалось в результате параметра ld --wrap).

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

Проблема, с которой я столкнулась, заключается в том, что это вступает в силу только для ссылок на foo и bar из вне этого блока компиляции (и разрешено во время соединения). То есть, вызовы foo и bar из других функций внутри foo.c не обернуты.

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

Я попытался использовать objcopy --redefine-sym, но это только переименовывает символы и их ссылки.

Я хотел бы заменить вызовы на foo и bar (внутри foo.o) на __wrap_foo и __wrap_bar (так же, как они разрешаются в других объектных файлах с помощью компоновщика --wrap). ПЕРЕД Я передаю файлы *.o в компоновщик --wrap и не изменяя исходный код foo.c.

Таким образом, обертывание/перехват происходит для всех вызовов foo и bar, а не только тех, которые происходят вне foo.o.

Возможно ли это?

4b9b3361

Ответ 1

Вы должны ослабить и глобализовать символ, используя 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.

Это сработало для меня

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

Скомпилируйте его

$ gcc -c foo.c bar.c 

Ослабьте символ foo и сделайте его глобальным, поэтому он снова доступен для компоновщика.

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

Теперь вы можете связать свой новый obj с оберткой с bar.c

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

Тест

$ ./nowrap 
main
foo

И завернутый:

$ ./wrapme 
main
Wrap-FU

Ответ 2

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

А потом просто скомпилируйте этот код и отлаживайте. Когда вы вызываете reall malloc, вызываемая функция будет __wrap_malloc и __real_malloc вызовет malloc.

Я думаю, что это способ перехвата вызовов.

В основном это опция -wrap, предоставленная ld.

Ответ 3

Это работает как задокументированное:

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

Обратите внимание на undefined. Когда компоновщик обрабатывает foo.o, bar() не undefined, поэтому компоновщик не обертывает его. Я не уверен, почему это так, но, вероятно, это вариант использования, который требует этого.

Ответ 4

Вы можете использовать __attribute__((weak)) перед реализацией вызываемого абонента, чтобы позволить кому-либо переопределить его без GCC, кричащего о нескольких определениях.

Например, предположим, что вы хотите высмеять функцию world в следующем блоке кода hello.c. Вы можете добавить атрибут, чтобы его можно было переопределить.

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

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

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

И вы можете переопределить его в другом файле. Очень полезно для модульного тестирования/издевательств:

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

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

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

Ответ 5

Вы можете добиться того, чего хотите, если используете --undefined с --wrap

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