堆栈纪律集会

时间:2012-10-18 08:38:56

标签: assembly stack

所以我正在学习系统期中考试,并且类似课程的旧期中考试有这个问题,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,但不是之前?

1 个答案:

答案 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中的哪一点,并确保包含该代码。