动态链接到底是谁完成的呢?内核?

这个问题的根源在:使用动态链接库可以省内存,因为多个进程可以共享物理内存;

老觉得这部分逻辑是在用户态的动态加载器完成的,但是想想奇怪,动态加载器是嵌入到进程里的so,案例说是不会感知到外面的世界的啊,难道是内核?

【等等,动态加载器,可能直接就是在不同的进程中中共享的一份,是不是它本身就记录着整个内核层面的库的使用捏?】

在内核中看到一个函数load_elf_interp: 使用stap抓一下函数调用栈

0xffffffff81267e2c : load_elf_binary+0x8fc/0x1170 [kernel]
 0xffffffff81212bee : search_binary_handler+0x9e/0x1d0 [kernel]
 0xffffffff81214353 : do_execveat_common.isra.33+0x533/0x710 [kernel]
 0xffffffff812147ca : sys_execve+0x3a/0x50 [kernel]
 0xffffffff81824795 : return_from_execve+0x0/0x23 [kernel]
 0xffffffff818244f2 : entry_SYSCALL_64_fastpath+0x16/0x71 [kernel] (inexact)

load_elf_binary函数会调用load_elf_interp

本来就应该是内核做的呀,毕竟启动进程是在shell里简简单单做了一个exec而已,内核里面是如何调用动态加载器的?内核把应该映射的段映射好,然后就是

真是丫的长见识了,竟然是: kernel_read(bprm->file, elf_ppnt->p_offset,内核中按照file结构体去读数据出来呢;

先是执行动态加载器,然后再是入口处执行呢,只是动态加载器在

依赖的动态库是怎么加载进来的呢?是内核加载的,还是动态加载器?内核当中直接调用这个函数读取动态加载器。。。open_exec

interpreter = open_exec(elf_interpreter);

一个进程所以来的动态库肯定是有

=======

好像从来都没有试着去编译动态库呢。。。。

gcc -g -o $@ $< cal.so -Xlinker -rpath ./

elf文件中会有个地方存储着这个文件都需要哪一些动态链接库:

readelf -d fun 能看到所有的文件所有依赖的动态库

hon@station6:~/codebox/dlink$ readelf -d fun

Dynamic section at offset 0xe08 contains 26 entries:
  标记        类型                         名称/值
 0x0000000000000001 (NEEDED)             共享库:[cal.so]
 0x0000000000000001 (NEEDED)             共享库:[libc.so.6]
 0x000000000000000f (RPATH)              Library   rpath: [./]
 0x000000000000000c (INIT)               0x400560
 0x000000000000000d (FINI)               0x400764
 0x0000000000000019 (INIT_ARRAY)         0x600df0
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x600df8
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)

动态链接ELF中最重要的结构是.dynamic段,这个段里面保存了动态连接器所需要的基本的信息,比如依赖于哪些共享对象、动态链接符号表额位置、动态链接重定位表的位置、共享对象初始化代码的地址等等。.dynamic可以看成是动态链接下的elf文件头了,

为了表明模块之间的符号导入导出的关系,elf专门有一个动态符号表用来保存这些信息,这个段的段名叫做.dynsym,.dynsym只保存了与动态链接相关的符号,.symtab中保存了所有的符号,但是.dynsym中只保存动态链接的符号;.dynstr符号表;动态链接中,我们需要在运行时查找符号,所以需要符号哈希表“.hash".

 动态链接器的自举代码:elf/rtld.c

肯定是可以共享的啊,因为你们用的是同一个文件,这些文件在内核中是同一个pagecache,然后page-cache的map的可不是一个么。。。

想通了。。。。。。这样减少了内存的使用我擦。。。

-----

动态加载器会把这个elf所有的符号放在

当一个新的so文件被加载的时候,符号表会增加到全局符号表当中,是其中所有符号的地址;需要填充符号的时候,就直接填充符号就哈哦了

.got.plt中存放的啥东西?GOT表中的前三位是固定的,是编译的时候就写好的,readelf中查看.got.plt 中的内容

【0】.dynamic段的起始地址,

【1】这个模块的模块名,比如本例中是cal.so

【2】解析函数的地址,_dl_runtime_resolve的地址

这三个段都是加载的时候确定的吧?readelf -x .got.plt cal.so

hon@station6:~/codebox/gdb$ readelf -x .got.plt cal.so

“.got.plt”节的十六进制输出:
 NOTE: This section has relocations against it, but these have NOT been applied to this dump.
  0x00201000 180e2000 00000000 00000000 00000000 .. .............
  0x00201010 00000000 00000000 d6060000 00000000 ................
  0x00201020 e6060000 00000000 f6060000 00000000 ................
  0x00201030 06070000 00000000                   ........
发现这个段根本就没有,其中[0]中的数值确实是正确的

 [20] .dynamic          DYNAMIC          0000000000200e18  00000e18
       00000000000001c0  0000000000000010  WA       4     0     8

想想也是,只有在动态加载器本身加载了好了,才能知道_dl_runtime_resolve的地址,也就是说,这个地址在真正程序加载之前是肯定不知道的,所以。。。。是全0有情可原,那么什么时候会填充这个位置呢?看动态加载器的代码咯:

DT_MIPS_PLTGOT

还有个问题;

不要以为所有读文件的字符串变量都是从内核态读出来的,也有可能是在内核里面去读取字符串触发读操作的,一切都是在内核中执行,比如这里从elf文件中读取动态链接器的地址并加载;

调试的方法

LD_DEBUG=symbols 真是世界之大无奇不有啊  http://blog.chinaunix.net/uid-24774106-id-3349549.html

https://eli.thegreenplace.net/2011/11/03/position-independent-code-pic-in-shared-libraries/  

LD_DEBUG是动态加载器的一个选项,会输出我当前都对那些符号做了处理

_dl_fixup-->elf_machine_fixup_plt

看下.got.plt段,这个段的大小是:0x38,共有7个项,每个项是8个字节,正好7

  [22] .got.plt          PROGBITS         0000000000201000  00001000 readelf -S cal.so
       0000000000000038  0000000000000008  WA       0     0     8
其中需要重定位的是:  

重定位节 '.rela.plt' 位于偏移量 0x640 含有 4 个条目:readelf -t cal.so
  偏移量          信息           类型           符号值        符号名称 + 加数
000000201018  000300000007 R_X86_64_JUMP_SLO 0000000000000000 puts@GLIBC_2.2.5 + 0
000000201020  001200000007 R_X86_64_JUMP_SLO 0000000000000836 min + 0
000000201028  000a00000007 R_X86_64_JUMP_SLO    max + 0
000000201030  000400000007 R_X86_64_JUMP_SLO 0000000000000000 sayhelloYa + 0
还剩下的三个条目是GOT【0-2】

多出来的12个字节是dynamic段地址,ModuleID 和 _dl_runtime_resolve的地址

使用gdb的watch功能监控GOT表的改变;下面一篇

 

posted @ 2018-03-17 08:27  honpey  阅读(554)  评论(0编辑  收藏  举报