从同一编译单元覆盖 C 中的函数调用

2024-01-10

我在尝试着重写 C 中的函数调用 https://stackoverflow.com/questions/617554/override-a-function-call-in-c,但是当该函数在同一编译单元中使用时,我遇到了问题。在下面的代码中,我试图替换函数 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

我的要求是,当我从 main() 调用 display() 时,我希望执行 __wrap_get_resolution() 但我总是看到原始的 get_resolution() 正在被调用。对反汇编的一点分析表明,函数 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

在 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()函数?

EDIT:

  1. 根据 hroptatyr 的回应,添加void get_resolution() __attribute__((weak));解决了使用 mingw-gcc 时的问题,但不在我的目标平台 QNX/ARM/gcc(4.4.2) 中
  2. 如果有人可以指出一个支持 ARM 目标的好库,即使是像函数 hook 这样的运行时方法也是可以接受的。

只需使用预处理器即可:

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

这个想法是“重命名”对包装函数的函数调用after你把需要查看原始功能的所有代码都放在其中。

一个更肮脏的版本可能是在本地隐藏函数地址,如下所示:

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

这个可以工作,但可能会给你一些令人讨厌的警告。

Edit
尽管在评论中指出改变display.c不是所希望的,这里weak属性解,从http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html

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

/* 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() (and编译了“强”版本)包装的版本被调用。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从同一编译单元覆盖 C 中的函数调用 的相关文章

  • BASIC 中的 C 语言中的 PeekInt、PokeInt、Peek、Poke 等效项

    我想知道该命令的等效项是什么Peek and Poke 基本和其他变体 用 C 语言 类似PeekInt PokeInt 整数 涉及内存条的东西 我知道在 C 语言中有很多方法可以做到这一点 我正在尝试将基本程序移植到 C 语言 这只是使用
  • 通过引用传递 [C++]、[Qt]

    我写了这样的东西 class Storage public Storage QString key const int value const void add item QString int private QMap
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • C# xml序列化必填字段

    我需要将一些字段标记为需要写入 XML 文件 但没有成功 我有一个包含约 30 个属性的配置类 这就是为什么我不能像这样封装所有属性 public string SomeProp get return someProp set if som
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • 在 WPF 中使用 ReactiveUI 提供长时间运行命令反馈的正确方法

    我有一个 C WPF NET 4 5 应用程序 用户将用它来打开某些文件 然后 应用程序将经历很多动作 读取文件 通过许多插件和解析器传递它 这些文件可能相当大 gt 100MB 因此这可能需要一段时间 我想让用户了解 UI 中发生的情况
  • C# 中的 IPC 机制 - 用法和最佳实践

    不久前我在 Win32 代码中使用了 IPC 临界区 事件和信号量 NET环境下场景如何 是否有任何教程解释所有可用选项以及何时使用以及为什么 微软最近在IPC方面的东西是Windows 通信基础 http en wikipedia org
  • C++ 继承的内存布局

    如果我有两个类 一个类继承另一个类 并且子类仅包含函数 那么这两个类的内存布局是否相同 e g class Base int a b c class Derived public Base only functions 我读过编译器无法对数
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • 当文件流没有新数据时如何防止fgets阻塞

    我有一个popen 执行的函数tail f sometextfile 只要文件流中有数据显然我就可以通过fgets 现在 如果没有新数据来自尾部 fgets 挂起 我试过ferror and feof 无济于事 我怎样才能确定fgets 当
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • 如何确定 CultureInfo 实例是否支持拉丁字符

    是否可以确定是否CultureInfo http msdn microsoft com en us library system globalization cultureinfo aspx我正在使用的实例是否基于拉丁字符集 我相信你可以使

随机推荐