您的位置:首页 > 其它

动态库连接器–动态库链接信息(Mach-O文件格式和程序从加载到执行过程)

2018-01-10 17:52 555 查看
section cmd说明举例
__text主程序代码
__stubs用于动态库链接的桩
__stub_helper用于动态库链接的桩
__cstring常亮字符串符号表描述信息,通过该区信息,可以获得常亮字符串符号表地址
__unwind_info这里字段不是太理解啥意思,希望大家指点下

动态库连接器–动态库链接信息

总结了mach-o文件的两个最重要的部分,那么动态库根据加载命令如何动态链接到内存中的呢?下面总结这个动态过程。

系统通过加载命令,获得动态加载器的地址/usr/lib/dyly(其解决的问题是,把代码段__TEXT中和动态库相关内容进行关联,比如代码中如何调用到哪个动态库的相关代码段的偏移地址)
(dyly是用户态进程,这个是开源的,不属于kern内核的部分)感兴趣可以看看这里

总结下:在加载命令中,和动态库和链接相关命令有如下几个:

段命令

LC_DYLD_INFO_ONLY

LC_LOAD_DYLIB

LC_LOAD_DYLINKER

LC_SYMTAB

LC_DYSYMTAB

区命令

__stubs

__stubs_helper

在进行动态链接器工作前,要先解析相关工作环境参数

LC_LOAD_DYLINKER 获得动态加载器地址

LC_LOAD_DYLIB 二进制文件依赖动态库信息
可以使用otool工具,读取该部分信息

代码5.0

yingfang:mach-o文件结构-src fangying$ otool -L a.out
a.out:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1225.1.1)


LC_DYLD_INFO_ONLY 动态库信息,根据该命令是真正动态库绑定,地址重定向重要的信息。
结合之前otool -l的信息中的command 4命令,下面struct就是该命令对应的数据结构:

代码5.1

struct dyld_info_command {
uint32_t   cmd;     /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */
uint32_t   cmdsize;     /* sizeof(struct dyld_info_command) */
uint32_t   rebase_off;
uint32_t   rebase_size;
uint32_t   bind_off;
uint32_t   bind_size;
uint32_t   weak_bind_off;
uint32_t   weak_bind_size;
uint32_t   lazy_bind_off;
uint32_t   lazy_bind_size;
uint32_t   export_off;
uint32_t   export_size;
};

根据该加载命令的字段偏移,系统可以得到压缩动态数据信息区(dymanic load info)。根据上述数据,dymanic load info数据区,主要包含了5种数据:
(下面的一些内容,我的理解可能不是太正确,希望和大家一起讨论)dyld_info_command具体定义地址

dymanic load info说明举例
重定向数据 rebasedemo中该段数据位 11 22 10 5111: 高位0x10表示设置立即数类型,低位0x01表示立即数类型为指针
22: 表示REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB + 2 重定向到数据段2。结合上面的信息,就是重定向到数据段2,该段数据信息为一个指针
结合数据段2的数据,获得一个重定向符号指针[0x100001010 -> _printf]
绑定数据 bind在demo中进行动态绑定依赖的dyld的函数U dyld_stub_binder
弱绑定数据 weak bind用于弱绑定动态库,就像weak_framework一样
懒绑定数据 lazy bind对于需要从动态库加载的函数符号demo中有两个:
U _printf
U _scanf
export数据用于对外开放的函数demo中只有两个
0000000100000000 T __mh_execute_header
0000000100000f50 T _main
对于相关的绑定函数查找,可以使用nm命令

代码5.2

yingfang:mach-o文件结构-src fangying$ nm a.out
0000000100000000 T __mh_execute_header
0000000100000f50 T _main
U _printf
U dyld_stub_binder

>
标注:dymanic load info数据是以命令码(命令码就是一个字节码)的形式,传递具体内容。高四位表示真正命令名,低四位表示一个立即数。00表示该类型命令结束

可以使用dylyinfo获得这部分信息读取

代码5.3

yingfang:mach-o文件结构-src fangying$ xcrun dyldinfo -opcodes a.out
rebase opcodes:
0x0000 REBASE_OPCODE_SET_TYPE_IMM(1)
0x0001 REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(2, 0x00000010)
0x0003 REBASE_OPCODE_DO_REBASE_IMM_TIMES(1)
0x0004 REBASE_OPCODE_DONE()
binding opcodes:
0x0000 BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(1)
0x0001 BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x00, dyld_stub_binder)
0x0013 BIND_OPCODE_SET_TYPE_IMM(1)
0x0014 BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x02, 0x00000000)
0x0016 BIND_OPCODE_DO_BIND()
0x0017 BIND_OPCODE_DONE
no compressed weak binding info
lazy binding opcodes:
0x0000 BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB(0x02, 0x00000010)
0x0002 BIND_OPCODE_SET_DYLIB_ORDINAL_IMM(1)
0x0003 BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM(0x00, _printf)
0x000C BIND_OPCODE_DO_BIND()
0x000D BIND_OPCODE_DONE
0x000E BIND_OPCODE_DONE
0x000F BIND_OPCODE_DONE


动态库链接器运行结果

上面总结,动态链接器相关信息(加载命令信息,加载命令偏移地址相关信息)。现在总结下动态链接器运行的结果是什么?如何使用相关动态链接信息,完成相关过程的?

从字面上,之前我理解,链接器就是把文本段原来动态库函数相关地址,用真实的动态库地址替换,生成一个真实的完整的可执行文本。比如demo中printf是其它动态库的,但是其真实地址对于可执行文件是不知道,只有这个执行文件运行时,通过动态链接器完善其原来文本段地址。

在总结之前两个问题前,先总结下__stubs(桩)的区概念:该区存放的是二进制文件中未定义符号的占位符,编译器生成代码时会创建对符号桩区的调用,链接器在运行时解决对桩的这些调用。链接器解决方案是在被调用的地址处,放置一条JMP指令。JMP指令将控制权转交给真实的函数体。

回顾下nm命令结果:U表示未定义的符号

yingfang:mach-o文件结构-src fangying$ nm a.out
0000000100000000 T __mh_execute_header
0000000100000f50 T _main
U _printf
U dyld_stub_binder

看下demo中main函数汇编代码:

demo中真实的print函数调用,编译器,编译为callq 0x100000f84 ## symbol stub for: _printf

汇编代码中,callq 0x100000f84 . 注意0x100000f84,这个地址是 __TEXT段的__stubs区的地址。换句话说,就是JMP到__stubs(桩区)– 段1 section位__stubs的起始地址

0x100000f84地址,是一段汇编指令 0x100000f84: jmpq *0x86(%rip) # 0x100001010

0x100001010地址,是指向(代码7.2)数据段__la_symbol_ptr区,由于这个数据都符号指针,查看下该地址指向的数据区域(代码7.3)

0x100001010地址指针,指向地址为4294971292(数据段都在高位,所以相比__TEXT地址,这个地址是正常的)

4294971292地址,执行汇编代码请看(代码7.4) 0x100000fa1: jmpq 0x100000f8c。请注意 0x100000f8c就是__TEXT段section __stub_helper区的起始地址。该地址是执行汇编代码具体请看(代码7.5)0x100000f95: jmpq *0x65(%rip) # 0x100001000

0x100001000地址,恰好是__DATA段, __nl_symbol_ptr区的其实地址,该地址指向的数据值位为 0x100001000: 0x0000000000000000 0x0000000000000000 (代码7.6)

>
总结:

+ __stubs区和__stub_helper区是帮助动态链接器找到指定数据段__nl_symbol_ptr区,二进制文件用0x0000000000000000进行占位,在运行时,系统根据dynamic loader info信息,把占位符换为调用dylib的dyld_stub_binder函数的汇编指令。

+ 当第一次调用完动态库中的符号后,动态链接器会根据dynamic loader info信息,把数据段__la_symbol_ptr指向正在的符号地址,而不是指向_nl_symbol_ptr区
http://blog.csdn.net/bjtufang/article/details/50628310
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: