【问题标题】:An alternative for the deprecated __malloc_hook functionality of glibcglibc 已弃用的 __malloc_hook 功能的替代方案
【发布时间】:2013-07-22 03:00:33
【问题描述】:

我正在为 C 编写一个内存分析器,为此我正在通过 malloc_hooks 拦截对 mallocreallocfree 函数的调用。不幸的是,这些已被弃用,因为它们在多线程环境中表现不佳。我找不到描述实现相同目标的替代最佳实践解决方案的文档,有人可以启发我吗?

我读过一个简单的#define malloc(s) malloc_hook(s) 就可以解决问题,但这不适用于我想到的系统设置,因为它对原始代码库的干扰太大,不适合在分析中使用/ 追踪工具。必须手动更改原始应用程序代码是任何体面的分析器的杀手。理想情况下,我正在寻找的解决方案应该通过链接到可选的共享库来启用或禁用。例如,我当前的设置使用__attribute__ ((constructor)) 声明的函数来安装拦截malloc 挂钩。

谢谢

【问题讨论】:

标签: c gcc malloc deprecated glibc


【解决方案1】:

刚刚设法构建包含__malloc_hook 的NDK 代码。

根据https://android.googlesource.com/platform/bionic/+/master/libc/include/malloc.h,esp 看来,它已在 Android API v28 中重新启用:

extern void* (*volatile __malloc_hook)(size_t __byte_count, const void* __caller) __INTRODUCED_IN(28);

【讨论】:

    【解决方案2】:

    在尝试了一些事情之后,我终于设法弄清楚了如何做到这一点。

    首先,在glibc中,malloc被定义为弱符号,表示它可以被应用程序或者共享库覆盖。因此,不一定需要LD_PRELOAD。相反,我在共享库中实现了以下功能:

    void*
    malloc (size_t size)
    {
      [ ... ]
    }
    

    由应用程序调用而不是 glibcs malloc

    现在,为了等同于 __malloc_hooks 功能,仍然缺少一些东西。

    1.) 来电者地址

    除了malloc的原始参数,glibcs__malloc_hooks还提供了调用函数的地址,这实际上是malloc将返回的返回地址。为了达到同样的目的,我们可以使用 gcc 中的__builtin_return_address 函数。我没有研究过其他编译器,因为无论如何我都仅限于 gcc,但如果你碰巧知道如何便携地做这样的事情,请给我留言:)

    我们的malloc 函数现在看起来像这样:

    void*
    malloc (size_t size)
    {
      void *caller = __builtin_return_address(0);
      [ ... ]
    }
    

    2.) 从你的钩子中访问glibcs malloc

    由于我的应用程序仅限于 glibc,因此我选择使用 __libc_malloc 来访问原始 malloc 实现。或者,也可以使用dlsym(RTLD_NEXT, "malloc"),但可能会导致此函数在第一次调用时使用calloc,这可能会导致无限循环导致段错误。

    完整的 malloc 钩子

    我的完整钩子函数现在看起来像这样:

    extern void *__libc_malloc(size_t size);
    
    int malloc_hook_active = 0;
    
    void*
    malloc (size_t size)
    {
      void *caller = __builtin_return_address(0);
      if (malloc_hook_active)
        return my_malloc_hook(size, caller);
      return __libc_malloc(size);
    }
    

    my_malloc_hook 看起来像这样:

    void*
    my_malloc_hook (size_t size, void *caller)
    {
      void *result;
    
      // deactivate hooks for logging
      malloc_hook_active = 0;
    
      result = malloc(size);
    
      // do logging
      [ ... ]
    
      // reactivate hooks
      malloc_hook_active = 1;
    
      return result;
    }
    

    当然,callocreallocfree 的钩子的工作方式类似。

    动态和静态链接

    使用这些功能,动态链接可以开箱即用。链接包含 malloc 钩子实现的 .so 文件将导致应用程序对 malloc 的所有调用以及所有通过我的钩子路由的库调用。静态链接虽然是有问题的。我还没有完全理解它,但是在静态链接中 malloc 不是一个弱符号,导致链接时出现多重定义错误。

    如果您出于某种原因需要静态链接,例如通过调试符号将第 3 方库中的函数地址转换为代码行,那么您可以静态链接这些第 3 方库,同时仍然动态链接 malloc 挂钩,避免多重定义问题.我还没有找到更好的解决方法,如果你知道,请随时给我留言。

    这是一个简短的例子:

    gcc -o test test.c -lmalloc_hook_library -Wl,-Bstatic -l3rdparty -Wl,-Bdynamic
    

    3rdparty 将静态链接,而malloc_hook_library 将动态链接,从而产生预期的行为,并且3rdparty 中的函数地址可通过test 中的调试符号进行转换。很整洁吧?

    结论

    上述技术描述了一种不被弃用的、与__malloc_hooks 几乎等效的方法,但有一些限制:

    __builtin_caller_address 仅适用于gcc

    __libc_malloc 仅适用于glibc

    dlsym(RTLD_NEXT, [...])glibc 中的 GNU 扩展

    链接器标志 -Wl,-Bstatic-Wl,-Bdynamic 特定于 GNU binutils。

    换句话说,这个解决方案是完全不可移植的,如果要将 hooks 库移植到非 GNU 操作系统,则必须添加替代解决方案。

    【讨论】:

    • 您在使用 valgrind 时遇到过任何问题吗?当两者结合时,我看到了奇怪的问题。
    • @meowsqueak 我没有尝试过,但 valgrind 往往会做一些奇怪的事情。
    • 嘿@AndreasGrapentin,我用你的方法写了一个通用的内存堆检查器,因为没有更好的名字,MEM_debug。感谢分享!
    • @itaych 嘿,谢谢!我很高兴听到一些有用的东西:)
    • @AndreasGrapentin "在 glibc 中,malloc 被定义为一个弱符号",似乎写的是哪个文档?我看code(malloc.c)好像不是这样的。
    【解决方案3】:

    您可以使用 LD_PRELOAD 和 dlsym 请参阅http://www.slideshare.net/tetsu.koba/presentations 上的“malloc 和免费提示”

    【讨论】:

    • 这很酷,我一定会尝试的。虽然需要明确设置 LD_PRELOAD 让我感到困扰。
    • 另外,这似乎不适用于静态链接的二进制文件:(
    • 不幸的是,dlsym 在某些情况下会调用 calloc。
    猜你喜欢
    • 2012-04-16
    • 1970-01-01
    • 2016-01-08
    • 2020-11-07
    • 2020-05-22
    • 2022-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多