5

我在 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一切正常并且没有核心转储。(加载或不加载http.so由配置文件中的指令管理:extension=http.so)当我加载http.sohttpd 进程 coredumps。

httpd以这种方式启动:

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

和退出时的核心转储。

当我设置 LD_BIND_NOW=1 并http.so加载时,我看到(在 gdb 下)http.sofree@plt指向libc::free并在其他加载的库中(例如libphp5.sofree@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'

似乎LD_PRELOAD=./libmem_consumption.so不用于http.so何时free查找。为什么 LD_PRELOAD 被忽略?

4

1 回答 1

8

似乎 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'
于 2012-11-14T06:49:42.537 回答