如何正确插入malloc,允许LD_Preload链接
我有一个创建的共享库,该库可插入malloc()和相关调用。 效果很好,但是有一些警告。 有一件事不起作用。我希望能够链接插入器,以便我可以运行类似的
LD_PRELOAD="/path/to/mymalloc.so /usr/lib64/jemalloc.so" some_app
意图是,而不是转发到libc malloc()我的库现在应该通过rtld_next将其转发到Jemalloc。
但是,它segfault生成了堆栈跟踪,显示了我的malloc包装器自称为ad infinitum。尽管当不使用Jemalloc时,它不会分配任何内存本身:
#224364 0x00007facb1aef46a in Memory::HybridAllocator<Memory::LibCAllocator, Memory::StaticAllocator>::malloc (this=0x7facb1d0be60 <Memory::getHybridAllocator()::hybrid>, size=72704) at /home/brucea/work/git/libbede/src/main/cpp/memory/Memory/HybridAllocator.h:109
#224365 0x00007facb1aefa8a in malloc (size=72704) at /home/brucea/work/git/libbede/src/main/cpp/memory/Memory/mallocwrap.cpp:11
#224366 0x00007facb1aeeca2 in Memory::LibCAllocator::malloc (this=0x7facb1cf3720 <Memory::getBootstrapAllocator()::bootstrap>, requestSize=72704) at /home/brucea/work/git/libbede/src/main/cpp/memory/Memory/LibCAllocator.h:77
#224367 0x00007facb1aef46a in Memory::HybridAllocator<Memory::LibCAllocator, Memory::StaticAllocator>::malloc (this=0x7facb1d0be60 <Memory::getHybridAllocator()::hybrid>, size=72704) at /home/brucea/work/git/libbede/src/main/cpp/memory/Memory/HybridAllocator.h:109
#224368 0x00007facb1aefa8a in malloc (size=72704) at /home/brucea/work/git/libbede/src/main/cpp/memory/Memory/mallocwrap.cpp:11
#224369 0x00007facb133fc1a in (anonymous namespace)::pool::pool (this=0x7facb163e200 <(anonymous namespace)::emergency_pool>) at ../../../../libstdc++-v3/libsupc++/eh_alloc.cc:123
#224370 __static_initialization_and_destruction_0 (__priority=65535, __initialize_p=1) at ../../../../libstdc++-v3/libsupc++/eh_alloc.cc:262
#224371 _GLOBAL__sub_I_eh_alloc.cc(void) () at ../../../../libstdc++-v3/libsupc++/eh_alloc.cc:338
#224372 0x00007facb1d1b8ba in call_init (l=<optimized out>, argc=argc@entry=4, argv=argv@entry=0x7ffe3ba440e8, env=env@entry=0x7ffe3ba44110) at dl-init.c:72
#224373 0x00007facb1d1b9ba in call_init (env=0x7ffe3ba44110, argv=0x7ffe3ba440e8, argc=4, l=<optimized out>) at dl-init.c:30
#224374 _dl_init (main_map=0x7facb1f3a1d0, argc=4, argv=0x7ffe3ba440e8, env=0x7ffe3ba44110) at dl-init.c:119
#224375 0x00007facb1d0cfda in _dl_start_user () from /lib64/ld-linux-x86-64.so.2
#224376 0x0000000000000004 in ?? ()
#224377 0x00007ffe3ba45d7f in ?? ()
#224378 0x00007ffe3ba45ddd in ?? ()
#224379 0x00007ffe3ba45de0 in ?? ()
#224380 0x00007ffe3ba45de4 in ?? ()
#224381 0x0000000000000000 in ?? ()
在GDB中调试的原因似乎是__libc_malloc()内部的malloc_hook在某种程度上指出了我对Malloc的实现,从而导致无限的重新计算。但这必须以某种方式做到这一点。
__GI___libc_malloc (bytes=16) at malloc.c:3037
3037 {
(gdb) s
3042 = atomic_forced_read (__malloc_hook);
(gdb) s
3043 if (__builtin_expect (hook != NULL, 0))
(gdb) s
3044 return (*hook)(bytes, RETURN_ADDRESS (0));
(gdb) s
malloc (size=140737488345424) at /home/brucea/work/git/libbede/src/main/cpp/memory/Memory/mallocwrap.cpp:12
基本轮廓是我的代码(在C ++中,除了低级零件外,对造成C纯粹主义者造成的任何犯罪的道歉):
extern "C" void* malloc(const size_t size) __THROW
{
return getMyAllocator().malloc(size);
}
// etc. for free() et al
// elsewhere
auto wrap(const char* sym)
{
static void* libchandle = nullptr;
auto f = dlsym(RTLD_NEXT,sym);
if (f == nullptr)
{
std::fprintf(stderr, "error: unable to find symbol via dlsym(RTLD_NEXT,%s):\n",sym);
std::fprintf(stderr, "%s\n",dlerror());
f = dlsym(RTLD_DEFAULT, sym);
}
if (f == nullptr)
{
std::fprintf(stderr, "error: unable to find symbol via dlsym(RTLD_DEFAULT,%s):\n",sym);
std::fprintf(stderr, "%s\n",dlerror());
if (libchandle == nullptr)
{
libchandle = dlopen("libc.so", RTLD_LAZY);
if (libchandle == nullptr)
{ \
std::fprintf(stderr, "unable to open libc.so:\n");
std::fprintf(stderr, "%s\n",dlerror());
}
if (libchandle != nullptr)
{
f = dlsym(libchandle, sym);
}
}
if (f == nullptr)
{
std::fprintf(stderr, "error: unable to find symbol via dlsym(\"libc\",%s):\n",sym);
std::fprintf(stderr, "%s\n",dlerror());
std::exit(1);
}
}
return f;
}
#define WRAP(X) \
{ \
static constexpr const char* const symName = #X; \
auto f = reinterpret_cast<decltype(&::X)>(wrap(#X)); \
this->X##Func = f; \
}
// Note: until ForwardingAllocator is setup
// malloc() etc are forwarded to __libc_malloc() etc
ForwardingAllocator::ForwardingAllocator()
{
WRAP(malloc)
WRAP(free)
WRAP(calloc)
WRAP(realloc)
WRAP(malloc_usable_size)
}
简洁地省略了很多东西。
有什么建议,关于我可能做错了什么或如何更好地诊断问题?
似乎Jemalloc本身定义了__libc_malloc
>nm /usr/lib/debug/usr/lib64/libjemalloc.so.2-5.2.1-2.el8.x86_64.debug | grep __libc_malloc
000000000000d4f0 t __libc_malloc
一些进一步的信息。
- malloc_hooks被弃用,所以我不使用它们。
我已经成功处理的并发症:
-
dlsym()使用malloc() - 我在启动期间使用一个简单的引导分配器,然后再切换到主要的libc malloc()
。 -
i最初使用幼稚的分配器作为BoostStrap Araloctor
我的包装器将包装器free()代表到适当的free(),具体取决于使用malloc()的使用
,我现在已搬到使用__libc_malloc作为Bootstrap分配器,但可以尽快通过DLSYM替换它。
这是一个有用的答案 - https://stackoverflow.com/a/17850402/1569204
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
尽管
jemalloc
提供__ libc_malloc
作为仅与glibc静态链接的符号。当您转发到
__ libc_malloc
在您的共享库中,您仍在转发到libc
实现。但是,似乎在启动>
jemalloc
设置malloc钩子以指向malloc()
的先前地址。在这种情况下,第一个库(即您的)中的Malloc包装器。在内部设置了几件事之后,目前需要3个调用
malloc()
jemalloc通过libc
malloc Hooks安装自身作为新的malloc
。不幸的是,没有其他由
glibc
导出的符号,您可以用来绕过malloc挂钩并直接使用malloc。至少在我使用的版本上。如果您有另一个使用Malloc替代品,则可以通过将Malloc钩子设置为自己来处理此操作。但是,您已经表达了“做正确的事”,而不是使用malloc钩子,因为它们是弃用
您可以在不使用malloc挂钩的情况下处理此操作
检测递归电话并为其他一些杂物提供一条途径
例如:
这很丑陋,但有效。您的包装器到malloc的效率较低,而不是一个增量,一个减少和一个条件分支的效果。
它可能没有任何区别,但是您可以使用C ++属性 ]] 或GCC的 __ nedein_endendin_expect 因为不太可能进行递归。
还有另一个要注意的陷阱。如果要转发多个符号,则应检查它们是否安全转发(通常这意味着到同一库)。
例如:
实践中的一个例子是电围栏。
如果我链:
ld_preload =“ mymalloc.so electric-fence.o”,
那么您会发现
malloc_usable_size()
来自libc
eybc ,而malloc
来自电气围栏
。授予的电围栏不再那么普遍了。
在这种情况下,将Malloc_usable_size()替换为始终返回0的虚拟函数会更安全。
例如,malloc_usable_size(ptr)的普通libc版本 - (请参阅)探讨位于分配块(即ptr-2*sizeof(size_t))之前的指示器。如果您给它一个不符合此模式的PTR,则可能会出现。
例如,请参见是否可以动态定义一个符号,以便通过DLSYM找到它?
Though
jemalloc
provides__libc_malloc
as a symbol it is for use for static linking with glibc only.when you forward to
__libc_malloc
in your shared library you are still forwarding to thelibc
implementation.However, it seems that during startup
jemalloc
sets malloc hooks to point to the previous address ofmalloc()
. In this case the malloc wrapper in the first library (i.e. yours).After setting a couple of things up internally which currently requires 3 calls to
malloc()
jemalloc installs itself as the newmalloc
via thelibc
malloc hooks.Unfortunately there is no other symbol exported by
glibc
that you can use to bypass malloc hooks and use malloc directly. At least on the version I'm using.You could handle this by setting malloc hooks yourself if you have another malloc replacement to use. However, you have already expressed a desire to "do the right thing" and not use malloc hooks because they are deprecated
You can handle this without using malloc hooks by
detecting recursive calls and providing a path to some other malloc
for example:
This is ugly but it works. Your wrapper to malloc is less efficient to the tune of one increment, one decrement and one conditional branch.
It probably doesn't make any difference but you could use the C++ attribute [[unlikely]] or gcc's __builtin_expect to say that the branch for recursion is not likely to be taken.
There is another pitfall to be aware of. If you are forwarding multiple symbols you should check that they are all forwarded safely (typically this means to the same library).
For example:
An example of this in practice is electric-fence.
If I chain:
LD_PRELOAD="mymalloc.so electric-fence.so"
You find that
malloc_usable_size()
comes fromlibc
whilemalloc
comes fromelectric-fence
.Granted electric-fence is not so common any more.
In this case it would be safer to replace malloc_usable_size() with a dummy function that always returns 0.
For example the normal libc version of malloc_usable_size(ptr) - (see https://code.woboq.org/userspace/glibc/malloc/malloc.c.html) looks at pointers located just before the allocated block (i.e. ptr-2*sizeof(size_t) ). If you give it a ptr that does not conform to this pattern it could segfault.
See for example Is it possible to define a symbol dynamically such that it will be found by dlsym?