【发布时间】:2017-03-12 18:33:54
【问题描述】:
我正在关注this tutorial 关于大会。
根据教程(我在本地也试过,结果差不多),源码如下:
int natural_generator() { int a = 1; static int b = -1; b += 1; /* (1, 2) */ return a + b; }
编译为这些汇编指令:
$ gdb static (gdb) break natural_generator (gdb) run (gdb) disassemble Dump of assembler code for function natural_generator: push %rbp mov %rsp,%rbp movl $0x1,-0x4(%rbp) mov 0x177(%rip),%eax # (1) add $0x1,%eax mov %eax,0x16c(%rip) # (2) mov -0x4(%rbp),%eax add 0x163(%rip),%eax # 0x100001018 <natural_generator.b> pop %rbp retq End of assembler dump.
(由我添加的行号 cmets (1)、(2) 和 (1, 2)。)
问题:为什么是,在编译的代码中,静态变量b相对于指令指针(RIP)的地址,它不断变化(见行(1) 和(2)),从而生成更复杂的汇编代码,而不是相对于存储这些变量的可执行文件的特定部分?
根据上面提到的教程,有有这样一个部分:
这是因为
b的值被硬编码在不同的部分 示例可执行文件,它与所有 当进程运行时,操作系统加载程序的机器码 启动。
(强调我的。)
【问题讨论】:
-
这使其位置独立,这对于共享库和 ASLR 等非常有用。另请注意,没有“相对于可执行文件的特定部分”的寻址模式,甚至同一部分中的地址也可以是相对的(通常用于控制传输)。
-
因此会生成更复杂的汇编代码:不,它不会。使用
objdump -drwC -Mintel获得不错的输出。-r解码符号表。 objdump 始终为您计算,并显示 RIP 相关指令的实际目标地址以及与 RIP 的偏移量。 -
生成指令的大小很重要,它都需要来自 RAM 并缓存在处理器缓存中。内存是现代处理器的一个重要瓶颈。想象一下,如果访问内存的每条指令也需要 8 个字节来对地址进行编码,那么您的首选方案会如何工作。机器码是机器生成的,复杂的工作不介意。
-
@PeterCordes 您通常不会看到 C++ 编译器在运行时初始化静态分配的变量,因为您不会看到 C 编译器进行运行时初始化(即 C++ 初始化会在 C 中是允许的,因为 C 编译器通常不支持静态的运行时初始化)。这就是这里的情况,因为变量
b没有在函数中初始化。 -
@RossRidge:是的,我的评论变得一团糟,因为一旦我意识到在这种情况下这不是问题,我就没有从头开始重写它。一开始我在想,对于这样一个简单的功能来说,它看起来太多了,但当然那只是因为 OP 未能启用优化。我只注意到当我仔细观察并没有看到分支时,然后是 /facepalm,哦,是的,这只是一个带有常量初始化器的
int。
标签: c gcc assembly cpu-registers