【发布时间】:2011-01-15 03:51:48
【问题描述】:
我想知道以下内容。我在编写汇编语言时注意到,IA32 的设计方式是促进使用相对跳转,即跳转字节的位移量与使用绝对跳转,即将 eip 更改为内存中的特定地址。这背后的逻辑是什么?
【问题讨论】:
-
请注意,在大多数情况下,您使用的是当前可用的地址,而不是您选择的任何地址。
我想知道以下内容。我在编写汇编语言时注意到,IA32 的设计方式是促进使用相对跳转,即跳转字节的位移量与使用绝对跳转,即将 eip 更改为内存中的特定地址。这背后的逻辑是什么?
【问题讨论】:
大多数跳转是到离跳转指令不远的目标。由于提供的跳转指令采用带符号的 16 位值,因此它们的字节数可能少于绝对跳转所需的字节数(通常是 4 个字节加上指令本身)。
相对分支的一个小的额外优势是它们不需要在链接器中进行修复,或者就此而言,通过 PIC 中所需的额外间接(位置无关代码)。
【讨论】:
相对跳转使编译器可以生成可重定位代码,这意味着代码可以在内存中的任何位置运行;它不绑定到固定位置。这是库背后的关键概念:您可以编写一次代码并将其编译为可重定位的 object 文件,该文件可以按原样链接到任何程序中。链接器只需要为外部可访问的函数分配绝对地址(以便您自己的代码可以找到它们);所有“内部”跳转都是相对的,不需要从一个可执行文件更改为下一个。
值得您花时间用 C 等高级语言编写一些代码,让编译器生成汇编代码(检查 gcc 的 -S 选项),然后读取汇编输出。特别注意条件和循环,比如if、for和while,你会发现它们都会产生相对跳转。
这是一个使用虚构汇编指令的人为示例:
// Sample C code Address Assembly Code Comments
if (x < 10) { 0000 CMP x,#10 ; Compare x to 10
do_true_stuff(); 0003 JGE +7 ; Skip 7 bytes to 000A if x>=10
} else { 0005 SUB do_true_stuff ; x<10
do_false_stuff(); 0008 JMP +5 ; Skip 5 bytes to 000D
} 000A SUB do_false_stuff ; else-branch (x>=10)
do_more_stuff(); 000D SUB do_more_stuff ; Finished if/else
在这里,编译器生成相对跳转(JGE 和 JMP)以跳过 if-else 块的未执行分支。无论链接器将代码放在内存中的哪个位置,这些跳转都将起作用。如果它们是绝对跳转,则链接器每次链接代码时都需要重新计算地址。
您甚至会发现许多函数调用会产生相对跳转,尤其是当函数的作用域位于单个文件中时。这不仅加快了链接过程,而且还使运行代码更小、更高效。这是因为相对地址通常被限制在比绝对地址小得多的范围内,这意味着它们可以用更少的字节表示。
希望有帮助!
【讨论】: