繁体   English   中英

使用-O2与-O1时Cygwin64 gcc C /汇编器崩溃

[英]Cygwin64 gcc C/assembler crash when using -O2 vs -O1

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

main.c中

#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");
}

lib.s

    .intel_syntax noprefix
    .text

    .globl asm_print
asm_print:
    push    rbp
    mov     rbp, rsp
    call    print
    mov     rsp, rbp
    pop     rbp
    ret

预期产量

printing with number: 1
done

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

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

一切正常。 如果我使用–O1或–O3,这也适用。 如果我使用gcc4.9.3和命令行在cygwin64上编译:

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

一切正常。

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

printing without number
done

不管我使用了多少优化。 任何人都可以建议代码出什么问题了,无论我在cygwin64上使用了多少优化,我都能得到预期的输出?

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

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函数不使用宿主空间,而是在调用printf时分配参数空间。 如果使用-O2,则编译器会执行尾部调用消除操作,并将“ call printf”指令替换为“ jmp printf”。 这实质上导致重新使用原本由asm_print分配的参数空间。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM