为什么LD_PRELOAD不能用于加载的共享库之一?

我在RedHat Linux 5.0上有一个内部共享库,它提供了freemalloc

 >nm ./libmem_consumption.so | grep -P -e "\bfree\b|\bmalloc\b" 0000000000006540 T free 00000000000088a0 T malloc 

这个共享库负责提供关于进程内存消耗的信息。 不幸的是,当它与Apache httpd使用时,这个共享库存在一个问题。 当Apache httpd与这个库一起运行时,我得到了一个libc::freecoredump和一个指针无效的消息。 问题似乎在http.so中,这是由httpd加载的libphp5.so加载的共享库。

其实当我不加载http.so一切正常,没有coredump。 (加载或不加载http.so是由一个configuration文件中的指令来pipe理:extension = http.so)当我加载http.so ,httpd进程的coredumps。

httpd是这样启动的:

 LD_PRELOAD=./libmem_consumption.so ./bin/httpd -f config 

并在出口coredumps。

当我设置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' 

看起来,当查找http.so时, LD_PRELOAD=./libmem_consumption.so似乎不用于http.so 为什么LD_PRELOAD被忽略?

Solutions Collecting From Web of "为什么LD_PRELOAD不能用于加载的共享库之一?"

它看起来http.so加载了RTLD_DEEPBIND标志,这就是为什么LD_PRELOAD被忽略的共享库之一。

这是从http://linux.die.net/man/3/dlopen

RTLD_DEEPBIND(自glibc 2.3.4开始)将该库中符号的查找范围放在全局范围之前。 这意味着一个独立的库将使用它自己的符号,优先于已经加载的库中包含的同名全局符号。 这个标志在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'