覆盖同一编译单元中C中的函数调用 [英] Override a function call in C from the same compilation unit

查看:46
本文介绍了覆盖同一编译单元中C中的函数调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试覆盖C语言中的函数调用,但是我在同一编译单元中使用该函数时遇到问题.在下面的代码中,我试图替换函数get_resolution(),但只有在test.c中完成,而不能从display.c

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

我的要求是,当我从main()调用display()时,我想执行__wrap_get_resolution(),但是我总是看到原始的get_resolution()被调用.对反汇编的一点分析表明,函数get_resolution的调用方式不同:

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:

在display()中-> get_resolution()的地址已解析

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

在main()中-> get_resolution的地址尚未解析

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    

现在的问题是,如何防止编译器解析函数display()中使用的get_resolution()地址,而是使用重定位表,以便可以在链接阶段覆盖get_resolution()函数?

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?

编辑:

  1. 基于hroptatyr的响应,添加 void get_resolution()__attribute __((weak)); 解决了使用mingw-gcc而不是在我的目标平台QNX/ARM/gcc(4.4.2)中的问题.)
  2. 如果有人可以指向支持ARM目标的良好库,那么即使是运行时方法(如函数钩子)也是可以接受的.
  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.

推荐答案

为此只需使用预处理器即可

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.

修改
即使在注释中指出不需要更改 display.c ,这里的 weak 属性解决方案也来自

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

使用GNU汇编器和链接器时,ELF目标和a.out目标均支持弱符号.

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

display.c中的

/* 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();
}

从现在开始,无论您在哪里调用 get_resolution()(都已编译为"strong"版本),都会调用包装版本.

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

这篇关于覆盖同一编译单元中C中的函数调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆