我正在尝试让现有的JIT使用mingw64在Windows x86_64上运行。
当JIT调用回预编译代码时,我会得到segfault,而该代码调用Windows API,因为在Windows API实现中,调用对齐的移动指令(如movaps
(时,%rsp
不是16的倍数,即堆栈没有与16字节的边界对齐。
Thread 1 hit Catchpoint 2 (signal SIGSEGV), 0x00007fff5865142d in KERNELBASE!FindFirstFileA () from C:WINDOWSSystem32KernelBase.dll
1: x/i $pc
=> 0x7fff5865142d <KERNELBASE!FindFirstFileA+125>: movaps 0x60(%rsp),%xmm0
2: /x $rsp = 0xd8edd8
在我期望的快速解决方案中,我想我会让gcc在进入由JIT代码调用并最终调用Windows API函数的预编译函数的过程中强制重新调整堆栈。
force_align_arg_pointer
属性的gcc文档:
在x86目标上,可以应用
force_align_arg_pointer
属性到各个函数定义,生成备用序言以及在必要时重新调整运行时堆栈的后记。这支持将使用4字节对齐堆栈运行的遗留代码与为SSE兼容性保留16字节堆栈的现代代码。
但是,将__attribute__((force_align_arg_pointer))
添加到函数说明符对输出程序集没有影响。
我还尝试了-mpreferred-stack-boundary=4
,它明确要求对所有函数进行2**4 == 16
对齐:
-mpreferred-stack-boundary=num
尝试将堆栈边界与提升到num字节的2对齐边界
这也没有任何效果。
事实上,我发现影响输出程序集的第一件事是-mpreferred-stack-boundary=3
(它应该使堆栈与8字节的边界对齐(。
这导致了这种差异:
@@ -46,8 +59,15 @@
.def foo; .scl 2; .type 32; .endef
.seh_proc foo
foo:
+ pushq %rbp
+ .seh_pushreg %rbp
+ movq %rsp, %rbp
+ .seh_setframe %rbp, 0
+ andq $-16, %rsp
.seh_endprologue
leaq .LC0(%rip), %rcx
+ movq %rbp, %rsp
+ popq %rbp
jmp printf
.seh_endproc
.def __main; .scl 2; .type 32; .endef
奇怪的是,这实际上是在放入andq $-16, %rsp
(将堆栈指针对齐为16的倍数(,尽管我们说过更喜欢8字节对齐。
我对这些选项或它们所涉及的案例有什么误解?
gcc的版本是MSYS2 mingw64的10.2.0:
$ gcc --version
gcc.exe (Rev4, Built by MSYS2 project) 10.2.0
正确的解决方法是-mincoming-stack-boundary=3
:您应该告诉编译器,它编译的函数可以用未对齐的堆栈调用(因此,"传入"而不是"首选":您不需要将首选对齐提高到默认对齐以上(。
至于为什么该属性不起作用,您似乎发现了一个特定于64位Microsoft ABI的编译器后端错误。该属性在以Linux为目标时可以正常工作,但后端有一些针对Microsoft(和苹果(ABI的特殊外壳,代码可能与预期行为不一致:
6089 /* 64-bit MS ABI seem to require stack alignment to be always 16,
6090 except for function prologues, leaf functions and when the defult
6091 incoming stack boundary is overriden at command line or via
6092 force_align_arg_pointer attribute.
6093
6094 Darwin's ABI specifies 128b alignment for both 32 and 64 bit variants
6095 at call sites, including profile function calls.
6096 */
6097 if (((TARGET_64BIT_MS_ABI || TARGET_MACHO)
6098 && crtl->preferred_stack_boundary < 128)
6099 && (!crtl->is_leaf || cfun->calls_alloca != 0
6100 || ix86_current_function_calls_tls_descriptor
6101 || (TARGET_MACHO && crtl->profile)
6102 || ix86_incoming_stack_boundary < 128))
6103 {
6104 crtl->preferred_stack_boundary = 128;
6105 crtl->stack_alignment_needed = 128;
6106 }
6107
(注意注释是如何引用属性的,但代码显然不是这样工作的(