Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to override static functions in an object module (gcc, ld, x86, objcopy)?

Is there a way to override functions with static scope within an object module?

If I start with something like this, a module with global symbol "foo" is a function that calls local symbol "bar," that calls local symbol "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

It has one global, "foo" and two locals "bar" and "baz."

Suppose I want to write some unit tests that exercise bar and baz, I can do:

[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 ~]$ 

And now bar and baz are global symbols and accessible from outside the module. So far so good.

But what if I want to interpose my own function on top of "baz", and have "bar" call my interposed "baz"?

Is there a way to do that?

--wrap option doesn't seem to do it...

[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 ~]$

The baz called from main() got wrapped, but bar still calls the local baz not the wrapped baz.

Is there a way to make bar call the wrapped baz?

Even if it requires modifying the object code to tinker with the addresses of function calls, if that can be done in an automated way, that might be good enough, but in that case it needs to work on at least i386 and x86_64.

-- steve

like image 266
smcameron Avatar asked Mar 21 '12 14:03

smcameron


2 Answers

Since static is a promise to the C compiler that the function or variable is local to the file, the compiler is free to remove that code if it can get the same result without it.

This might be inlining the function call. It might mean replacing a variable with constants. If the code is inside an if statement that is always false, the function may not even exist in the compiled result.

All of that means that you cannot reliably redirect calls to that function.

If you compile with the new -lto options it is even worse, because the compiler is free to reorder, remove or inline all of the code in the entire project.

like image 183
Zan Lynx Avatar answered Sep 28 '22 03:09

Zan Lynx


I received an email from Ian Lance Taylor (author of gold, an alternative linker to ld):

Is there a way to override functions with static scope within an object module? (I'm on x86_64 and i386 linux)

No, there isn't. In particular, the compiler may inline calls to static functions, and it may also rewrite the functions to use a different calling convention (GCC does both of these optimizations). So there is no reliable way to override a static function after the code has been compiled.

The inlining can be dealt with, I think, by -fno-inline, but changing the calling conventions is probably too much.

That being said, the DynamoRIO guys claim to be able to do it, but I haven't verified it: https://groups.google.com/forum/?fromgroups#!topic/dynamorio-users/xt8JTXBCZ74

like image 26
smcameron Avatar answered Sep 28 '22 04:09

smcameron