为什么 LD_PRELOAD 对加载的共享库之一不起作用? [英] Why LD_PRELOAD doesn't work for one of loaded shared libraries?

查看:46
本文介绍了为什么 LD_PRELOAD 对加载的共享库之一不起作用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 RedHat Linux 5.0 上有一个内部共享库,提供函数 freemalloc:

I have an in-house shared library on RedHat Linux 5.0 that provides functions free and 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 加载.

This shared library is responsible for providing information about memory consumption of a process. Unfortunatelly there is a problem with this shared library when it is used with Apache httpd. When Apache httpd is run with this library I get a coredump in libc::free and a message that the pointer is invalid. The problem seems to be in http.so which is a shared library loaded by libphp5.so that is loaded by the httpd.

实际上,当我不加载 http.so 时,一切正常,并且没有核心转储.(加载或不加载 http.so 由配置文件中的指令管理:extension=http.so)当我加载 http.so 时,httpd 进程核心转储.

Actually when I do not load http.so everything is OK and there is no coredump. (Loading or not loading http.so is managed by the directive in a configuration file: extension=http.so) When I load http.so the httpd process coredumps.

httpd 是这样启动的:

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

以及退出时的核心转储.

and coredumps on exit.

当我设置 LD_BIND_NOW=1 并加载 http.so 时,我看到(在 gdb 下)http.so 有 free@plt 指向 libc::free 并在其他加载库(例如 libphp5.so)free@plt 指向 libmem_consumption.so::free.这怎么可能?

When I set LD_BIND_NOW=1 and http.so is loaded I see (under gdb) that http.so has free@plt pointing to libc::free and in in other loaded libraries (for example libphp5.so) free@plt points to libmem_consumption.so::free. How that could be possible?

顺便说一下,当我导出 LD_DEBUG=all 并将输出保存到文件时,我看到 libphp5.so(也已加载)的这些行:

By the way when I export LD_DEBUG=all and save output to a file I see these lines for libphp5.so (which is also loaded):

 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 完全不同:

And completely different for 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 被忽略?

It seems that LD_PRELOAD=./libmem_consumption.so is not used for http.so when free is looked up. Why LD_PRELOAD is ignored?

推荐答案

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

It seeems that http.so is loaded with the RTLD_DEEPBIND flag and that is why LD_PRELOAD is ignored for one of shared libraries.

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

RTLD_DEEPBIND(自 glibc 2.3.4 起)将此库中符号的查找范围置于全局范围之前.这意味着一个自包含的库将使用它的自己的符号优先于同名的全局符号包含在已经加载的库中.这个标志不是在 POSIX.1-2001 中指定.

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.

我写了一个测试共享库:

I wrote a test shared library:

  #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 程序核心转储.

When I set UNSET_RTLD_DEEPBIND to 0 and run httpd the program 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 时一切正常.

When I set UNSET_RTLD_DEEPBIND to 1 and run httpd everything is OK.

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 的输出:

And this is the output of LD_DEBUG=all for the UNSET_RTLD_DEEPBIND to 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'

这篇关于为什么 LD_PRELOAD 对加载的共享库之一不起作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆