我想知道以下情况。我注意到在编写汇编语言时,IA32的设计方式是促进相对跳跃的使用,即跳转位移量的字节与使用绝对跳转,即将eip更改为内存中的特定地址。这背后的逻辑是什么?
答案 0 :(得分:14)
大多数跳跃都是距离跳转指令不远的目标。由于提供了带有带符号的16位值的跳转指令,因此它们可以比绝对跳转所需的字节少(通常是4个字节加上指令本身)。
相对分支的一个小额外优点是它们不需要在链接器中修复,或者就此而言,需要经过PIC中所需的额外间接(位置无关代码)。
答案 1 :(得分:9)
相对跳转使编译器可以生成可重定位的代码,这意味着代码将在内存中的任何位置运行;它不依赖于固定的位置。这是库背后的关键概念:您可以编写一次代码并将其编译为可重定位的对象文件,该文件可以按原样链接到任何程序中。链接器只需要为外部可访问的函数分配绝对地址(因此您自己的代码可以找到它们);所有“内部”跳转都是相对的,不需要从一个可执行文件更改为下一个。
值得花时间用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
块的未执行分支。无论链接器放置代码的内存在何处,这些跳转都将起作用。如果它们是绝对跳转,则链接器每次链接代码时都需要重新计算地址。
您甚至会发现许多函数调用将生成相对跳转,特别是如果函数在单个文件中作用域。这不仅加快了链接过程,而且使运行代码更小,更高效。这是因为相对地址通常限制在比绝对地址小得多的范围内,这意味着它们可以用更少的字节表示。
希望有所帮助!