x86-64 汇编器中的无限循环
Posted
技术标签:
【中文标题】x86-64 汇编器中的无限循环【英文标题】:An infinite loop in x86-64 assembler 【发布时间】:2013-11-07 17:27:50 【问题描述】:#include <stdio.h>
#include <math.h>
int main(int argc, const char *argv[])
long i, max;
long sum = 0;
max = (long)pow(2,32);
for (i = 0; i < max; i++)
sum += i;
printf("%ld\n", sum);
return 0;
$gcc -S main.c
问题是:在下面的.L2
代码中,-8(%rbp)
始终等于零,而%rax
始终大于零。所以这是一个无限循环?如果我用gcc -S -O1 main.c
编译,那就很清楚了。我真的很困扰!
只是汇编代码的一小部分:
main:
pushq %rbp
movq %rsp, %rbp
subq $48, %rsp
movl %edi, -36(%rbp)
movq %rsi, -48(%rbp)
movq $0, -16(%rbp)
movl $0, -8(%rbp)
movl $2, -4(%rbp)
movq $0, -24(%rbp)
jmp .L2
.L3:
movq -24(%rbp), %rax
addq %rax, -16(%rbp)
addq $1, -24(%rbp)
.L2:
movq -24(%rbp), %rax
cmpq -8(%rbp), %rax
jl .L3
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
【问题讨论】:
long
的大小是多少?如果它是 32 位(4 字节),那么 pow(2, 32)
将溢出。如果您想要 long
的最大值,那么您应该在 C++ 中使用 std::numeric_limits
,并在 C 中使用 <limits.h>
头文件中的 LONG_MAX
。
-8(%rbp)
是 max
的值,因此它显然不应该为零。也许你可以展示更多它被设置的汇编器。
当您检查它是否为零时,您从 -8(%rbp) 读取了多少字节?它的值应该是0x100000000
,它的低 32 位为零。
@JoachimPileborg Pileborg 很抱歉没有明确说明,我使用 ubuntu 12.04 x86_64。所以long
的大小是8字节,那么pow(2,32)就不会溢出了。
@mbratch 但-8(%rbp)
被初始化为零。 cmpq -8(%rbx), %rax
的第一次是真的,然后总是真的。
【参考方案1】:
真正的循环计数器 (i
) 位于 -24(%rbp)
。在第三行,它增加了。在第 4 行,它被加载到rax
。所以rax
不是一个常数零,它与i
一起贯穿值。
-8(%rbp)
,一个假设是max
所在的位置。所以i
的值与那个值进行比较,这就是你的循环退出条件。 -8(%rbp)
不应为零。如果是的话,我会闻到流氓 32 位算法的味道。
编辑:我想我知道是怎么回事。常量 2 和 32 是 int
,而不是 long
,因此假定为 32 位。 int
的大小取决于平台;甚至 GCC 的约定也可能会有所不同。 pow(int, int)
被实现为内在的。当参数为 32 位时,2^32 为 0。
替换
max = (long)pow(2,32);
与
max = pow(2l, 32l);
或者使用常量更好:
max = 0x100000000l;
正如我和其他人所怀疑的那样,混合中存在 32 位。
【讨论】:
【参考方案2】:不,这不是无限循环。首先,%rax 并不总是大于 0,在该行中,它从 -24(%rbp) 中获取值,这显然是变量 i
。当它进入循环时,它将 -24(%rbp) 设置为零,然后跳转到 .L2 它发生的部分原因是您没有显示。如果 max
的变量值 -8(%rbp) 等于零(在溢出的情况下)jl
将不会跳转到 .L3 并且循环将在第一次检查后终止。
我不太明白为什么你需要为此阅读汇编,这从 C++ 源代码中很明显。
【讨论】:
另外,通过运行代码,我们可以看到它结束了。即使只是一些额外的printf()
语句也必须有所帮助。
不,-8(%rbx)
被初始化为零,在此之上我添加了额外的代码。并且cmpq -8(%rbx), %rax
的第一次为真,然后始终为真,除非%rax
会溢出。
是的,我就是想看看用-O1
编译的代码与不编译的汇编代码的区别。因为我发现用-O1
编译的代码比平时花的时间少。跨度>
@xuefu 正如我之前所说,-24(%rbp)
被初始化为 0。然后它跳转到 .L2
,然后比较 -8(%rbp)
和 -24(%rbp)
,因为它们都等于 0 jl
将不跳转到 .L3 所以循环退出。哪一部分你不明白?以上是关于x86-64 汇编器中的无限循环的主要内容,如果未能解决你的问题,请参考以下文章
为什么用cycle()创建的迭代器中的可变值没有更新,即使有停止条件,也会导致无限循环?