所以我正在学习系统期中考试,并且类似课程的旧期中考试有这个问题,C和汇编代码如下:
int gcd(int a, int b)
{
if(!b)
{
return a;
}
return gcd(b, a%b);
}
0x08048394 <+0>: push %ebp
0x08048395 <+1>: mov %esp,%ebp
0x08048397 <+3>: sub $0x10,%esp
0x0804839a <+6>: mov 0x8(%ebp),%eax
0x0804839d <+9>: mov 0xc(%ebp),%ecx
0x080483a0 <+12>: test %ecx,%ecx
0x080483a2 <+14>: je 0x80483b7 <gcd+35>
0x080483a4 <+16>: mov %eax,%edx
0x080483a6 <+18>: sar $0x1f,%edx
0x080483a9 <+21>: idiv %ecx
0x080483ab <+23>: mov %edx,0x4(%esp)
0x080483af <+27>: mov %ecx,(%esp)
0x080483b2 <+30>: call 0x8048394 <gcd>
0x080483b7 <+35>: leave
0x080483b8 <+36>: ret
他们告诉我们%esp的起始值是0xffff1000,并告诉我们gcd(213,18)将导致:gcd(213,18),gcd(18,15),gcd(15,3)和gcd(3,0)。然后他们在执行gcd(15,3)的返回指令之前询问%esp的值是什么。
解决方案说它是0xffff0fcc。我不太明白为什么。这是我的理由:
我们减去了0x10三次,我们调用了gcd(18,15)和gcd(15,3),它们组合起来我们应该从堆栈中减去0x30和0x8。那么我们不应该在0xffff0fc8吗?然后,在我们返回之后,我们再次添加0x4,以便$ esp是0xffff0fcc,但不是之前?
答案 0 :(得分:1)
如果他们在此行之前表示esp
= 0xffff1000:
0x08048394 <+0>: push %ebp
然后从esp
减去
4: 0x08048394 <+0>: push %ebp
16: 0x08048397 <+3>: sub $0x10,%esp
4: 0x080483b2 <+30>: call 0x8048394 <gcd>; gcd(18, 15)
然后:
4: 0x08048394 <+0>: push %ebp
16: 0x08048397 <+3>: sub $0x10,%esp
4: 0x080483b2 <+30>: call 0x8048394 <gcd>; gcd(15, 3)
然后:
0: because "leave" undoes "sub $0x10,%esp" and "push %ebp"
你得到0xffff1000 - 2 *(4 + 16 + 4)= 0xffff0fd0。
如果主程序中的esp
之前的OTOH,call 0x8048394 <gcd>; gcd(213, 18)
= 0xffff1000,那么上面的值会减少返回地址的大小4,并且你得到0xffff0fcc。
我会说问题陈述(假设你没有改变它的含义)有点含糊不清。即使是主程序中的调用gcd(213, 18)
,我也会计算参数的大小。但接下来还有另外一个问题。在gcd(213, 18)
之前主程序是否执行了堆栈对齐,我是否应该对它进行计数?请注意,gcd()
内部存在堆栈对齐,因为函数仅使用8个字节递归传递参数,但分配16。
要正确解决此问题(=获取教师期望的数字),必须明确说明代码esp
= 0xffff1000中的哪一点,并确保包含该代码。