我只是在学习汇编中的函数和堆栈框架等等,所以我一直在查看 gdb 中的堆栈框架,因为我运行了一个递归算法来看看会发生什么。
如果我在 C 中运行一些递归代码,堆栈看起来就像我期望的那样 - 每次调用函数时堆栈上的一个对象。在递归阶乘函数的最低递归级别,堆栈帧如下所示:(这是 gdb 中的回溯,在函数的第一行有一个断点。)
(gdb) bt
#0 factorial (n=1) at recursion.c:20
#1 0x00005555555551c7 in factorial (n=2) at recursion.c:21
#2 0x00005555555551c7 in factorial (n=3) at recursion.c:21
#3 0x00005555555551c7 in factorial (n=4) at recursion.c:21
#4 0x00005555555551c7 in factorial (n=5) at recursion.c:21
#5 0x00005555555551c7 in factorial (n=6) at recursion.c:21
#6 0x00005555555551c7 in factorial (n=7) at recursion.c:21
#7 0x00005555555551c7 in factorial (n=8) at recursion.c:21
#8 0x00005555555551c7 in factorial (n=9) at recursion.c:21
#9 0x00005555555551c7 in factorial (n=10) at recursion.c:21
#10 0x000055555555517f in main (argc=2, args=0x7fffffffe768) at recursion.c:13
我的 C 代码是这样的:
int factorial (int n)
{
if (n <= 1) return 1;
return n * factorial(n-1);
}
现在我在汇编中做同样的事情(我已经从 Rey Seyfarth 的书“64 位汇编编程简介”中复制了这段代码,所以我假设它是正确的)并且,无论递归的深度如何,堆栈帧看起来像这样:(第 50 行是call fact
)。
(gdb) bt
#0 fact () at fact.asm:40
#1 0x00000000004011a8 in greater () at fact.asm:50
#2 0x0000000000000000 in ?? ()
阶乘函数的代码是这样的 - 在这种情况下,断点位于以下sub rsp, 16
行:
fact: ; recursive function
n equ 8
push rbp
mov rbp, rsp
sub rsp, 16 ; make room for n
cmp rdi, 1 ; end recursion if n=1
jg greater
mov eax, 1
leave
ret
greater:
mov [rsp+n], rdi ; save n
dec rdi ; call fact with n-1
call fact
mov rdi, [rsp+n] ; restore original n
imul rax, rdi
leave
ret
事实上,在这种情况下,回溯的输出真的让我很困惑。如果我在调用事实函数 ( ) 之前将断点放在行上,dec rdi
那么结果通常是这样的:
(gdb) bt
#0 greater () at fact.asm:49
#1 0x0000000000000000 in ?? ()
但事实上第五次调用是这样的:
(gdb) bt
#0 greater () at fact.asm:49
#1 0x00007ffff7f94be0 in ?? () from /usr/lib/libc.so.6
#2 0x0000000000000006 in ?? ()
#3 0x00007fffffffe5f0 in ?? ()
#4 0x00000000004011a8 in greater () at fact.asm:50
#5 0x0000000000000000 in ?? ()
然后在第七次通话中,这个:
(gdb) bt
#0 greater () at fact.asm:49
#1 0x0000003000000008 in ?? ()
#2 0x0000000000000004 in ?? ()
#3 0x00007fffffffe5b0 in ?? ()
#4 0x00000000004011a8 in greater () at fact.asm:50
#5 0x0000000000000000 in ?? ()
我的问题:
为什么堆栈的行为与 C 中的不同?
为什么我偶尔会得到最后一个看似垃圾的输出?
谢谢!