Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/70.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
从同一编译单元重写C中的函数调用_C_Function_Gcc_Linker_Overriding - Fatal编程技术网

从同一编译单元重写C中的函数调用

从同一编译单元重写C中的函数调用,c,function,gcc,linker,overriding,C,Function,Gcc,Linker,Overriding,我正在尝试,但是当函数在同一个编译单元中使用时,我面临一个问题。在下面的代码中,我试图替换函数get_resolution(),但只有在test.c中完成,而不是在display.c中完成,我才能实现它 // display.c ------------------------------------------------------------- #include <stdio.h> void get_resolution() { printf("Original g

我正在尝试,但是当函数在同一个编译单元中使用时,我面临一个问题。在下面的代码中,我试图替换函数get_resolution(),但只有在test.c中完成,而不是在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
//display.c-------------------------------------------------------------
#包括
void get_resolution()
{
printf(“原始获取分辨率”);
}
无效显示()
{
获得分辨率();
}
//测试c----------------------------------------------------------------
#包括
void uuu wrap u get u resolution()
{
printf(“模拟获取分辨率”);
//_uuureal_get_resolution();//应该可以调用original
}
int main()
{
display();//**问题**调用原始获取分辨率()
调用get_resolution();//u wrap_get_resolution()
返回0;
}
//gcc-Wl,--wrap,获取分辨率显示.c test.c
我的要求是,当我从main()调用display()时,我希望执行_wrap_get_resolution(),但我总是看到原始的get_resolution()被调用。对dis assembly的一点分析表明,函数get_resolution的调用方式不同:

在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
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    
00000000:
0:55推力%ebp
1:89 e5 mov%esp,%ebp
3:83 ec 18子$0x18,%esp
6:c7 04 24 00动产$0x0,(%esp)
d:e8 00呼叫12
12:c9离开
13:c3 ret
00000014 :
14:55%推压ebp
15:89 e5 mov%esp,%ebp
17:83 ec 08子$0x8,%esp
1a:e8 e1 ff呼叫0
1f:c9离开
20:c3 ret
21:90不
22:90不
23:90不
在main()->尚未解析get_解析的地址

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
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    
00000000:
0:55推力%ebp
1:89 e5 mov%esp,%ebp
3:83 ec 18子$0x18,%esp
6:c7 04 24 00动产$0x0,(%esp)
d:e8 00呼叫12
12:c9离开
13:c3 ret
00000014 :
14:55%推压ebp
15:89 e5 mov%esp,%ebp
17:83 e4 f0和$0xfffffff0,%esp
1a:e8 00呼叫1f
1f:e8 00呼叫24
24:e8 00呼叫29
29:b8 00 mov$0x0,%eax
2e:c9离开
2f:c3 ret
现在的问题是,如何防止编译器解析函数display()中使用的get_resolution()的地址,而是使用重定位表,以便在链接阶段重写get_resolution()函数

编辑

  • 根据hroptatyr的响应,添加
    void get_resolution()_属性__((弱))解决了使用mingw gcc时的问题,但不是在我的目标平台QNX/ARM/gcc(4.4.2)中
  • 如果有人能够指向一个支持ARM目标的好库,那么即使像function hook这样的运行时方法也是可以接受的

  • 这里可能会提供一个答案:

    如果要重写库中的函数,可以使用(unser linux)
    LD\u PRELOAD

    希望这有帮助


    问候。

    如果您这样做会怎么样:


    gcc-Wl,--wrap,get_resolution test.c 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
    
    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();
        ...
    }
    
    其思想是在放入所有需要查看原始函数的代码后,对包装函数的函数调用进行“重命名”

    更脏的版本可能是在本地对函数地址进行阴影处理,如下所示:

    int main()
    {
        void(*get_resolution)() = __wrap_get_resolution;
        get_resolution();
        ...
    }
    
    这一个可以工作,但可能会给你一些恶劣的警告

    编辑
    尽管在评论中指出不需要更改
    display.c
    ,但这里的
    弱属性解决方案

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


    从现在起,无论您在哪里调用
    get_resolution()
    (并编译“强”版本),都会调用包装版本。

    如果符号在汇编文件中定义,则汇编程序将始终直接调用符号;用GNU
    作为
    (据我所知)无法改变这一点。这是假设编译器尚未决定内联函数调用


    解决方案是在
    display.c
    中未定义
    get\u分辨率。为此,我建议将其拆分为两个文件,一个文件具有
    get_resolution
    ,另一个文件具有源文件的其余部分。您可以在不实际拆分文件的情况下执行此操作,方法是放入
    #ifdef
    块,并使用不同的定义编译两次。

    我认为除了使用shell代码之外,很难实现它。您可以看到原因


    <>作者给出了一个C++代码的例子,我用类似的方法对你的问题进行了尝试,但我没有达到你的要求。所以我认为解决问题的唯一方法可能是使用shell代码,但是Linux的ASLR让它很难。这就是我所知道的,希望它能帮助您

    gcc执行的“wrap”函数是在链接器阶段完成的,而不是由编译器完成的。编译器知道get_解析的位置,因为它在同一个编译单元中,所以不需要链接器解析它(这就是神奇的地方)

    如果不以某种方式更改display.c(或其中一个包含的头文件),我看不出您可以解决这个问题。最简单的是t