- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我知道在典型的 ELF 二进制文件中,函数是通过过程链接表 (PLT) 调用的。函数的 PLT 条目通常包含到全局偏移表 (GOT) 条目的跳转。该条目将首先引用一些代码以将实际函数地址加载到 GOT 中,并包含第一次调用后的实际函数地址(惰性绑定(bind))。
准确地说,在将 GOT 入口点延迟绑定(bind)回 PLT 之前,跳转到 GOT 之后的指令。这些指令通常会跳转到 PLT 的头部,从那里调用一些绑定(bind)例程,然后更新 GOT 条目。
现在我想知道为什么有两种间接方式(调用 PLT,然后从 GOT 跳转到一个地址),而不是仅仅保留 PLT 并直接从 GOT 调用地址。看起来这可以节省跳跃和完整的 PLT。当然,您仍然需要一些代码来调用绑定(bind)例程,但这可以在 PLT 之外。
有什么我想念的吗?额外 PLT 的目的是什么?
更新:
正如评论中所建议的,我创建了一些(伪)代码 ASCII 艺术来进一步解释我所指的内容:
据我了解,这是在当前 PLT 方案中延迟绑定(bind)之前的情况:(PLT 和 printf
之间的一些间接关系由“...”表示。)
Program PLT printf
+---------------+ +------------------+ +-----+
| ... | | push [0x603008] |<---+ +-->| ... |
| call j_printf |--+ | jmp [0x603010] |----+--...--+ +-----+
| ... | | | ... | |
+---------------+ +-->| jmp [printf@GOT] |-+ |
| push 0xf |<+ |
| jmp 0x400da0 |----+
| ... |
+------------------+
Program PLT printf
+---------------+ +------------------+ +-----+
| ... | | push [0x603008] | +-->| ... |
| call j_printf |--+ | jmp [0x603010] | | +-----+
| ... | | | ... | |
+---------------+ +-->| jmp [printf@GOT] |--+
| push 0xf |
| jmp 0x400da0 |
| ... |
+------------------+
Program Lazy Binding Table printf
+-------------------+ +------------------+ +-----+
| ... | | push [0x603008] |<-+ +-->| ... |
| call [printf@GOT] |--+ | jmp [0x603010] |--+--...--+ +-----+
| ... | | | ... | |
+-------------------+ +-->| push 0xf | |
| jmp 0x400da0 |--+
| ... |
+------------------+
Program Lazy Binding Table printf
+-------------------+ +------------------+ +-----+
| ... | | push [0x603008] | +-->| ... |
| call [printf@GOT] |--+ | jmp [0x603010] | | +-----+
| ... | | | ... | |
+-------------------+ | | push 0xf | |
| | jmp 0x400da0 | |
| | ... | |
| +------------------+ |
+------------------------+
最佳答案
问题是替换 call printf@PLT
与 call [printf@GOTPLT]
要求编译器知道函数 printf
存在于共享库中而不是静态库中(甚至仅存在于普通对象文件中)。链接器可以更改 call printf
进入 call printf@PLT
, jmp printf
进入 jmp printf@PLT
甚至mov eax, printf
进入 mov eax, printf@PLT
因为它所做的只是根据符号 printf
更改重定位根据符号 printf@PLT
进行重定位.链接器无法更改 call printf
进入 call [printf@GOTPLT]
因为它从重定位中不知道它是 CALL 指令还是 JMP 指令或完全其他的东西。在不知道它是否是 CALL 指令的情况下,它不知道是否应该将操作码从直接 CALL 更改为间接 CALL。
但是,即使有一个特殊的重定位类型表明该指令是 CALL,您仍然会遇到直接调用指令是 5 个字节长而间接调用指令是 6 个字节长的问题。编译器必须发出 nop; call printf@CALL
之类的代码给链接器空间来插入所需的额外字节,并且它必须为对任何全局函数的所有调用执行此操作。由于所有额外且实际上不是必需的 NOP 指令,它可能最终会导致净性能损失。
另一个问题是在 32 位 x86 目标上,PLT 条目在运行时被重新定位。间接jmp [xxx@GOTPLT]
PLT 中的指令不像直接 CALL 和 JMP 指令那样使用相对寻址,因为 xxx@GOTPLT
的地址取决于图像在内存中的加载位置,需要修复指令以使用正确的地址。通过将所有这些间接 JMP 指令组合成一个 .plt
section 意味着需要修改的虚拟内存页面数量要少得多。每个被修改的 4K 页面都不能再与其他进程共享,当需要修改的指令分散在内存中时,它需要更大的部分图像不共享。
请注意,这个后面的问题只是共享库和在 32 位 x86 目标上定位独立可执行文件的问题。传统的可执行文件无法重定位,因此无需修复 @GOTPLT 引用,而在 64 位 x86 目标上,RIP 相对寻址用于访问 @GOTPLT 条目。
由于最后一点,新版本的 GCC(6.1 或更高版本)支持 -fno-plt
旗帜。在 64 位 x86 目标上,此选项导致编译器生成 call printf@GOTPCREL[rip]
指令而不是 call printf
指示。但是,对于未在同一编译单元中定义的函数的任何调用,它似乎都会这样做。那就是它不确定的任何函数都没有在共享库中定义。这意味着间接跳转也将用于调用其他目标文件或静态库中定义的函数。在 32 位 x86 目标上,-fno-plt
除非编译与位置无关的代码( -fpic
或 -fpie
),否则该选项将被忽略,它会导致 call printf@GOT[ebx]
正在发出的指令。除了产生不必要的间接跳转之外,这还有一个缺点,即需要为 GOT 指针分配一个寄存器,尽管大多数函数无论如何都需要分配它。
最后,Windows 可以通过在头文件中使用“dllimport”属性声明符号来执行您的建议,表明它们存在于 DLL 中。这样编译器就知道在调用函数时是生成直接调用指令还是间接调用指令。这样做的缺点是符号必须存在于 DLL 中,因此如果使用此属性,则无法在编译后决定链接到静态库。
另请阅读 Drepper 的 How to write a shared library论文,它详细解释了这一点(对于Linux)。
关于assembly - 为什么除了 GOT 之外还有 PLT,而不是仅仅使用 GOT?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56283367/
我正在努力解决一个问题 Rahul 正在玩一个非常有趣的游戏。他有 N 个圆盘(每个圆盘的半径相等)。每个磁盘都有一个不同的数字,从 1 到 N 与之相关联。磁盘一个接一个地放在一堆中。 Rahul
我正在尝试使用此代码发出请求: public JsonObject account() throws BinanceApiException { return (new Request
我使用的是 Mac OS 和 emacs -nw (终端模式)。 我不知道如何在 emacs 之外粘贴东西(已由 M-w 在 emacs -nw 中实现)。 我知道emacs -ns可以做到。 搜索互
我试图让导航栏菜单出现在“标题容器”菜单中,但由于某种原因,导航栏链接流到外面(在修改浏览器窗口之前)。我不明白为什么,但我怀疑它与这一行有关: div class="collapse navbar-
我们的项目是在 WAS 6.1/hibernate/struts 上使用 RAD 7.0 开发的中型 Web 应用程序,该应用程序已投入生产。目前我们在属性文件中硬编码了生产系统的 IP 地址,在 h
我的要求是在传单中创建 N 类型的标记。该列表很大,无法容纳在 map 区域中。 我想要类似的东西: http://blog.georepublic.info/2012/leaflet-example
如 docs 中所述,基于 spring-boot 的 Web 服务正在使用 Sentry .它工作正常,但不应将某些异常发送到 Sentry ,例如为了在某些请求上返回 HTTP 状态 410
我已经阅读了 Apple Core Animation 文档。它说核心动画没有提供在窗口中实际显示图层的方法,它们必须由 View 托管。当与 View 配对时, View 必须为底层图层提供事件处理
我试图在滚动时检查元素是否在我的视口(viewport)内。如果它在我的视口(viewport)之外,我会添加一个类来将元素固定到顶部。 我用来确定元素是否在视口(viewport)之外的函数是: i
我正在查询中创建一个弹出窗口。悬停时弹出窗口一切正常。当用户的鼠标离开 div 以关闭它时,我让它看到计时器启动。如果他在计时器完成之前再次进入 div,则计时器将被清除。 这很好,但是如果用户点击
我使用名为 zonemap 的字典创建了一个 4x6 区域 map 。我在该字典中嵌套了多个字典;每个区域代表玩家可以访问并与之互动的区域。我希望能够将玩家的移动限制在该 4x6 区域,并重新显示他们
我正在构建一个页面,该页面将使用 ajax 来更新主要内容区域。用户将单击左侧菜单栏中的项目来更新右侧的 div 并包含搜索结果。 我想检测用户是否向下滚动到目前为止导致右侧结果 div 移出视口(v
好的,我在 div 中有一个带有拖放类的表格,其溢出设置为“自动”,这允许我隐藏部分时间表,只在底部放置一个滚动条。但是,我只是在可滚动 div 之外创建了一些可放置元素,并且我的可拖动元素无法离开可
我有大量项目绑定(bind)到 ListBox,VirtualizingStackPanel 设置为它的 ItemsPanel。随着用户滚动和项目容器的创建,我做了一些工作来用数据填充项目(使用数据库
我想知道是否有一种方法可以将类成员的访问范围专门限定为在 C# 中获取/设置实现,以减少我意外直接访问它们的可能性。类似 private 的东西,但只允许 get/set 访问它,我想我可以将每个变量
我正在尝试编写一个小游戏,以应用我自己在本类(class)中学到的概念。当游戏打开时,我想要一个自定义模态视图来告诉用户如何玩。同样,当他们输了时,我想呈现一个结果页面,该页面将位于 if 语句内。我
我有一个非常具体的 HTML/CSS 和/或 JS 问题。我在 this fiddle here 创建了一个示例显示问题。 我有一个可滚动的 div,它是一个表的父级: ...我的表格行之一包
我的 jar 文件中打包了一个 exe,我试图将它复制到一个临时位置,以便我可以使用 Desktop.browse() 运行它,为此我设置了一个使用 class.getResourceAsStream
您好,我对这段代码有疑问。我的问题是第一个 console.log(smile_per_sec) 给了我需要的值,但是第二个给了我声明变量时给它的值。 $.getJSON( twitter
我必须更改标记弹出窗口的默认大小以容纳我想放入其中的数据。我更改了一些 map 设置,因此当用户将其拖出 View 时,它总是会弹回最大范围。我遇到的问题是,对于靠近边缘的标记,当它的弹出窗口打开时,
我是一名优秀的程序员,十分优秀!