使用-O2 vs -O1时,Cygwin64 gcc C /汇编程序崩溃

时间:2015-09-11 23:18:25

标签: c gcc cygwin

我有以下代码(两个文件):

的main.c

text

lib.s

resubmit

预期输出

#include <stdio.h>

void print();
void asm_print();

int main() {
    asm_print();
    printf("done\n");

    return 0;
}

void print() {
    printf("printing with number: %d\n", 1);
    // printf("printing without number\n");
}

如果我使用gcc4.9.3和命令行在linux上编译:

    .intel_syntax noprefix
    .text

    .globl asm_print
asm_print:
    push    rbp
    mov     rbp, rsp
    call    print
    mov     rsp, rbp
    pop     rbp
    ret
一切正常。如果我使用-O1或-O3,这也有效。如果我使用gcc4.9.3和命令行编译cygwin64:

printing with number: 1
done
一切正常。

但是,如果我将上面的内容更改为使用-O2或-O3,则只会生成第一行输出。如果在函数print()中注释掉第一行并取消注释第二行,我得到预期的输出:

gcc -O2 -m64 -Wall -Wextra -Werror -std=gnu99 main.c lib.s

无论我使用的优化量如何。任何人都可以建议代码有什么问题,这样无论cygwin64上使用的优化量如何,我都能获得预期的输出?

1 个答案:

答案 0 :(得分:1)

问题是Windows 64位ABI与32位ABI不同,并且要求调用者在堆栈上分配32字节的临时参数(home)空间以供被调用者使用。

http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/ https://msdn.microsoft.com/en-us/library/tawsa7cb.aspx

所以你需要做的是在调用之前将堆栈减少至少32。另外,x64需要将堆栈指针保持在16个字节的倍数上。 64位返回地址是8个字节,因此您实际需要将rsp移动40,56等

asm_print:
    push    rbp
    mov     rbp, rsp
    sub     rsp, 40
    call    print
    add     rsp, 40
    pop     rbp
    ret

据推测,当你用一个字符串常量调用print / printf时,它实际上并没有使用任何临时空间,所以没有什么不好的事情发生。另一方面,当你使用%d格式时,它需要参数空间,并在堆栈中破坏你保存的寄存器。

禁用优化的原因是print函数不使用home空间,并在调用printf时分配参数空间。如果使用-O2,编译器会进行尾调用消除,并用“jmp printf”替换“call printf”指令。这实质上导致重新使用应该由asm_print分配的参数空间。