Override a function call in C

I want to override certain function calls to various APIs for the sake of logging the calls, but I also might want to manipulate data before it is sent to the actual function.

For example, say I use a function called getObjectName thousands of times in my source code. I want to temporarily override this function sometimes because I want to change the behaviour of this function to see the different result.

I create a new source file like this:

#include <apiheader.h>    

const char *getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return "name should be here";
}

I compile all my other source as I normally would, but I link it against this function first before linking with the API’s library. This works fine except I can obviously not call the real function inside my overriding function.

Is there an easier way to “override” a function without getting linking/compiling errors/warnings? Ideally I want to be able to override the function by just compiling and linking an extra file or two rather than fiddle around with linking options or altering the actual source code of my program.

If it’s only for your source that you want to capture/modify the calls, the simplest solution is to put together a header file (intercept.h) with:

#ifdef INTERCEPT
    #define getObjectName(x) myGetObjectName(x)
#endif

Then you implement the function as follows (in intercept.c which doesn’t include intercept.h):

const char *myGetObjectName (object *anObject) {
    if (anObject == NULL) return "(null)";
    return getObjectName(anObject);

Then make sure each source file where you want to intercept the call has the following at the top:

#include "intercept.h"

When you compile with “-DINTERCEPT“, all files will call your function rather than the real one, whereas your function will still call the real one.

Compiling without the “-DINTERCEPT” will prevent interception from occurring.

It’s a bit trickier if you want to intercept all calls (not just those from your source) – this can generally be done with dynamic loading and resolution of the real function (with dlload- and dlsym-type calls) but I don’t think it’s necessary in your case.

With gcc, under Linux you can use the --wrap linker flag like this:

gcc program.c -Wl,-wrap,getObjectName -o program

and define your function as:

const char *__wrap_getObjectName (object *anObject)
{
    if (anObject == NULL)
        return "(null)";
    else
        return __real_getObjectName( anObject ); // call the real function
}

This will ensure that all calls to getObjectName() are rerouted to your wrapper function (at link time). This very useful flag is however absent in gcc under Mac OS X.

Remember to declare the wrapper function with extern "C" if you’re compiling with g++ though.

You can override a function using LD_PRELOAD trick – see man ld.so. You compile shared lib with your function and start the binary (you even don’t need to modify the binary!) like LD_PRELOAD=mylib.so myprog.

In the body of your function (in shared lib) you write like this:

const char *getObjectName (object *anObject) {
  static char * (*func)();

  if(!func)
    func = (char *(*)()) dlsym(RTLD_NEXT, "getObjectName");
  printf("Overridden!n");     
  return(func(anObject));    // call original function
}

You can override any function from shared library, even from stdlib, without modifying/recompiling the program, so you could do the trick on programs you don’t have a source for. Isn’t it nice?

If you use GCC, you can make your function weak. Those can be overridden by non-weak functions:

test.c:

#include <stdio.h>

__attribute__((weak)) void test(void) { 
    printf("not overridden!n"); 
}

int main() {
    test();
}

What does it do?

$ gcc test.c
$ ./a.out
not overridden!

test1.c:

#include <stdio.h>

void test(void) {
    printf("overridden!n");
}

What does it do?

$ gcc test1.c test.c
$ ./a.out
overridden!

Sadly, that won’t work for other compilers. But you can have the weak declarations that contain overridable functions in their own file, placing just an include into the API implementation files if you are compiling using GCC:

weakdecls.h:

__attribute__((weak)) void test(void);
... other weak function declarations ...

functions.c:

/* for GCC, these will become weak definitions */
#ifdef __GNUC__
#include "weakdecls.h"
#endif

void test(void) { 
    ...
}

... other functions ...

Downside of this is that it does not work entirely without doing something to the api files (needing those three lines and the weakdecls). But once you did that change, functions can be overridden easily by writing a global definition in one file and linking that in.

It is often desirable to modify the
behavior of existing code bases by
wrapping or replacing functions. When
editing the source code of those
functions is a viable option, this can
be a straight-forward process. When
the source of the functions cannot be
edited (e.g., if the functions are
provided by the system C library),
then alternative techniques are
required. Here, we present such
techniques for UNIX, Windows, and
Macintosh OS X platforms.

This is a great PDF covering how this was done on OS X, Linux and Windows.

It doesn’t have any amazing tricks that haven’t been documented here (this is an amazing set of responses BTW)… but it is a nice read.

Intercepting arbitrary functions on Windows, UNIX, and Macintosh OS X platforms (2004), by Daniel S. Myers and Adam L. Bazinet.

You can download the PDF directly from an alternate location (for redundancy).

And finally, should the previous two sources somehow go down in flames, here’s a Google search result for it.

You can define a function pointer as a global variable. The callers syntax would not change. When your program starts, it could check if some command-line flag or environment variable is set to enable logging, then save the function pointer’s original value and replace it with your logging function. You would not need a special “logging enabled” build. Users could enable logging “in the field”.

You will need to be able to modify the callers’ source code, but not the callee (so this would work when calling third-party libraries).

foo.h:

typedef const char* (*GetObjectNameFuncPtr)(object *anObject);
extern GetObjectNameFuncPtr GetObjectName;

foo.cpp:

const char* GetObjectName_real(object *anObject)
{
    return "object name";
}

const char* GetObjectName_logging(object *anObject)
{
    if (anObject == null)
        return "(null)";
    else
        return GetObjectName_real(anObject);
}

GetObjectNameFuncPtr GetObjectName = GetObjectName_real;

void main()
{
    GetObjectName(NULL); // calls GetObjectName_real();

    if (isLoggingEnabled)
        GetObjectName = GetObjectName_logging;

    GetObjectName(NULL); // calls GetObjectName_logging();
}

Building on @Johannes Schaub’s answer with a solution suitable for code you don’t own.

Alias the function you want to override to a weakly-defined function, and then reimplement it yourself.

override.h

#define foo(x) __attribute__((weak))foo(x)

foo.c

function foo() { return 1234; }

override.c

function foo() { return 5678; }

Use pattern-specific variable values in your Makefile to add the compiler flag -include override.h.

%foo.o: ALL_CFLAGS += -include override.h

Aside: Perhaps you could also use -D 'foo(x) __attribute__((weak))foo(x)' to define your macros.

Compile and link the file with your reimplementation (override.c).

  • This allows you to override a single function from any source file, without having to modify the code.

  • The downside is that you must use a separate header file for each file you want to override.

There’s also a tricky method of doing it in the linker involving two stub libraries.

Library #1 is linked against the host library and exposes the symbol being redefined under another name.

Library #2 is linked against library #1, interecepting the call and calling the redefined version in library #1.

Be very careful with link orders here or it won’t work.

You could use a shared library (Unix) or a DLL (Windows) to do this as well (would be a bit of a performance penalty). You can then change the DLL/so that gets loaded (one version for debug, one version for non-debug).

I have done a similar thing in the past (not to achieve what you are trying to achieve, but the basic premise is the same) and it worked out well.

[Edit based on OP comment]

In fact one of the reasons I want to
override functions is because I
suspect they behave differently on
different operating systems.

There are two common ways (that I know of) of dealing with that, the shared lib/dll way or writing different implementations that you link against.

For both solutions (shared libs or different linking) you would have foo_linux.c, foo_osx.c, foo_win32.c (or a better way is linux/foo.c, osx/foo.c and win32/foo.c) and then compile and link with the appropriate one.

If you are looking for both different code for different platforms AND debug -vs- release I would probably be inclined to go with the shared lib/DLL solution as it is the most flexible.

I tried @vaughan’s solution and I think there’s something more to say.

main.c

#include <stdio.h>

void main (void)
{
    func1();

}

original.c: This file contains the function you want to override and assume you only have the object file.

#include <stdio.h>

void func1 (void)
{
    printf("in original func1()n");
}

mock.c: This file contains your implementation for the overridden function.

#include <stdio.h>

void func1 (void)
{
    printf("in mock func1()n");
}

decl.h:

 void func1 (void); // no weak declaration at all

Makefile1: link with mock .o and original .a

ALL:
    gcc -c mock.c -o mock.o
    gcc -c original.c -o original.o
    ar cr original.a original.o <============ HERE use archive
    gcc -include override.h main.c mock.o original.a -o main

Makefile2: link with both mock .o and original .o

ALL:
    gcc -c mock.c -o mock.o
    gcc -c original.c -o original.o  <============= NO archive
    gcc -include override.h main.c mock.o original.o -o main

Makefile3: link with both original .a and mock .a

ALL:
    gcc -c mock.c -o mock.o
    gcc -c original.c -o original.o
    ar cr mock.a mock.o
    ar cr original.a original.o
    gcc -include override.h main.c mock.a original.a -o main

With Makefile1, output:

[email protected]:~/source/override$ make ALL
gcc -c mock.c -o mock.o
gcc -c original.c -o original.o
ar cr original.a original.o
gcc -include override.h main.c mock.o original.a -o main

[email protected]:~/source/override$ ./main
in mock func1()

With Makefile2, output:

gcc -c mock.c -o mock.o
gcc -c original.c -o original.o
gcc -include override.h main.c mock.o original.o -o main
original.o: In function `func1':
original.c:(.text+0x0): multiple definition of `func1'
mock.o:mock.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
Makefile:2: recipe for target 'ALL' failed
make: *** [ALL] Error 1

With Makefile3, output:

[email protected]:~/source/override$ make ALL -f Makefile3
gcc -c mock.c -o mock.o
gcc -c original.c -o original.o
ar cr mock.a mock.o
ar cr original.a original.o
gcc -include decl.h main.c mock.a original.a -o main

[email protected]:~/source/override$ ./main
in mock func1()

So let’s check the symbols:

With Makefile1 and Makefile3:

[email protected]:~/source/override$ nm mock.a

mock.o:
0000000000000000 T func1  <=========== strong symbol
                 U _GLOBAL_OFFSET_TABLE_
                 U puts

[email protected]:~/source/override$ nm original.a

original.o:
0000000000000000 T func1  <=========== strong symbol
                 U _GLOBAL_OFFSET_TABLE_
                 U puts

So, it seems the critical thing is, we don’t need to use the __attribute__((weak)), just remember that:

  • a function defined in the object file can override an archive file.
  • a function defined in the first archive file can override the latter ones.

If change the decl.h to this:

 __attribute__((weak)) void func1 (void);

The Makefile3 will fail:

[email protected]:~/source/override$ make ALL -f Makefile3
gcc -c mock.c -o mock.o
gcc -c original.c -o original.o
ar cr mock.a mock.o
ar cr original.a original.o
gcc -include decl.h main.c mock.a original.a -o main

[email protected]:~/source/override$ ./main
Segmentation fault (core dumped)

I further tried:

Makefile4: link with mock.a alone

ALL:
    gcc -c mock.c -o mock.o
    ar cr mock.a mock.o
    gcc -include decl.h main.c mock.a -o main

Output:

Segmentation fault (core dumped)

Makefile5: link with mock.o alone

ALL:
    gcc -c mock.c -o mock.o
    ar cr mock.a mock.o
    gcc -include decl.h main.c mock.o -o main

Output:

in mock func1()

The symbols dump with nm:

Makefile4:

0000000000000824 r __FRAME_END__
                 w func1 <================ func1 is a weak symbol
0000000000200fb8 d _GLOBAL_OFFSET_TABLE_

Makefile5:

000000000000085c r __FRAME_END__
0000000000000646 T func1 <================ func1 is a strong symbol and has an address
0000000000200fb8 d _GLOBAL_OFFSET_TABLE_

I guess this is related to the segment fault. So I disassemble the whole binary (the one with segment fault) and check the func1 part, where I cannot find the func1 body:

0000000000000520 <[email protected]>:
 520:   ff 25 aa 0a 20 00       jmpq   *0x200aaa(%rip)        # 200fd0 <func1>
 526:   68 00 00 00 00          pushq  $0x0
 52b:   e9 e0 ff ff ff          jmpq   510 <.plt>

and:

000000000000064a <main>:
 64a:   55                      push   %rbp
 64b:   48 89 e5                mov    %rsp,%rbp
 64e:   e8 cd fe ff ff          callq  520 <[email protected]>
 653:   90                      nop

While for the binary without segment fault, the disassembly looks like below, where I can find the func1 body:

000000000000063a <main>:
 63a:   55                      push   %rbp
 63b:   48 89 e5                mov    %rsp,%rbp
 63e:   e8 03 00 00 00          callq  646 <func1>

and:

0000000000000646 <func1>:
 646:   55                      push   %rbp
 647:   48 89 e5                mov    %rsp,%rbp
 64a:   48 8d 3d 93 00 00 00    lea    0x93(%rip),%rdi        # 6e4 <_IO_stdin_used+0x4>
 651:   e8 ba fe ff ff          callq  510 <[email protected]>
 656:   90                      nop
 657:   5d                      pop    %rbp
 658:   c3                      retq   
 659:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)

So far, I still cannot fully explain why the segment fault is happening. I need to check for more materials. Hope someone can shed some light before I got the answer.

Leave a Comment