9

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

smcameron
  • 1,307
  • 8
  • 8
  • I have not used weak symbols yet, but they sound like weakening baz of your original module will let you replace it with a new one – x539 Mar 21 '12 at 14:32
  • I imagine that if he tries to do such things, he doesn't have access to source code – Jérôme Mar 21 '12 at 14:39
  • I do have access to the source code, but I would prefer not to modify the source in the usual way of unit testing. Actually this is for testing a linux kernel module. Upstream, they generally don't like you putting changes in the source just for accommodating things which are not the kernel, and for patch compatibility, I don't want to mess with the source unnecessarily. – smcameron Mar 21 '12 at 15:06
  • Weaken doesn't work: [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 baz wrapped baz [scameron@localhost ~]$ (Ugh. Stackoverflow comment formatting sucks.) – smcameron Mar 21 '12 at 15:08
  • This looks interesting though:http://www.cs.virginia.edu/kim/publicity/pin/docs/45467/Pin/html/index.html#ReplaceSigProbed – smcameron Mar 21 '12 at 15:10
  • This also looks interesting: http://dynamorio.org/ – smcameron Mar 21 '12 at 16:10

3 Answers3

4

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.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131
  • I get around the inlining by compiling with -fno-inline. There are no functions I'm interested in which get deleted entirely by the compiler (else I would have deleted them already myself.) There may be some corner cases that I can't deal with, but that's ok. I'm more interested in the typical case. I am not opposed to mechanically modifying the machine code at the call sites to achieve this end, just need to figure out how to do it. – smcameron Mar 21 '12 at 17:31
2

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

smcameron
  • 1,307
  • 8
  • 8
0

If you are okay with modifying machine code then you should have no problem modifying source code.

Write scripts to mechanically produce unit test sources from the real sources. Perl does this very well.

Zan Lynx
  • 53,022
  • 10
  • 79
  • 131