我在这里特别从9.2节开始阅读本节:https://www.nasm.us/xdoc/2.14.02/html/nasmdoc8.html#section-8.4
我整天都在读书,我想确保在继续之前我了解这里发生了什么。我相信他们说我们需要强制执行位置无关的代码,因为使用共享库时,访问某些全局变量时的重定位一定不能硬编码。因此,我们使用全局偏移表。
我仍然相信他们会继续说:
call .get_GOT
.get_GOT:
pop ebx
add ebx,_GLOBAL_OFFSET_TABLE_+$$-.get_GOT wrt ..gotpc
首先将值ebx推入堆栈即可使用GOT,.get_GOT将GOT相对于rip寄存器的位置放回ebx。
仅此而已,我相信我理解正确。如果有人可以澄清这是怎么回事
add ebx,_GLOBAL_OFFSET_TABLE_+$$-.get_GOT wrt ..gotpc
我将不胜感激。另外,请在该add指令中将其分为关于整个第二操作数部分的部分。谢谢。
答案 0 :(得分:0)
这是那些疯狂的事情之一,不知何故仍然是一件事......而且在我看到的任何地方都没有很好的解释。
我会用 64 位寄存器来回答,因为现在世界是 64 位的,而指针是 64 位的。
调用和推送后,rbx
包含 .get_GOT
的地址。这是因为调用将 rip
压入堆栈,而 rip
始终指向当前指令(通常是 next 指令)之后的地址。因此,pop 将 .get_GOT
的绝对地址移动到 rbx
中。到目前为止,一切都很好。
_GLOBAL_OFFSET_TABLE_
是一个符号(它在 ELF 文件的符号表中),它被定义为指向 GOT 的开头。为什么不直接使用 mov rbx, _GLOBAL_OFFSET_TABLE_
?出于历史遗忘的原因……或者至少是我失去了。该指令在我的(64 位)机器上运行良好。同样,$$
是定义为指向当前部分开头的符号。
现在,让我们做一些地址数学。
add ebx, _GLOBAL_OFFSET_TABLE_ + $$ - .get_GOT wrt ..gotpc
= .get_GOT + _GLOBAL_OFFSET_TABLE_ + $$ - .get_GOT wrt ..gotpc
请注意,我们有 .get_GOT - .get_GOT
。我们可以摆脱它。
= .get_GOT + _GLOBAL_OFFSET_TABLE_ + $$ - .get_GOT wrt ..gotpc
= _GLOBAL_OFFSET_TABLE_ + $$ wrt ..gotpc
现在,让我们看看 wrt ..gotpc
的定义。
使用wrt ..gotpc 引用标记全局偏移表基础的符号将最终给出从当前部分的开头到全局偏移表的距离。 (((GLOBAL_OFFSET_TABLE)) 是用于引用 GOT 的标准符号名称。)因此,您需要将 $$ 添加到结果中以获取 GOT 的真实地址。
基于此,我们可以将表达式 _GLOBAL_OFFSET_TABLE_ wrt ..gotpc
替换为表达式 _GLOBAL_OFFSET_TABLE_ - $$
。 (这可能是肯定的,因为 GOT 很可能位于比程序更高的地址中。)这就解释了需要添加 NASM 手册中提到的 $$
。
我们替换。
= _GLOBAL_OFFSET_TABLE_ + $$ wrt ..gotpc
= $$ + _GLOBAL_OFFSET_TABLE_ wrt ..gotpc
= $$ + (_GLOBAL_OFFSET_TABLE_ - $$)
= _GLOBAL_OFFSET_TABLE_
等等!希望这会有所帮助。
答案 1 :(得分:-1)
我在理解所有上下文方面有些麻烦。
CALL x 等效于PUSH RIP,JMP x
因此,下一条指令将获得当前的RIP。
注意:此代码是提供给链接器的,因此链接器将读取该代码并放入正确的CALL地址,但NASM在组装时无法知道该地址。
最后一条指令是ADD:我们将EBX添加到常量中。是的,将在组装时计算一个常数。
我们缺乏足够的信息来详细说明,但是如您所见,我们从RIP(在ebx中)添加了偏移量。
$$的值等于当前部分的开头; section 3.5
对于wrt运算符:section 7.6,并且很可能这部分将更好地解释您正在查找的代码。
我对NASM的看法太过乐观了。