HQW.ang
HQW.ang

Reputation: 163

How to redirect a function call to a user-defined one of the same name?

Environment:

I have two dependent libraries (either static or shared, not sure whether this affects), libA and libB. A function foo() is defined in libA, and is called in libB. Now I have my application code app.c directly call functions from libB which will eventually call foo(). A simple workflow is app.c -> libB.a -> libA.a -> foo().

However, I want to change how foo() is implemented for some particular purpose. I hope app.c can have a magic function, if called, redirecting the call to foo() to the one provided by app.c. So the workflow, if could be achieved, should be

app.c -> libB.a -(other than foo())-> libA.a
 |      /
 |  (to call foo())
 v   /
foo()

The embarrassment is that I have fully control on app.c but minimal or even no access to those libraries. So is it possible to achieve such idea and how?

Update

As mentioned in comments, well missing this can lead to a totally different way of solution, the redirection is better configurable and happens at runtime. This is for test purpose so that normally the workflow should be what it is while only changes itself when testing is performed.

Upvotes: 0

Views: 929

Answers (1)

Employed Russian
Employed Russian

Reputation: 213656

Hopefully clearer statement of the problem: app.c calls bar(), which is defined in libB.a. bar() calls foo(), which is defined in libA.a.

Desired outcome: at runtime, optionally redirect the call to foo() to an alternate implementation foo_other() depending on command line argument or environment variable.


Solution (assuming the signature of foo is int foo(void)):

  1. Add this code to app.c:
int foo_other()
{
  printf("%s in %s:%d\n", __func__, __FILE__, __LINE__);
  return 42;
}

int __real_foo();
int __wrap_foo()
{
  printf("%s in %s:%d\n", __func__, __FILE__, __LINE__);
  if (getenv("TTT") != NULL) return foo_other();
  return __real_foo();
}
  1. Link your application as follows:
gcc app.c  libB.a libA.a -Wl,--wrap,foo

Result:

$ ./a.out
bar in bar.c:6
__wrap_foo in main.c:19
foo in foo.c:4

$ TTT=1 ./a.out
bar in bar.c:6
__wrap_foo in main.c:19
foo_other in main.c:13

Obviously you can remove the debug printf calls and change the mechanism used to switch between real foo() and foo_other() as you need.

P.S. For documentation for the linker --wrap flag, see man ld.

Example of using --wrap for mocking.

When using shared libraries, this approach will not work. But a different equivalent approach is available via function interpositioning.

Upvotes: 1

Related Questions