Можно ли переопределить статические функции в объектном модуле (gcc, ld, x86, objcopy)?

Есть ли способ переопределить функции со статической областью видимости в объектном модуле?

Если я начну с чего-то подобного, то модуль с глобальным символом «foo» — это функция, которая вызывает локальный символ «bar», который вызывает локальный символ «baz».

[scameron@localhost ~]$ cat foo.c
#include <stdio.h>
static void baz(void)
{
    printf("baz\n");
}

static void bar(void)
{
    printf("bar\n");
    baz();
}

void foo(void)
{
    printf("foo\n");
    bar();
}

[scameron@localhost ~]$ gcc -g -c foo.c
[scameron@localhost ~]$ objdump -x foo.o | egrep 'foo|bar|baz'
foo.o:     file format elf32-i386
foo.o
00000000 l    df *ABS*  00000000 foo.c
00000000 l     F .text  00000014 baz
00000014 l     F .text  00000019 bar
0000002d g     F .text  00000019 foo

Он имеет один глобальный «foo» и два локальных «bar» и «baz».

Предположим, я хочу написать несколько модульных тестов, которые тренируют bar и baz, я могу это сделать:

[scameron@localhost ~]$ cat barbaz
bar
baz
[scameron@localhost ~]$ objcopy --globalize-symbols=barbaz foo.o foo2.o
[scameron@localhost ~]$ objdump -x foo2.o | egrep 'foo|bar|baz'
foo2.o:     file format elf32-i386
foo2.o
00000000 l    df *ABS*  00000000 foo.c
00000000 g     F .text  00000014 baz
00000014 g     F .text  00000019 bar
0000002d g     F .text  00000019 foo
[scameron@localhost ~]$ 

И теперь bar и baz являются глобальными символами и доступны извне модуля. Все идет нормально.

Но что, если я хочу вставить свою собственную функцию поверх «база», а «бар» будет вызывать мою вставленную «баз»?

Есть ли способ сделать это?

Опция --wrap, похоже, этого не делает...

[scameron@localhost ~]$ cat ibaz.c
#include <stdio.h>
extern void foo();
extern void bar();

void __wrap_baz()
{
    printf("wrapped baz\n");
}
int main(int argc, char *argv[])
{
    foo();
    baz();
}

[scameron@localhost ~]$ gcc -o ibaz ibaz.c foo2.o -Xlinker --wrap -Xlinker baz
[scameron@localhost ~]$ ./ibaz
foo
bar
baz
wrapped baz
[scameron@localhost ~]$

База, вызванная из main(), была обернута, но bar по-прежнему вызывает локальную базу, а не обернутую базу.

Есть ли способ заставить бар называть завернутый баз?

Даже если требуется изменить объектный код, чтобы возиться с адресами вызовов функций, если это можно сделать автоматически, этого может быть достаточно, но в этом случае он должен работать как минимум на i386 и x86_64.

-- Стив


person smcameron    schedule 21.03.2012    source источник
comment
Я еще не использовал слабые символы, но они звучат так, как будто ослабление базы вашего оригинального модуля позволит вам заменить его новым.   -  person x539    schedule 21.03.2012
comment
Я предполагаю, что если он пытается делать такие вещи, у него нет доступа к исходному коду.   -  person Jérôme    schedule 21.03.2012
comment
У меня есть доступ к исходному коду, но я бы предпочел не изменять исходный код обычным способом модульного тестирования. На самом деле это для тестирования модуля ядра Linux. Upstream, они, как правило, не любят, когда вы вносите изменения в исходный код только для размещения вещей, которые не являются ядром, и для совместимости патчей, я не хочу возиться с исходным кодом без необходимости.   -  person smcameron    schedule 21.03.2012
comment
Ослабление не работает: [scameron@localhost ~]$ objcopy --weaken-symbol=baz foo2.o foo3.o [scameron@localhost ~]$ gcc -o ibaz ibaz.c foo3.o -Xlinker --wrap - Xlinker baz [scameron@localhost ~]$ ./ibaz foo bar bazwrapped baz [scameron@localhost ~]$ (Тьфу. Форматирование комментариев Stackoverflow отстой.)   -  person smcameron    schedule 21.03.2012
comment
Это выглядит интересно:cs .virginia.edu/kim/publicity/pin/docs/45467/Pin/html/   -  person smcameron    schedule 21.03.2012
comment
Это тоже выглядит интересно: dynamorio.org   -  person smcameron    schedule 21.03.2012


Ответы (3)


Поскольку static является обещанием компилятору C, что функция или переменная являются локальными для файла, компилятор может удалить этот код, если он может получить тот же результат без него.

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

Все это означает, что вы не можете надежно перенаправить вызовы этой функции.

Если вы компилируете с новыми параметрами -lto, это еще хуже, потому что компилятор может свободно переупорядочивать, удалять или встраивать весь код во всем проекте.

person Zan Lynx    schedule 21.03.2012
comment
Я обхожу встраивание, компилируя с -fno-inline. Нет функций, которые меня интересуют, которые полностью удаляются компилятором (иначе я бы уже удалил их сам). Могут быть некоторые крайние случаи, с которыми я не могу справиться, но это нормально. Меня больше интересует типичный случай. Я не против механической модификации машинного кода на сайтах вызовов для достижения этой цели, просто нужно выяснить, как это сделать. - person smcameron; 21.03.2012

Я получил электронное письмо от Яна Лэнса Тейлора (автора золота, альтернативного компоновщика ld):

Есть ли способ переопределить функции со статической областью видимости в объектном модуле? (у меня x86_64 и i386 linux)

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

Я думаю, что со встраиванием можно справиться с помощью -fno-inline, но изменение соглашений о вызовах, вероятно, слишком много.

При этом ребята из DynamoRIO утверждают, что могут это сделать, но я этого не проверял: https://groups.google.com/forum/?fromgroups#!topic/dynamorio-users/xt8JTXBCZ74

person smcameron    schedule 22.03.2012
comment
Конечно, со встраиванием также можно справиться только путем перекомпиляции кода. - person 0xC0000022L; 12.11.2018

Если вы согласны с изменением машинного кода, у вас не должно возникнуть проблем с изменением исходного кода.

Напишите скрипты для механического создания исходников модульных тестов из реальных исходников. Perl делает это очень хорошо.

person Zan Lynx    schedule 26.03.2012