注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

动态连接库so文件搜索路径  

2013-05-16 00:30:42|  分类: Glibc分析 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、so文件的搜索
在现在的linux系统中,so文件的使用越来越多,它便于在系统级别共享可执行文件,从而减少物理内存使用量;另外在一些框架性代码中,so文件可以提供比较好的兼容性,框架只需要提供一个对so文件的加载,通过约定的接口执行功能,而不用依赖底层的实现,这样就是大量的插件实现原理。
在linux下,so的搜索并不总是可以搜索到的,有时候一个文件夹里命名包含了一个so文件,但是在执行的时候动态连接器却无法找到该so文件,这些就好看一下linux下动态连接器对于so文件的搜索方式了。
二、从ldconfig开始
ldconfig是一个重新刷新linux下系统so文件缓存(cache)及查看so配置的工具,如果ldconfig没有添加参数,则该工具会刷新系统的cache配置文件中的内容。
在ldconfig.c中,重新刷新该文件的选项默认是打开的
/* Build cache.  */
static int opt_build_cache = 1;
在main函数的最后
    case 'N':
      opt_build_cache = 0;
      break;
    case 'n':
      opt_build_cache = 0;
      opt_only_cline = 1;
……
  if (!opt_only_cline)
    {
      parse_conf (config_file, true);

      /* Always add the standard search paths.  */
      add_system_dir (SLIBDIR);
      if (strcmp (SLIBDIR, LIBDIR))
    add_system_dir (LIBDIR);
    }
……
  if (opt_build_cache)
    {
      save_cache (cache_file);
      save_aux_cache (_PATH_LDCONFIG_AUX_CACHE);
    }
也就是如果不在选项中添加n或者N选项,默认都会更新cache的内容。
从该文件的代码可以知道,在ldconfig的输出中,SLIBDIR和LIBDIR是最后出现的两个文件夹,所以我们看一下ldconfig就可以知道系统配置的SLIBDIR和LIBDIR的位置,在我的系统中,两者分别为/lib和 /usr/lib文件夹
[root@Harry glibcobjconf]# ldconfig -v | more
/usr/lib/qt-3.3/lib:
    libqui.so.1 -> libqui.so.1.0.0
    libqt-mt.so.3 -> libqt-mt.so.3.3.8
/usr/lib/vmware-tools/lib32/libvmGuestLib.so:
    libvmGuestLib.so -> libvmGuestLib.so
/usr/lib/vmware-tools/lib64/libvmGuestLib.so:
    libvmGuestLib.so -> libvmGuestLib.so
/usr/lib/vmware-tools/lib32/libvmGuestLibJava.so:
    libvmGuestLibJava.so -> libvmGuestLibJava.so
/usr/lib/vmware-tools/lib64/libvmGuestLibJava.so:
    libvmGuestLibJava.so -> libvmGuestLibJava.so
/usr/lib/vmware-tools/lib32/libDeployPkg.so:
    libDeployPkg.so -> libDeployPkg.so
/usr/lib/vmware-tools/lib64/libDeployPkg.so:
    libDeployPkg.so -> libDeployPkg.so
/usr/lib/xulrunner-1.9.1:
    libxul.so -> libxul.so
    libmozjs.so -> libmozjs.so
    libpyxpcom.so -> libpyxpcom.so
    libxpcom.so -> libxpcom.so
/lib:
    liblvm2cmd.so.2.02 -> liblvm2cmd.so.2.02
    libproc-3.2.8.so -> libproc-3.2.8.so
--More--
三、动态连接器的搜索路径
/* Map in the shared object file NAME.  */

struct link_map *
internal_function
_dl_map_object (struct link_map *loader, const char *name, int preloaded,
        int type, int trace_mode, int mode, Lmid_t nsid)

  if (fd == -1
      && (__builtin_expect (! preloaded, 1)
          || ! INTUSE(__libc_enable_secure)))
    {
      /* Check the list of libraries in the file /etc/ld.so.cache,
         for compatibility with Linux's ldconfig program.  */
      const char *cached = _dl_load_cache_lookup (name);

      if (cached != NULL)
        {
#ifdef SHARED
          // XXX Correct to unconditionally default to namespace 0?
          l = loader ?: GL(dl_ns)[LM_ID_BASE]._ns_loaded;
#else
          l = loader;
#endif

          /* If the loader has the DF_1_NODEFLIB flag set we must not
         use a cache entry from any of these directories.  */
          if (
#ifndef SHARED
          /* 'l' is always != NULL for dynamically linked objects.  */
          l != NULL &&
#endif
          __builtin_expect (l->l_flags_1 & DF_1_NODEFLIB, 0))
        {
          const char *dirp = system_dirs;
          unsigned int cnt = 0;

          do
            {
              if (memcmp (cached, dirp, system_dirs_len[cnt]) == 0)
            {
              /* The prefix matches.  Don't use the entry.  */
              cached = NULL;
              break;
            }

              dirp += system_dirs_len[cnt] + 1;
              ++cnt;
            }
          while (cnt < nsystem_dirs_len);
        }

          if (cached != NULL)
        {
          fd = open_verify (cached,
                    &fb, loader ?: GL(dl_ns)[nsid]._ns_loaded,
                    LA_SER_CONFIG, &found_other_class, false);
          if (__builtin_expect (fd != -1, 1))
            {
              realname = local_strdup (cached);
              if (realname == NULL)
            {
              __close (fd);
              fd = -1;
            }
            }
        }
        }
    }

      /* Finally, try the default path.  */
      if (fd == -1
      && ((l = loader ?: GL(dl_ns)[nsid]._ns_loaded) == NULL
          || __builtin_expect (!(l->l_flags_1 & DF_1_NODEFLIB), 1))
      && rtld_search_dirs.dirs != (void *) -1)
    fd = open_path (name, namelen, preloaded, &rtld_search_dirs,
            &realname, &fb, l, LA_SER_DEFAULT, &found_other_class);

      /* Add another newline when we are tracing the library loading.  */
      if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_LIBS, 0))
        _dl_debug_printf ("\n");
系统默认路径在cache搜索失败之后会越过cache,直接读取系统默认文件夹,系统默认文件夹中的so是不会进行缓存的,每次都是实时数据。
四、系统路径的由来
static const char system_dirs[] = SYSTEM_DIRS;

void
internal_function
_dl_init_paths (const char *llp)
  strp = system_dirs;
  idx = 0;

  do
    {
      size_t cnt;

      *aelem++ = pelem;

      pelem->what = "system search path";
      pelem->where = NULL;

      pelem->dirname = strp;
这个宏在glibc的源代码中没有,所以它是通过外部的configure和build命令生成的一个变量,搜索一下可以知道其大致由来:
glibc-2.7\elf\Makefile
(objpfx)trusted-dirs.st: Makefile $(..)Makeconfig
    $(make-target-directory)
    echo "$(subst :, ,$(default-rpath) $(user-defined-trusted-dirs))"    \
    | $(AWK) -f gen-trusted-dirs.awk > ${@:st=T}
;
    echo '#define DL_DST_LIB "$(notdir $(slibdir))"' >> ${@:st=T}
    $(move-if-change) ${@:st=T} ${@:st=h}
    touch $@
在awk脚本中,它大致的功能就是打印输入内容,形成一个头文件
END {
  printf ("#define SYSTEM_DIRS \\\n");

  printf ("  \"%s\"", s[0]);

  for (i = 1; i < cnt; ++i) {
    printf (" \"\\0\" \"%s\"", s[i]);
  }
经过再几次反向的追踪,我们会发现这个路径是由 slibdir libdir user-defined-trusted-dir 三个变量确定,而由于最后一个通常为空,所以有前两个确定。
五、变量的由来
# Where to install the library and object files.
ifndef libdir
libdir = $(exec_prefix)/lib
endif
inst_libdir = $(install_root)$(libdir)

# Where to install the shared library and dynamic linker.
ifndef slibdir
slibdir = $(exec_prefix)/lib
endif
inst_slibdir = $(install_root)$(slibdir)
可以看到,默认是两者都是在安装文件夹下的lib文件,由于通常prefix为/usr,所以这个文件夹就是/usr/lib文件夹,但是这个lib是从哪里来的呢?
glibc-2.11.2\sysdeps\unix\sysv\linux\configure
# The Linux filesystem standard prescribes where to place "essential"
# files.  I.e., when the installation prefix is "/usr" we have to place
# shared library objects and the configuration files on the root partition
# in /lib and /etc
.
case "$prefix" in
/usr | /usr/)
  # 64-bit libraries on bi-arch platforms go in /lib64 instead of /lib.
  # Allow earlier configure scripts to handle libc_cv_slibdir, libdir,
  # and libc_cv_localedir.
  test -n "$libc_cv_slibdir" || \
  case $machine in
  sparc/sparc64 | x86_64 | powerpc/powerpc64 | s390/s390-64)
    libc_cv_slibdir="/lib64"
    if test "$libdir" = '${exec_prefix}/lib'; then
      libdir='${exec_prefix}/lib64';
      # Locale data can be shared between 32bit and 64bit libraries
      libc_cv_localedir='${exec_prefix}/lib/locale'
    fi
    ;;
  *)
    libc_cv_slibdir="/lib"
    ;;
  esac
猜测是这里的代码根据configure中输入的prefix=/usr来再次设置了slibdir变量。
六、验证这个配置
事实上在ldconfig的输出中已经可以看出两者的分布,但是从其他方面也可以验证这个结论:
1、对于glibc执行configure --prefix=/usr
在文件夹下生成有配置文件
[root@Harry glibcobjconf]# head  config.make  -n 30
# config.make.  Generated from config.make.in by configure.
# Don't edit this file.  Put configuration parameters in configparms instead.

version = 2.11.2
release = stable

# Installation prefixes.
install_root =
prefix = /usr
exec_prefix = ${prefix}
datadir = ${datarootdir}
libdir = ${exec_prefix}/lib
slibdir = /lib
localedir =
虽然不知道这个文件如何生成及如何使用,但是感觉就是这个意思。
2、查看glibc库文件中变量
通过 ar x /usr/lib/libc.so来解包glibc的静态库,可以看到有dl-load.o文件,执行
[root@Harry libcextrace]# readelf -a dl-load.o | grep system_dirs
    10: 00000000    16 OBJECT  LOCAL  DEFAULT    8 system_dirs
    23: 00000010     8 OBJECT  LOCAL  DEFAULT    8 system_dirs_len
文件的第8个节为rodata节,所以执行其内容
 [ 8] .rodata           PROGBITS        00000000 0039ec 00003a 00   A  0   0  4
查看该节内容
[root@Harry libcextrace]# objdump -sj .rodata dl-load.o

dl-load.o:     file format elf32-i386

Contents of section .rodata:
 0000 2f6c6962 2f002f75 73722f6c 69622f00  /lib/./usr/lib/.
 0010 05000000 09000000 04000000 10000000  ................
 0020 01000000 474e5500 7f454c46 01010100  ....GNU..ELF....
 0030 007f454c 46010101 0300               ..ELF.....     
和readelf中显示的大小16一致。
  评论这张
 
阅读(1184)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017