37

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

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

谢谢

4

3 回答 3

59

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

首先, in glibcmalloc被定义为弱符号,这意味着它可以被应用程序或共享库覆盖。因此,LD_PRELOAD不一定需要。相反,我在共享库中实现了以下功能:

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

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

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

1.) 来电者地址

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

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

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

2.)glibc从你的钩子中访问 s 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;
}

当然, 和 的钩子的calloc工作realloc方式free类似。

动态和静态链接

使用这些功能,动态链接可以开箱即用。链接包含 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, [...])是 GNU 扩展glibc

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

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

于 2013-07-25T06:15:16.907 回答
2

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

于 2013-07-23T08:05:37.263 回答
1

刚刚设法 NDK 构建包含__malloc_hook.

根据https://android.googlesource.com/platform/bionic/+/master/libc/include/malloc.h的说法,它似乎已在 Android API v28 中重新启用,尤其是:

extern void* (*volatile __malloc_hook)(size_t __byte_count, const void* __caller) __INTRODUCED_IN(28);
于 2019-02-28T06:45:55.150 回答