8

I have a static library of C files, compiled with g++ on Cygwin. I wish to unit test one function that is defined in the library. That function calls another function defined in that library and I wish to override the dependency to replace it with my own version of that function. I can't modify what's in the static library, so this solution [ Override a function call in C ] doesn't apply.

Usually, I can write a .cpp file and include the .c file containing the function I want to unit test, which essentially extends that file with the code I add. It's a dirty trick I'd never use for production code but it's handy for unit testing C files, because it gives my test code access to static things in that C file. Then, I can write in my fake version of the dependency, and my unit test function that calls the function I'm testing. I compile my.cpp to get my.o, then link it with the static library. In theory, since the linker has found a definition for the dependency already (the one I provide) it won't look in the library and there will be no conflict. Usually this works, but now I'm getting a "multiple definition" error where the linker first finds my fake and then finds the real one. I don't know what might cause this and don't know what to look for. I also can't boil this down to a simple example because my simple examples don't have this problem.

Ideas please?

Community
  • 1
  • 1
jasper77
  • 1,553
  • 5
  • 19
  • 31
  • 2
    You could change the code at runtime to use an unconditional `JMP` to the replacement function. This technique is called hooking. – David Heffernan Nov 03 '11 at 21:22

3 Answers3

5

One possibility (admittedly, and ugly one, but...) is to extract the individual object files from the static library. If the function you're calling and the function it's calling are in separate object files, you can link against the object file containing the function you need to call, but not against the one containing the function it calls.

This only gives you granularity on the level of complete object files though, so if the two functions involved are both in the same object file, it won't work. If you really need to get things to work, and don't mind making a really minor modification to the object file in question, you may be able to use a binary editor to mark the second function as a weak external, which means it'll be used in the absence of any other external with the same name, but if another is provided, that will be used instead.

Whether that latter qualifies as "modifying the library" or not depends a bit on your viewpoint. It's not modifying the code in the library, but is modifying a bit of the object file wrapper around that code. My guess is that you'd rather not do it, but it may still be the cleanest way out of an otherwise untenable situation.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Interesting ideas for possible approaches to solving the problem. Do you have any idea why it may be a problem in the first place? – jasper77 Nov 04 '11 at 00:12
  • @jasper77: I guess you could approach that from two directions. One is that you're apparently using the code in a way they never intended. The other is that they didn't design the code to be as flexible as you apparently need (or at least want). – Jerry Coffin Nov 04 '11 at 03:27
  • I'm selecting your answer because some things you said prompted a thought process that eventually led to the answer. – jasper77 Nov 04 '11 at 18:48
  • regarding: `you may be able to use a binary editor to mark the second function as a weak external`, this can be done with `objcopy -W symbol_to_become_weak somefile.o modified_file.o` – mvds Sep 19 '16 at 01:31
1

It turns out the reason the linker found both defintions of the function is that the faked function's source file defined a variable which is extern'ed in its header file. That unresolved external in the header file caused the linker to link the faked function's object file (the whole thing) to the tested function's file inside the library. So, it's impossible to extract the definition of the tested function without the definition for the dependency.

What I ended up doing was similar to Override a function call in C where I used a different function name instead of the same one, and a preprocessor directive to swap the two. I put the preprocessor directive and the fake function in a separate file which can be included in a unit test, so the production code in the library does not have to be touched. Plus, if I want to fake that same function for another unit test somewhere else, I can re-use the new file.

Community
  • 1
  • 1
jasper77
  • 1,553
  • 5
  • 19
  • 31
0

Depending on your platform and performance requirements, you might be able to use pin to dynamically modify the application and replace one function with another at runtime.

There's no direct example in the manual, but you could easily modify one of the sample pin tools to do this.

Christoffer
  • 12,712
  • 7
  • 37
  • 53