我正在尝试使用 mingw64 让现有的 JIT 在 Windows x86_64 上运行。
当 JIT 回调预编译代码时,我遇到段错误,并且该代码调用 Windows API,因为正在使用 调用 Windows API 实现中的对齐移动指令,例如
不是 16 的倍数,即堆栈未与 16 字节边界对齐。movaps
%rsp
Thread 1 hit Catchpoint 2 (signal SIGSEGV), 0x00007fff5865142d in KERNELBASE!FindFirstFileA () from C:\WINDOWS\System32\KernelBase.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 文档:
On x86 targets, the
force_align_arg_pointer
attribute may be applied to individual function definitions, generating an alternate prologue and epilogue that realigns the run-time stack if necessary. This supports mixing legacy codes that run with a 4-byte aligned stack with modern codes that keep a 16-byte stack for SSE compatibility.
然而,将 __attribute__((force_align_arg_pointer))
添加到函数说明符对输出程序集没有影响。
我还尝试了 -mpreferred-stack-boundary=4
,它明确要求所有函数的 2**4 == 16
对齐:
-mpreferred-stack-boundary=num
Attempt to keep the stack boundary aligned to a 2 raised to num byte boundary.
这也没有效果。
事实上,我发现影响输出程序集的第一件事是 -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(和 Apple)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
(注意注释是如何引用属性的,但是代码显然不能那样工作)
关于gcc - 我应该如何让 gcc 在进入函数的过程中将堆栈指针重新对齐到 16 字节边界?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64343738/