编程高手必学的内存知识
海纳
华为编译器高级专家,原 Huawei JDK 团队负责人
20674 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 33 讲
编程高手必学的内存知识
15
15
1.0x
00:00/00:00
登录|注册

08 | 动态链接(下):延迟绑定与动态链接器是什么?

你好,我是海纳。
在上节课里,我们学习了动态链接过程的基本原理。动态链接通过 GOT 表加一层间接跳转的方式,解决了代码中 call 指令对绝对地址的依赖,从而实现了 PIC 的能力。我们同时也讲到了 GOT 表中的地址是由加载器在加载时填充的。
不过,细心的你也发现了,动态链接带来的代价是性能的牺牲。这里性能的牺牲主要来自于两个方面:
每次对全局符号的访问都要转换为对 GOT 表的访问,然后进行间接寻址,这必然要比直接的地址访问速度慢很多;
动态链接和静态链接的区别是将链接中重定位的过程推迟到程序加载时进行。因此在程序启动的时候,动态链接器需要对整个进程中依赖的 so 进行加载和链接,也就是对进程中所有 GOT 表中的符号进行解析重定位。这样就导致了程序在启动过程中速度的减慢。
我们这节课来看看,如何通过延迟绑定技术,来解决性能下降的问题。延迟绑定不仅仅是用在动态链接中,还被广泛地应用在 Hotspot,V8 等带有即时编译功能的虚拟机中。另外,在游戏行业,修复服务器的错误的同时保证用户不掉线是硬需求,这种不停机进行代码修复的技术被称为热更新技术。学习完这节课后,你不仅能理解动态链接的基本原理,而且也能对热更新的基本原理有所感悟。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

延迟绑定技术在动态链接中的应用是本文的重点。通过对动态链接技术的原理和实现过程进行深入分析,文章详细介绍了延迟绑定技术的具体实现方式,并通过示例展示了其应用。文章指出,延迟绑定技术将函数地址的重定位工作推迟到第一次访问时进行,避免了对未使用的全局函数进行重定位,从而提高了程序性能。此外,文章还介绍了patch code技术和动态链接器的基本原理,为读者提供了全面的技术知识。整个Loader加载动态链接的可执行文件流程也得到了详细阐述。文章内容深入浅出,适合读者快速了解动态链接和延迟绑定技术的基本原理和应用。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《编程高手必学的内存知识》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(11)

  • 最新
  • 精选
  • keepgoing
    三节课的编译链接内容感觉收获不小,尝试把知识串连一下加深印象: 编译器:生成每个编译单元的机器码,生成重定位表,符号地址0占位 链接器:合并目标文件,静态符号分配地址,重定位表寻找静态符号填址 加载器(加载过程):重定位表找动态符号,根据符号所在动态库,把符号偏移定位在各个动态库的GOT表中。对于各个动态库中的符号,进行地址分配,填回GOT表中。 - 对于动态库本身,编译时会将需要重定位类型的符号也放到GOT表中,等到与可执行文件共同被装载的时候分配虚拟内存空间。 加载器(运行时加载):重定位表找动态符号,把符号引向PLT表,真正运行的时候找到对应动态库加载,将虚拟地址填到动态库GOT表中,保存下一次直接访问。 - 所以编译动态库的时候如果用了plt,跟上面的区别就是会把需要重定位的符号引向自身的plt表中,来间接访问GOT表。 不知道有没有理解错误的地方。 另外还想向老师请教几个问题: 1、plt可以理解为一段不会变更的跳转规则封装吗,目的就是为了避免直接修改执行指令,是不是也能理解不是绝对安全的,如果能直接修改GOT已经存好的符号地址(只不过难度比较大)? 2、上节课的提问老师帮忙解释了进程不会有GOT,只会存在动态库中,所以我上面在总结时猜测的多个动态库加载的情况,对于可执行文件中对动态库符号的访问,是不是就得访问多个动态库的GOT表,因为动态库本身编译的segement位置是固定的,如果合并GOT,自身依赖其他动态库符号就没法统一偏移了。 3、延迟绑定技术的设计是进程和各个动态库都会有一份自己的plt吗,因为看文中说的使用延迟绑定技术只有一个参数 -fno-plt 控制,是不是可以理解加载时链接和运行时链接这两种动态链接方式只能存在一种。 4、老师在最后举了一个A、B动态库符号相同的例子。如果情况编程可执行文件有方法foo,动态库A的方法foo2依赖动态库B的方法foo,如果在可执行文件里调用foo2方法,最终被调用的结果是不是也有不同的情况? 老师这三节课的确讲的太好了,所以疑问产生也比较多,感谢老师赐教。

    作者回复: 总得得非常非常好!我已经把你的总结截图发到微信交流群里了:)这四个问题,前三个,你的猜想都是对的。GOT表在内存只能和动态链接库靠在一起,它是万万不能合并的,否则代码段对它的访问就不对了。最后一个问题,最终会调用谁取决于解析符号的顺序,解析到谁就是谁。

    2021-11-11
    5
    10
  • kylin
    -fPIC 选项有时候是不需要的。 在动态链接库不需要依赖其它动态库的时候,就不需要位置无关代码,所以我认为在这种特殊场景下是不需要打开-fPIC的。

    作者回复: very good

    2021-11-10
    3
    8
  • thomas
    在 Hotspot 里的 patch code 技术,会直接修改指令参数。不过,运行时修改指令总是一件很危险的事情. ---------------------------------------------------------> 老师,代码段不是可读的吗,怎么能够修改指令?

    作者回复: 看一下第11节课。JIT编译出来的代码,是运行时编译的,所以我们会申请一块可写可执行的内存用于存储JIT的代码,这就导致JIT代码所在的内存区域可以在运行时修改,从而可以支持patch code技术。所以代码段只读这个说法只对静态编译是正确的,对于动态编译则不正确。

    2021-12-30
    5
  • shenglin
    Disassembly of section .got.plt: 0000000000201000 <_GLOBAL_OFFSET_TABLE_>: 201000: 08 0e or %cl,(%rsi) 201002: 20 00 and %al,(%rax) ... 201018: 46 06 rex.RX (bad) 20101a: 00 00 add %al,(%rax) 20101c: 00 00 add %al,(%rax) 20101e: 00 00 add %al,(%rax) 201020: 56 push %rsi 201021: 06 (bad) 201022: 00 00 add %al,(%rax) 201024: 00 00 add %al,(%rax) 0000000000000630 <extern_func@plt-0x10>: 630: ff 35 d2 09 20 00 pushq 0x2009d2(%rip) # 201008 <_GLOBAL_OFFSET_TABLE_+0x8> 636: ff 25 d4 09 20 00 jmpq *0x2009d4(%rip) # 201010 <_GLOBAL_OFFSET_TABLE_+0x10> 63c: 0f 1f 40 00 nopl 0x0(%rax) 0000000000000640 <extern_func@plt>: 640: ff 25 d2 09 20 00 jmpq *0x2009d2(%rip) # 201018 <_GLOBAL_OFFSET_TABLE_+0x18> 646: 68 00 00 00 00 pushq $0x0 64b: e9 e0 ff ff ff jmpq 630 <_init+0x28> 0000000000000650 <global_func@plt>: 650: ff 25 ca 09 20 00 jmpq *0x2009ca(%rip) # 201020 <_GLOBAL_OFFSET_TABLE_+0x20> 656: 68 01 00 00 00 pushq $0x1 65b: e9 d0 ff ff ff jmpq 630 <_init+0x28> 老师请问一下,从地址650处跳转到GOT表里执行之后,按照上面说的流程,GOT表里应该立即执行一条跳转指令,回到地址656处执行参数压栈,但是GOT表里的指令好像没有执行这个,是哪里有问题吗

    作者回复: 这里可能是我没讲清楚哈。GOT表里的是一个地址,跳转发生在plt的第一行,你观察一下,plt中的那条jmp指令,它跳转的目标是从GOT里获取的。所以GOT里是数据,不是指令。你再看一下0x201020那个位置是不是0x0656? :)

    2021-11-18
    2
    2
  • 小时候可鲜啦
    gdb调试的过程中发现 在main函数内调用动态库定义的函数之前其plt表内对应的地址项已经被重写了,这是怎么回事?貌似动态链接器在装载期就完成了动态库符号的重定位。

    作者回复: 你是想说GOT表吧?你注意看一下-fno-plt是打开的还是关闭的。这将决定是加载时重定位还是运行期重定位。

    2021-12-09
    2
  • 我是内存
    你好,文中描述plt的时候说:我们再回到图中看看,在序号①箭头的位置,也就是第一级跳转,它的目的是把参数 0 入栈。由于 GOT 表的 0x0,0x8,0x10 的位置都被占用了,所以参数 0 代表的就是 0x18 位置,这就是 B 函数的真实地址应该存放的地方。 ------------------- 这里没有懂。为什么got表里的0x0,0x08,0x10被占用了,参数0就代表0x18呢?如果0x18也被占用了,那参数0就代表0x20吗? 这里的“参数0”是指数值0吗?还是第0个参数?

    作者回复: 这段文字的意思是,linker和loader在约定好了,第一个参数放在0x18,这个过程不是自动的。是实现者的一个约定。两边商量好,你这么实现,我这么用。

    2021-11-20
  • kylin
    海纳老师,您好,我有一个疑问,加载时动态链接的话,每个.so文件(动态链接库)是不是都有一个自己的GOT表?

    作者回复: 有不需要的情况,可以想一下got产生的动机是什么?它要解决什么问题?有没有哪种情况不存在这个问题的?

    2021-11-10
    2
  • 凯文小猪
    动态链接延迟绑定在北美西北大学的课程网站上也有描述,文章也详述了GOT表前三项的表示含义: 截取GOT[1]的内容可做印证: GOT[1] is the pointer to a data structure that the dynamic linker manages. This data structure is a linked list of nodes corresponding to the symbol tables for each shared library linked with the program. When a symbol is to be resolved by the linker, this list is traversed to find the appropriate symbol. Using the LD_PRELOAD environment variable basically ensures that your preload library will be the first node on this list. reloc_index参数的意义: The next instruction pushes some info on the stack (the PLT offset) and jumps to the very first entry into the PLT, which calls into the dynamic linker's resolution function (_dl_runtime_resolve for ld.so). 原文链接:http://users.eecs.northwestern.edu/~kch479/docs/notes/linking.html
    2023-02-21归属地:上海
    1
    2
  • 凯文小猪
    简单回应下这一节的PIC内容 其实解析PIC无关函数地址,目前linux上是使用_dl_runtime_resolve(link_map_obj, reloc_index)函数 该函数是一段汇编代码 源码解析可以在网上找到 建议只看英文 link_map_obj:要求传的是link_map,也就是维护.dynstr、.dynsym、.rela.plt等的双向链表 reloc_index:是.rela.plt表的索引项 老师给的图实际上是有歧义的,因为根据Sysytem V ABI Intel i386定义,GOT表前三项是保留字段 GOT[0] 是.dynamic地址 GOT[1] 是link_map地址 GOT[2] 是_dl_runtime_resolve地址 所以B.plt 的push 0x0 不能说是因为GOT前三项是被占用,所以压入0自然就是B函数,而是因为B函数在重定向表.rela.plt的索引是0,所以才压入0 要想明白动态链接流程,还是应该从汇编角度理解_dl_runtime_resolve在X86上调用流程 最后函数调用完,确实是回填GOT表,因为GOT表在数据段是可读可写的,下次就不用再解析一次了
    2023-02-21归属地:上海
    1
    2
  • 一个工匠
    老师是大王,赞
    2022-11-01归属地:浙江
收起评论
显示
设置
留言
11
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部