Cygwin64 gcc C/assembler 使用 -O2 与 -O1 时崩溃
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 上使用多少优化,我都能得到预期的输出?
问题是windows64位ABI与32位ABI不同,需要调用者在栈上分配32字节的scratch参数(home)space被调用者使用。
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位return地址是8个字节,所以实际需要将rsp移动40、56等
asm_print:
push rbp
mov rbp, rsp
sub rsp, 40
call print
add rsp, 40
pop rbp
ret
据推测,当您仅使用一个字符串常量调用 print / printf 时,它实际上并没有使用任何划痕 space,所以没有什么不好的事情发生。另一方面,当您使用 %d 格式时,它需要参数 space,并破坏您在堆栈上保存的寄存器。
它在禁用优化的情况下工作的原因是打印函数不使用 home space,并在调用 printf 时分配参数 space。如果使用 -O2,编译器会执行尾调用消除并用 "jmp printf" 替换 "call printf" 指令。这实质上导致重新使用本应由 asm_print.
分配的参数 space
我有以下代码(两个文件):
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 上使用多少优化,我都能得到预期的输出?
问题是windows64位ABI与32位ABI不同,需要调用者在栈上分配32字节的scratch参数(home)space被调用者使用。
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位return地址是8个字节,所以实际需要将rsp移动40、56等
asm_print:
push rbp
mov rbp, rsp
sub rsp, 40
call print
add rsp, 40
pop rbp
ret
据推测,当您仅使用一个字符串常量调用 print / printf 时,它实际上并没有使用任何划痕 space,所以没有什么不好的事情发生。另一方面,当您使用 %d 格式时,它需要参数 space,并破坏您在堆栈上保存的寄存器。
它在禁用优化的情况下工作的原因是打印函数不使用 home space,并在调用 printf 时分配参数 space。如果使用 -O2,编译器会执行尾调用消除并用 "jmp printf" 替换 "call printf" 指令。这实质上导致重新使用本应由 asm_print.
分配的参数 space