【问题标题】:Stack discipline assembly堆栈纪律大会
【发布时间】:2012-10-18 08:38:55
【问题描述】:

所以我在学习一个系统期中,一个类似课程的旧期中出现了这个问题,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,但不是之前?

【问题讨论】:

    标签: assembly stack


    【解决方案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 &lt;gcd&gt;; gcd(213, 18) 之前,那么上面的值减去返回地址的大小,4,得到 0xffff0fcc。

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

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

    【讨论】:

    • 是的,我写错了,也没有看清楚。确切的措辞是“同样想象在调用之前,%esp 的值是 0xffff1000,也就是说,0xffff1000 是 %esp 在调用指令执行之前的值。”所以基本上,我完全错了。所以我们为 gcd(213, 18) 减去 24,然后为 gcd(18, 15) 再次减去 24。但是,我不明白如果我们还没有执行离开或返回,为什么它会减少 4? Fwiw 这是问题 5,b 部分在这里cs.cmu.edu/afs/cs/academic/class/15213-f11/www/oldexams/…
    • call 将返回地址压入堆栈。 ret 将其从那里删除。
    • 然后你说They then ask what the value of %esp is before the execution of the return instruction for gcd(15, 3).,所以如果你在retleave应该已经执行了。
    • 好的,谢谢。我想我需要开始更仔细地阅读说明和汇编代码。
    猜你喜欢
    • 2018-02-22
    • 2023-04-10
    • 2012-06-26
    • 2013-10-21
    • 1970-01-01
    • 1970-01-01
    • 2010-11-24
    • 2021-10-11
    • 2021-03-26
    相关资源
    最近更新 更多