2

所以我正在学习系统期中,并且类似课程的旧期中出现了这个问题,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 减去 3 次,调用 gcd(18, 15) 和 gcd(15, 3),它们结合起来我们应该从堆栈中减去 0x30 和 0x8。那么我们不应该在 0xffff0fc8 吗?然后,在我们返回之后,我们再次添加 0x4,这样 $esp 就是 0xffff0fcc,但不是之前?

4

1 回答 1

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。

如果 OTOH, esp= 0xffff1000 之前call 0x8048394 <gcd>; gcd(213, 18)在主程序中,那么上面的值减去返回地址的大小,4,你得到 0xffff0fcc。

我会说问题陈述(假设您没有改变其含义)有点模棱两可。gcd(213, 18)即使是在主程序中的调用,我也会计算参数的大小。但是还有另一个问题。主程序之前是否有任何堆栈对齐,gcd(213, 18)我也应该计算它吗?请注意,内部gcd()有堆栈对齐,因为该函数仅使用 8 个字节来递归传递参数,但分配了 16 个字节。

为了正确解决这个问题(=获得老师期望的数字),它必须明确说明代码中的确切位置esp= 0xffff1000 并确保包含该代码。

于 2012-10-18T09:09:31.760 回答