【问题标题】:Int vs Double in assembly汇编中的 Int 与 Double
【发布时间】:2017-04-29 17:59:16
【问题描述】:

为什么 GCC 编译器在使用 double 时添加这三行而在使用 Int 时不添加?


带整数:

#include <cstdio>

int main(){
    int i = 1;
}

==>

main:
        push    ebp
        mov     ebp, esp
        sub     esp, 16
        mov     DWORD PTR [ebp-4], 1
        mov     eax, 0
        leave
        ret

双倍:

#include <cstdio>

int main(){
    double i = 1;
}

==>

main:
        lea     ecx, [esp+4]            // This three lines
        and     esp, -8                 //  ...
        push    DWORD PTR [ecx-4]       //  ...
        push    ebp
        mov     ebp, esp
        push    ecx
        sub     esp, 20
        fld1
        fstp    QWORD PTR [ebp-16]
        mov     eax, 0
        add     esp, 20
        pop     ecx
        pop     ebp
        lea     esp, [ecx-4]
        ret

使用指针时也会发生类似的情况,例如 int *s = new int(4);

您能解释一下为什么会发生这种情况,为什么不总是这样吗?

【问题讨论】:

  • 如果您不使用-O3 开关,编译器生成的代码可能是多余的或无意义的(尽管仍然正确)
  • 是的,我知道。我只是在学习:)
  • 这是 C 还是 C++?它们是不同的语言,可能会为函数进入/退出生成不同的代码。
  • @MargaretBloom:不是废话。只是多余的。但通常更容易被初学者理解并且更容易调试。不过,通常最好使用-Og 进行编译以进行调试。

标签: c++ c gcc x86 reverse-engineering


【解决方案1】:

如果double 在自动范围内(在堆栈上),额外的代码将堆栈帧对齐在偶数 8 字节边界,以便double 变量存储在具有适当对齐方式的内存地址.堆栈指针在进入函数时,不能保证在偶数 8 字节边界上对齐,编译器会添加额外的代码来做到这一点。

and esp, -8 就是这样做的。 -8 是 0xFFFFFFF8。这会清除 esp 的最后 3 位,使用 and,使其指向偶数 8 字节的边界内存地址,向下调整(堆栈从高内存地址增长到低内存地址)。

【讨论】:

  • 当然,这就是代码的作用。但是为什么它会为double 而不是int? (严肃的问题,不是拖钓:我已经很久没有研究代码生成问题了……)
  • 在 x86 上,double 是 64 位类型,需要 8 字节对齐,但 ABI 不要求堆栈是 8 字节对齐。 long long 也会发生同样的事情,顺便说一句,因为它也是 8 字节类型。 int 不需要,因为它只是一个 4 字节的类型,并且堆栈指针保证是 4 字节对齐的。
猜你喜欢
  • 2011-08-25
  • 1970-01-01
  • 1970-01-01
  • 2010-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-20
  • 2018-09-27
相关资源
最近更新 更多