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

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

Linux下动态链接原理  

2011-06-28 23:13:43|  分类: Linux内核 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

一、动态连接器的设置

动态连接器一般有一个自动设置项,而且默认值一般式正确的,但是连接器也给出了选项,你可以自己设置这个选项。GNU手册中对于这一项的说明为

--dynamic-linker file
Set the name of the dynamic linker. This is only meaningful when generating dynamically linked ELF executables. The default dynamic linker is normally correct; don't use this unless you know what you are doing.

一般我们默认的就是

/lib/ld.so.1

注意:这是一个绝对路径,其它地方同名文件不行。这种情况可能存在某些嵌入式系统中。

二、操作系统对动态连接器的加载

当使用了动态连接器,连接器会在省城的可执行文件的Program Header中加上一项,

#define PT_INTERP  3
然后操作系统在加载这个可知文件的时候将后首相将这个可执行文件映射到将要执行的文件的地址空间中,然后将控制权传递给解释器而不是可执行文件本身指定的程序入口,这样就可以透明的在可执行文件执行之前完成可执行文件的加载

对应的内核代码

static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)

……

 for (i = 0; i < loc->elf_ex.e_phnum; i++) {
  if (elf_ppnt->p_type == PT_INTERP) {
   /* This is the program interpreter used for
    * shared libraries - for now assume that this
    * is an a.out format binary
    */
   retval = -ENOEXEC;
   if (elf_ppnt->p_filesz > PATH_MAX ||
       elf_ppnt->p_filesz < 2)
    goto out_free_ph;

   retval = -ENOMEM;
   elf_interpreter = kmalloc(elf_ppnt->p_filesz,
        GFP_KERNEL);
   if (!elf_interpreter)
    goto out_free_ph;

   retval = kernel_read(bprm->file, elf_ppnt->p_offset,
          elf_interpreter,
          elf_ppnt->p_filesz);

在其中的生成table的时候,执行的操作为

create_elf_tables(struct linux_binprm *bprm, struct elfhdr *exec,
……

#define NEW_AUX_ENT(id, val) \
 do { \
  elf_info[ei_index++] = id; \
  elf_info[ei_index++] = val; \
 } while (0)

#ifdef ARCH_DLINFO
 /*
  * ARCH_DLINFO must come first so PPC can do its special alignment of
  * AUXV.
  * update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT() in
  * ARCH_DLINFO changes
  */
 ARCH_DLINFO;
#endif

从之后的代码可以看到,这个结构是在argc argv 以及envp之后,并且紧邻这个这些内存。这一点非常重要,因为之后动态连接器将会依赖这个特征。我们可以在用户态通过

hexdump /proc/pid/auxv

来显示一个线程的这个结构,其中每组为一个dword,共32bits。

三、用户态的动态连接器解析

glibc-2.7\elf\ dl-sysdep.c

#ifndef __ASSUME_STD_AUXV

/* The PowerPC's auxiliary argument block gets aligned to a 16-byte
   boundary.  This is history and impossible to change compatibly.  */

#define DL_FIND_ARG_COMPONENTS(cookie, argc, argv, envp, auxp) \
  do {               \
    char **_tmp;             \
    size_t _test;             \
    (argc) = *(long int *) cookie;           \
    (argv) = (char **) cookie + 1;           \
    (envp) = (argv) + (argc) + 1;           \
    for (_tmp = (envp); *_tmp; ++_tmp)           \
      continue;              \
    /* The following '++' is important!  */          \
    ++_tmp;              \
               \
    _test = (size_t)_tmp;            \
    _test = (_test + 0xf) & ~0xf;           \
    /* Under some circumstances, MkLinux (up to at least DR3a5)        \
       omits the padding.  To work around this, we make a        \
       basic sanity check of the argument vector.  Of         \
       course, this means that in future, the argument         \
       vector will have to be laid out to allow for this        \
       test :-(.  */             \
     if (((ElfW(auxv_t) *)_test)->a_type <= 0x10)         \
       _tmp = (char **)_test;            \
    (auxp) = (ElfW(auxv_t) *) _tmp;           \
  } while (0)
#endif

由于ld.so现在是在将要执行的可执行文件的进程空间中,所以这个操作可以获得内核为这个用户设置的信息,从而可以知道这个程序的大部分内容,其中最为重要的就是一个可执行文件的 program_header和program_num,通过这两个变量就可以知道其中的信息,以及ENTRY基本信息,从而可以完成重定向,因为可以从program_header的dynamic节中读出这个可执行文件的NEEDED成员,从而可以完成各个模块的加载和重定向

四、内核态及用户态对本身为so文件的处理

同样是load_elf_binary函数中对ET_DYN的相关处理,可以看到,该函数对so文件几乎没有做任何特殊处理,而只是作为一个简单的可执行文件来处理。

而用户态的连接器也正是使用了这个特征glibc-2.7\elf\rtld.c

 if (*user_entry == (ElfW(Addr)) ENTRY_POINT) 基地址和静态地址相同
    {
      /* Ho ho.  We are not the program interpreter!  We are the program
  itself!  This means someone ran ld.so as a command.  Well, that
  might be convenient to do sometimes.  We support it by
  interpreting the args like this:

  ld.so PROGRAM ARGS...

  The first argument is the name of a file containing an ELF
  executable we will load and run with the following arguments.
  To simplify life here, PROGRAM is searched for using the
  normal rules for shared objects, rather than $PATH or anything
  like that.  We just load it and use its entry point; we don't
  pay attention to its PT_INTERP command (we are the interpreter
  ourselves).  This is an easy way to test a new ld.so before
  installing it.  */
      rtld_is_main = true;

 

五、so文件的转发

在生成so文件的时候,我们可以通过soname来修改那些来链接这个so文件的程序中needed中体现的so文件的名称,从而实现转发功能。例如,在生成的libc.so.1可以转发到libc.so.2,从而可以对低版本的可执行文件保持兼容,并且可以使用新的版本的so文件。

  评论这张
 
阅读(1174)| 评论(0)
推荐 转载

历史上的今天

评论

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

页脚

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