9

有没有办法在对象模块中覆盖具有静态范围的函数?

如果我从这样的东西开始,具有全局符号“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 是全局符号,可以从模块外部访问。到目前为止,一切都很好。

但是如果我想在“baz”之上插入我自己的函数,并让“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() 调用的 baz 被包装了,但 bar 仍然调用本地 baz 而不是包装的 baz。

有没有办法让 bar 调用包装好的 baz?

即使它需要修改目标代码以修改函数调用的地址,如果可以以自动化方式完成,那可能就足够了,但在这种情况下,它至少需要在 i386 和 x86_64 上工作。

——史蒂夫

4

3 回答 3

4

由于static向 C 编译器承诺函数或变量在文件中是本地的,因此编译器可以自由地删除该代码,如果它可以在没有它的情况下获得相同的结果。

这可能是内联函数调用。这可能意味着用常量替换变量。如果代码位于if始终为假的语句中,则该函数甚至可能不存在于编译结果中。

所有这一切都意味着您无法可靠地将调用重定向到该函数。

如果您使用新-lto选项进行编译,情况会更糟,因为编译器可以自由地重新排序、删除或内联整个项目中的所有代码。

于 2012-03-21T16:55:36.163 回答
2

我收到了一封来自 Ian Lance Taylor(gold 的作者,ld 的替代链接器)的电子邮件:

有没有办法在对象模块中覆盖具有静态范围的函数?(我在 x86_64 和 i386 linux 上)

不,没有。特别是,编译器可以内联对静态函数的调用,也可以重写函数以使用不同的调用约定(GCC 进行了这两种优化)。因此,在编译代码后没有可靠的方法来覆盖静态函数。

我认为可以通过 -fno-inline 处理内联,但更改调用约定可能太多了。

话虽这么说,DynamoRIO 家伙声称能够做到,但我还没有验证它: https ://groups.google.com/forum/?fromgroups#!topic/dynamorio-users/xt8JTXBCZ74

于 2012-03-22T14:05:50.187 回答
0

如果您可以修改机器代码,那么修改源代码应该没有问题。

编写脚本以从真实源中机械地生成单元测试源。Perl 在这方面做得很好。

于 2012-03-26T19:02:30.987 回答