我正在为C编写一个内存探查器,并通过malloc_hooks拦截对malloc
、realloc
和free
函数的调用。不幸的是,由于它们在多线程环境中的不良行为,因此不推荐使用这些方法。我找不到一份描述替代最佳实践解决方案的文件来实现同样的目标,有人能启发我吗?
我读到过一个简单的#define malloc(s) malloc_hook(s)
可以做到这一点,但它不适用于我心目中的系统设置,因为它对原始代码库的侵入性太强,不适合在分析/跟踪工具中使用。必须手动更改原始应用程序代码对于任何优秀的探查器来说都是一个杀手。最理想的情况是,我正在寻找的解决方案应该通过链接到一个可选的共享库来启用或禁用。例如,我当前的设置使用一个用__attribute__ ((constructor))
声明的函数来安装拦截malloc
挂钩。
感谢
在尝试了一些事情之后,我终于找到了如何做到这一点。
首先,在glibc
中,malloc
被定义为弱符号,这意味着它可以被应用程序或共享库覆盖。因此,不一定需要LD_PRELOAD
。相反,我在一个共享库中实现了以下功能:
void*
malloc (size_t size)
{
[ ... ]
}
它由应用程序调用,而不是由glibc
的malloc
调用。
现在,为了等效于__malloc_hook
的功能,仍然缺少一些东西。
1.)呼叫者地址
除了malloc
的原始参数外,glibc
s __malloc_hook
s还提供了调用函数的地址,这实际上是malloc
返回到的返回地址。为了实现同样的目的,我们可以使用gcc中可用的__builtin_return_address
函数。我没有研究过其他编译器,因为我只限于gcc,但如果你碰巧知道如何便携地完成这样的事情,请给我留言:)
我们的malloc
函数现在看起来是这样的:
void*
malloc (size_t size)
{
void *caller = __builtin_return_address(0);
[ ... ]
}
2.)从挂钩中访问glibc
的malloc
由于我的应用程序仅限于glibc,所以我选择使用__libc_malloc
来访问原始的malloc实现。或者,也可以使用dlsym(RTLD_NEXT, "malloc")
,但可能存在的缺陷是,该函数在第一次调用时使用calloc
,可能会导致导致segfault的无限循环。
完全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并不是一个弱符号,导致在链接时出现多定义错误。
如果出于任何原因需要静态链接,例如通过调试符号将第三方库中的函数地址转换为代码行,那么您可以静态链接这些第三方lib,同时仍然动态链接malloc挂钩,从而避免多定义问题。我还没有找到更好的解决方法,如果你知道的话,请随时给我留言。
下面是一个简短的例子:
gcc -o test test.c -lmalloc_hook_library -Wl,-Bstatic -l3rdparty -Wl,-Bdynamic
3rdparty
将被静态链接,而malloc_hook_library
将被动态链接,从而产生预期的行为,并且3rdparty
中的函数地址可以通过test
中的调试符号进行翻译。很整洁,是吗?
共谋
上面的技术描述了一种与__malloc_hook
基本相同的方法,但有几个平均限制:
__builtin_caller_address
仅适用于gcc
__libc_malloc
仅适用于glibc
dlsym(RTLD_NEXT, [...])
是glibc
中的GNU扩展
链接器标志CCD_ 39和CCD_。
换句话说,这个解决方案是完全不可移植的,如果钩子库要移植到非GNU操作系统,就必须添加替代解决方案。
您可以使用LD_PRELOAD&dlsym请参阅"malloc和免费提示"http://www.slideshare.net/tetsu.koba/presentations
刚刚成功构建了包含__malloc_hook
的NDK代码。
根据https://android.googlesource.com/platform/bionic/+/master/libc/include/malloc.h,esp:
extern void* (*volatile __malloc_hook)(size_t __byte_count, const void* __caller) __INTRODUCED_IN(28);