Переименовать функцию без изменения ее ссылок - PullRequest
2 голосов
/ 19 октября 2019

У меня есть объектный файл, скомпилированный с использованием gcc с опцией -ffunction-section. У меня есть доступ к исходному файлу, но я не могу его изменить.

file.c
void foo(void)
{
    bar();
}
void bar(void)
{
    abc();
}

То, чего я пытаюсь добиться, - это заставить все ссылки на bar принимать абсолютный адрес (который я назначу в компоновщикеscript) тогда как bar будет размещен компоновщиком по какому-то другому адресу.

Возможное решение - переименовать bar в file_bar без изменения вызова bar внутри foo (). Я попытался использовать objcopy -redefine-syms, но, похоже, он переименовывает даже вызовы bar.

Решение, предоставленное busybee, решает проблему, если функции не находятся в одном модуле компиляции. foo1.c

#include <stdio.h>
extern void bar1();
void foo1(){
printf("foo1\n");
}
int main(){
printf("main\n");
foo1();
bar1();
}

bar1.c

#include <stdio.h>
void bar1(){
printf("bar1\n");
}

wrapper.c

#include <stdio.h>
void __wrap_foo1(){
printf("wrap_foo1\n");
}
void __wrap_bar1(){
printf("wrap_bar1\n");
}

Сейчас,

$ gcc -c -ffunction-sections foo1.c bar1.c wrapper.c
$ gcc -Wl,--wrap=foo1 -Wl,--wrap=bar1 -o output foo1.o bar1.o wrapper.o
$ ./output
main
foo1
wrap_bar1

1 Ответ

1 голос
/ 19 октября 2019

Все функции, которые нужно перенаправить, находятся в своем собственном модуле компиляции.

У компоновщика есть опция «--wrap», которая заменяет все ссылки на символ «xxx» на «__wrap_xxx», а сам символ на «__real_xxx». Он используется для помещения функции-обертки в качестве «перехватчика» между вызовом и функцией.

Но с помощью этой опции вы можете делать все что угодно с этими символами в вашем скрипте компоновщика. Вам просто нужно определить "__wrap_xxx" с символом, чтобы ссылки были разрешаемыми.

В зависимости от ваших потребностей вы также можете написать фиктивную функцию с именем "__wrap_xxx ()", которая даже не вызывает "__real_xxx ()». Или вы можете поместить «__real_xxx» в таблицу векторов, или ... все, что вы можете придумать.

Все перенаправляемые функции не являются статичными («глобальными»), исправляя непосредственные значения

Я просмотрел ответы на другой вопрос ОП, опубликованный в комментарии. Это дало мне идею ослабить рассматриваемые символы и переопределить их значением с помощью компоновщика.

Этот пример может дать вам некоторое представление. Я тестировал в Linux, в котором рандомизация размещения адресного пространства , поэтому все адреса смещены от случайной базы. Но для целевой системы ОП она должна работать как положено.

foo1.c

Из-за произвольных значений для перенаправленных адресов функции не могут быть вызваны. Но программа может распечатать их адреса.

#include <stdio.h>

void foo1(void) {
}

extern void bar1(void);

int main(void) {
  printf("%p\n", main);
  printf("%p\n", foo1);
  printf("%p\n", bar1);
  return 0;
}

bar1.c

void bar1(void) {
}

wrapper.ld

Это первая альтернатива, которая дает компоновщику адреса для использования, дополнительный скрипт компоновщика. Второй см. Ниже. Стандартный скрипт компоновщика будет дополнен, нет необходимости копировать и исправлять его. Из-за простой структуры это, вероятно, самый простой способ предоставить много перенаправленных адресов, которые можно легко автоматизировать.

foo1 = 0x1000;
bar1 = 0x2000;

Примечание. Это не C! Это синтаксис «сценария компоновщика», который очень похож.

Как я построил и протестировал

Эта последовательность команд может быть автоматизирована и отсортирована для вашегосимпатия. В частности, вызовы objcopy могут быть выполнены с помощью некоторого цикла по списку.

gcc -c -ffunction-sections foo1.c
objcopy --weaken-symbol=foo1 foo1.o foo2.o

gcc -c -ffunction-sections bar1.c
objcopy --weaken-symbol=bar1 bar1.o bar2.o

gcc foo1.o bar1.o -o original
echo original
./original

gcc foo2.o bar2.o -o weakened
echo weakened
./weakened

gcc foo2.o bar2.o wrapper.ld -o redirected
echo redirected
./redirected

Вместо дополнительного сценария компоновщика определения символов также могут быть даны в командной строке. Это упомянутая вторая альтернатива.

gcc foo2.o bar2.o -Wl,--defsym=foo1=0x1000 -Wl,--defsym=bar1=0x2000 -o redirected

Кстати, компоновщик понимает @file для чтения всех аргументов из файла file. Таким образом, размер команды компоновщика не ограничен.

Все перенаправляемые функции нестатичны («глобальны»), перезаписываются новыми функциями

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

wrapper.c

Да, верно, имена равнына имена оригиналов! Поскольку мы сделали символы исходных функций слабыми , мы не получим сообщение об ошибке от компоновщика, когда он перезаписывает ссылки адресами новых функций.

void foo1(void) {
}

void bar1(void) {
}

Buildперенаправленная программа, подобная этой (показаны только новые команды):

gcc -c -ffunction-sections wrapper.c

gcc foo2.o bar2.o wrapper.o -o redirected

Функция для перенаправления: static

Ну, в зависимости от вашей целевой архитектуры это, вероятно, будет невозможно. Это из-за записи перемещения ссылки. Это будет своего рода родственник, говорящий компоновщику разрешить с помощью смещения в разделе функции вместо разрешения по символу функции.

Я не исследовал это далее.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...