我在 RedHat Linux 5.0 上有一个内部共享库,它提供函数 free
和 malloc
:
>nm ./libmem_consumption.so | grep -P -e "\bfree\b|\bmalloc\b"
0000000000006540 T free
00000000000088a0 T malloc
此共享库负责提供有关进程内存消耗的信息。
不幸的是,这个共享库在与 Apache httpd
一起使用时会出现问题。
当 Apache httpd 与这个库一起运行时,我在 libc::free
中得到一个核心转储和一条指针无效的消息。
问题似乎出在 http.so 中,它是由 libphp5.so 加载的共享库,由 httpd
加载。
实际上,当我不加载 http.so
时,一切正常,没有 coredump。
(加载或不加载 http.so
由配置文件中的指令管理:extension=http.so)
当我加载 http.so
时,httpd 进程核心转储。
httpd
是这样启动的:
LD_PRELOAD=./libmem_consumption.so ./bin/httpd -f config
和退出时的核心转储。
当我设置 LD_BIND_NOW=1 并加载 http.so
时,我看到(在 gdb 下)http.so 有 free@plt
指向 libc: :free
并在其他加载
库(例如 libphp5.so
)free@plt
指向 libmem_consumption.so::free
。
这怎么可能?
顺便说一句,当我导出 LD_DEBUG=all 并将输出保存到一个文件时,我看到了 libphp5.so(也已加载)的这些行:
25788: symbol=free; lookup in file=/apache2/bin/httpd [0]
25788: symbol=free; lookup in file=/apache2/ps/lib/libmem_consumption.so [0]
25788: binding file /apache2/modules/libphp5.so [0] to /apache2/ps/lib/libmem_consumption.so [0]: normal symbol `free' [GLIBC_2.2.5]
和 http.so 完全不同:
25825: symbol=free; lookup in file=/apache2/ext/http.so [0]
25825: symbol=free; lookup in file=/apache2/ps/lib/libz.so.1 [0]
25825: symbol=free; lookup in file=/apache2/ps/lib/libcurl.so.4 [0]
25825: symbol=free; lookup in file=/lib64/libc.so.6 [0]
25825: binding file /apache2/ext/http.so [0] to /lib64/libc.so.6 [0]: normal symbol `free'
当查找free
时,LD_PRELOAD=./libmem_consumption.so
似乎没有用于http.so
。为什么忽略 LD_PRELOAD?
最佳答案
似乎 http.so 加载了 RTLD_DEEPBIND 标志,这就是为什么 LD_PRELOAD 被其中一个共享库忽略的原因。
这是来自 http://linux.die.net/man/3/dlopen :
RTLD_DEEPBIND (since glibc 2.3.4) Place the lookup scope of the symbols in this library ahead of the global scope. This means that a self-contained library will use its own symbols in preference to global symbols with the same name contained in libraries that have already been loaded. This flag is not specified in POSIX.1-2001.
我写了一个测试共享库:
#include <dlfcn.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void initialize_my_dlopen(void) __attribute__((constructor));
void* (*real_dlopen)(const char *, int flag);
static int unset_RTLD_DEEPBIND=0;
static int _initialized = 0;
static void initialize_my_dlopen(void)
{
if (_initialized)
return;
real_dlopen = (void *(*)(const char *,int))dlsym(RTLD_NEXT, "dlopen");
unset_RTLD_DEEPBIND = atoi(getenv("UNSET_RTLD_DEEPBIND") ? getenv("UNSET_RTLD_DEEPBIND") : "0");
printf("unset_RTLD_DEEPBIND: %d\n", unset_RTLD_DEEPBIND);
_initialized = 1;
}
extern "C" {
void *dlopen(const char *filename, int flag)
{
int new_flag = unset_RTLD_DEEPBIND == 0 ? flag : flag & (~RTLD_DEEPBIND);
return (*real_dlopen)(filename, new_flag);
}
}
并构建它:
gcc -shared -fPIC -g -m64 my_dlopen.cpp -o libmy_dlopen.so -ldl
当我将 UNSET_RTLD_DEEPBIND 设置为 0 并运行 httpd
程序 coredumps。
export UNSET_RTLD_DEEPBIND=0
LD_PRELOAD=./libmy_dlopen.so:./ps/lib/libmem_consumption.so ./bin/httpd -f config
当我将 UNSET_RTLD_DEEPBIND 设置为 1 并运行 httpd
时,一切正常。
export UNSET_RTLD_DEEPBIND=1
LD_PRELOAD=./libmy_dlopen.so:./ps/lib/libmem_consumption.so ./bin/httpd -f config
这是 LD_DEBUG=all 将 UNSET_RTLD_DEEPBIND 设置为 1 的输出:
10678: symbol=free; lookup in file=/apache2/bin/httpd [0]
10678: symbol=free; lookup in file=/apache2/libmy_dlopen.so [0]
10678: symbol=free; lookup in file=/apache2/ps/lib/libmem_consumption.so [0]
10678: binding file /apache2/ext/http.so [0] to /apache2/ps/lib/libmem_consumption.so [0]: normal symbol `free'
关于c++ - 为什么 LD_PRELOAD 不适用于加载的共享库之一?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13374240/