我有以下代码(两个文件):
的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上使用的优化量如何,我都能获得预期的输出?
答案 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分配的参数空间。