@plt 在这里是什么意思?
- 2024-10-14 08:40:00
- admin 原创
- 107
问题描述:
0x00000000004004b6 <main+30>: callq 0x400398 <printf@plt>
有人知道吗?
更新
为什么两个disas printf
给我不同的结果?
(gdb) disas printf
Dump of assembler code for function printf@plt:
0x0000000000400398 <printf@plt+0>: jmpq *0x2004c2(%rip) # 0x600860 <_GLOBAL_OFFSET_TABLE_+24>
0x000000000040039e <printf@plt+6>: pushq $0x0
0x00000000004003a3 <printf@plt+11>: jmpq 0x400388
(gdb) disas printf
Dump of assembler code for function printf:
0x00000037aa44d360 <printf+0>: sub $0xd8,%rsp
0x00000037aa44d367 <printf+7>: mov %rdx,0x30(%rsp)
0x00000037aa44d36c <printf+12>: movzbl %al,%edx
0x00000037aa44d36f <printf+15>: mov %rsi,0x28(%rsp)
0x00000037aa44d374 <printf+20>: lea 0x0(,%rdx,4),%rax
0x00000037aa44d37c <printf+28>: lea 0x3f(%rip),%rdx # 0x37aa44d3c2 <printf+98>
解决方案 1:
这是一种获取代码修复(根据代码在虚拟内存中的位置调整地址,而不同进程的地址可能不同)的方法,无需为每个进程维护单独的代码副本。PLT(即过程链接表)是使动态加载和链接更易于使用的结构之一(另一个是 GOT(即全局偏移表))。
请参阅下图,其中显示了调用代码和库代码(您调用的)映射到两个不同进程中的不同虚拟地址:A
和B
。实际内存中每段代码只有一个副本,每个进程内的不同虚拟地址映射到该实际地址:
Process A
Addresses (virtual):
0x1234 0x8888
+-------------+ +---------+ +---------+
| | | Private | | |
| | | PLT/GOT | | |
| Shared | +---------+ | Shared |
===== application =============== library =====
| code | +---------+ | code |
| | | Private | | |
| | | PLT/GOT | | |
+-------------+ +---------+ +---------+
0x2020 0x6666
Process B
当共享库被引入到地址空间时,将在进程特定的(私有的)PLT 和/或 GOT 中构建条目,这些条目将在首次使用时执行一些修复以使运行速度更快。后续使用将绕过修复,因为不再需要它。
整个过程大致如下。
printf@plt
实际上是一个小存根,它(最终)调用真正的printf
函数,并在调用过程中进行修改,以加快后续调用速度。
真实 函数printf
被映射到给定进程(虚拟地址空间)中的任意位置,就像试图调用它的代码一样。
因此,为了允许调用代码(左侧上方)和被调用代码(右侧)的正确代码共享,您不能直接对调用代码应用任何修复,因为这会“损害”它在其他进程中的工作方式(如果它映射到每个进程中的相同位置,那就无关紧要了,但这是一个有点限制,特别是如果其他东西已经映射到那里)。
因此,在可靠计算的运行时地址处PLT
有一个较小的特定于进程的区域,该区域不会在进程之间共享,因此任何给定的进程都可以自由地更改它,而不会对其他进程产生不利影响。
让我们更详细地了解一下这个过程。上图没有显示 PLT/GOT 的地址,因为可以使用相对于当前程序计数器的位置来找到它。这可以通过您的 PC 相对查找来证明:
<printf@plt+0>: jmpq *0x2004c2(%rip) ; 0x600860 <_GOT_+24>
通过在被调用库中使用位置无关代码以及 PLT/GOT,对函数的第一次printf@plt
调用(在 PLT 中)是一个多阶段操作,其中执行以下操作:
它调用 GOT 版本(通过指针),该版本最初指向 PLT 中的某些设置代码。
如果尚未完成,该设置代码将加载相关的共享库,然后修改GOT 指针,以便后续调用直接转到实际
printf
(在特定于进程的虚拟地址)而不是 PLT 设置代码。然后它调用
printf
该地址处加载的代码。
在后续调用中,由于 GOT 指针已被修改,因此多阶段方法得到简化:
它调用 GOT 版本(通过指针),现在指向真实的
printf
。
这里有一篇很好的文章,详细介绍了如何glibc
在运行时加载。
解决方案 2:
不确定,但您看到的内容可能有意义。第一次运行 disas 命令时,printf 尚未被调用,因此尚未解析。一旦您的程序第一次调用 printf 方法,GOT 就会更新,现在 printf 已解析,GOT 指向真实函数。因此,下一次调用 disas 命令会显示真实的 printf 汇编。
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件