我通常不会花太多时间阅读汇编,所以以下编译器输出让我感到困惑。
假设我在运行OSX 10.6的Intel Core 2 Duo上编译这段C代码:
while (var != 69) // var is a global variable
{
printf("Looping!\n");
}
“var!= 69”比较的程序集如下所示:
cmpl $69, _var(%rip)
我知道它实际上意味着将值“69”与全局变量“var”的内容进行比较,但我很难理解“_var(%rip)”部分。通常,我希望有一个偏移值,比如引用堆栈中的局部变量(例如:-4($ ebp))。但是,我并没有完全遵循如何使用“_var”声明来偏移指令指针将给出全局变量“var”的内容。
这条线究竟意味着什么?
感谢。
答案 0 :(得分:14)
这与使用offset(%ebp)
寻址堆栈中的局部变量非常相似。在这种情况下,链接器会将该指令的偏移字段设置为var
地址与%rip
执行该指令时的值之间的差值。 (如果我没记错的话,该值是 next 指令的地址,因为%rip
总是指向当前正在执行的指令后的指令。)因此给出var
的地址。
为什么这样?这是position-independent code的标志。如果编译器已生成
cmpl $69, _var
并且链接器填写了var
的绝对地址,然后当你运行程序时,可执行映像总是必须加载到一个特定地址的内存中,所以所有变量都具有代码所期望的绝对地址。通过这种方式,唯一需要修复的是代码和数据之间的距离;
...为什么要这么麻烦?为什么必须在一个特定地址加载可执行文件?它不是必然的。共享库必须与位置无关,因为否则您可能有两个想要在重叠地址加载的库,并且您不能在同一程序中同时使用它们。 (有些系统通过保留所有库的全局注册表及其所需的空间来解决这个问题,但显然这不会扩展。)使可执行文件位置无关在很大程度上是作为安全措施完成的:它是如果您不知道程序代码在内存中的位置(这称为address space layout randomization),则有点难以利用缓冲区溢出。