Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Override a function call in C from the same compilation unit

I am trying to Override a function call in C, but I am facing a problem when the function is used in the same compilation unit. In the code below, I am trying to replace the function get_resolution(), but I am able to achieve it only if done in test.c but not from display.c

// display.c -------------------------------------------------------------

#include <stdio.h>

void get_resolution()
{
    printf("Original get_resolution\n");
}

void display()
{
    get_resolution();
}

// test.c ----------------------------------------------------------------

#include <stdio.h>

void __wrap_get_resolution()
{
    printf("Mock get_resolution\n");
    // __real_get_resolution(); // Should be possible to call original
}

int main()
{
    display();         // **ISSUE** Original get_resolution() is called
    get_resolution();  // __wrap_get_resolution() is called
    return 0;
}

// gcc -Wl,--wrap,get_resolution display.c test.c

My requirement is that, when I call display() from main(), I want __wrap_get_resolution() to be execute but I always see that the original get_resolution() is being called. A little analysis of the dis-assembly reveals that the function get_resolution is called differently:

In display() -> The address of get_resolution() is already resolved

00000000 <_get_resolution>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 18                sub    $0x18,%esp
   6:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
   d:   e8 00 00 00 00          call   12 <_get_resolution+0x12>
  12:   c9                      leave  
  13:   c3                      ret    

00000014 <_display>:
  14:   55                      push   %ebp
  15:   89 e5                   mov    %esp,%ebp
  17:   83 ec 08                sub    $0x8,%esp
  1a:   e8 e1 ff ff ff          call   0 <_get_resolution>
  1f:   c9                      leave  
  20:   c3                      ret    
  21:   90                      nop
  22:   90                      nop
  23:   90                      nop

In main() -> The address of get_resolution is not yet resolved

00000000 <___wrap_get_resolution>:
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   83 ec 18                sub    $0x18,%esp
   6:   c7 04 24 00 00 00 00    movl   $0x0,(%esp)
   d:   e8 00 00 00 00          call   12 <___wrap_get_resolution+0x12>
  12:   c9                      leave  
  13:   c3                      ret    

00000014 <_main>:
  14:   55                      push   %ebp
  15:   89 e5                   mov    %esp,%ebp
  17:   83 e4 f0                and    $0xfffffff0,%esp
  1a:   e8 00 00 00 00          call   1f <_main+0xb>
  1f:   e8 00 00 00 00          call   24 <_main+0x10>
  24:   e8 00 00 00 00          call   29 <_main+0x15>
  29:   b8 00 00 00 00          mov    $0x0,%eax
  2e:   c9                      leave  
  2f:   c3                      ret    

The question now is, how to prevent the compiler from resolving the address of get_resolution() used in the function display() and instead use the relocation table, so that the get_resolution() function can be overridden during the linking stage?

EDIT:

  1. Based on hroptatyr's response, adding void get_resolution() __attribute__((weak)); solves the problem when using mingw-gcc but not in my target platform QNX/ARM/gcc(4.4.2)
  2. Even a run time method like function hook is acceptable if someone can point to a good library with support to ARM target.
like image 528
vijairaj Avatar asked Aug 01 '12 11:08

vijairaj


People also ask

Can I override a function in C?

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.

What is overriding in C?

When the base class and derived class have member functions with exactly the same name, same return-type, and same arguments list, then it is said to be function overriding.


1 Answers

Just use the preprocessor for that:

void __wrap_get_resolution()
{
    /* calling the real one here */
    get_resolution();
}

#define get_resolution   __wrap_get_resolution

int main()
{
    /* the __wrap... gets called */
    get_resolution();
    ...
}

The idea is to "rename" function calls to your wrapper functions after you put all the code that needs to see the original functions.

A dirtier version could be to shadow the function address locally, like so:

int main()
{
    void(*get_resolution)() = __wrap_get_resolution;
    get_resolution();
    ...
}

This one would work but could give you some nasty warnings.

Edit
Even though pointed out in the comments that changing display.c is not desired, here the weak attribute solution, from http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

Weak symbols are supported for ELF targets, and also for a.out targets when using the GNU assembler and linker.

/* in display.c */
void __real_get_resolution()
{
    ...
}
void get_resolution() __attribute__((weak, alias("__real_get_resolution")));

/* in test.c */
void get_resolution()
{
    /* this version will take precedence over get_resolution() in display.c */
    ...
    /* lastly call the real thing */
    __real_get_resolution();
}

and from now on, wherever you call get_resolution() (and have the "strong" version compiled in) the wrapped version gets called.

like image 166
hroptatyr Avatar answered Oct 09 '22 23:10

hroptatyr