我在RedHat Linux 5.0上有一个内部共享库,它提供免费的功能和malloc:
>nm ./libmem_consumption.so | grep -P -e "\bfree\b|\bmalloc\b"
0000000000006540 T free
00000000000088a0 T malloc
此共享库负责提供有关进程内存消耗的信息.
不幸的是,当它与Apache httpd一起使用时,这个共享库存在问题.
当使用这个库运行Apache httpd时,我得到一个libc :: free的coredump和一个指针无效的消息.
问题似乎是在http.so中,这是一个由libdp5.so加载的共享库,由httpd加载.
实际上,当我没有加载http.so一切都没关系,没有coredump.
(加载或不加载http.so由配置文件中的指令管理: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有免费@ 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
这是UNSET_RTLD_DEEPBIND为1的LD_DEBUG = all的输出:
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'