3

我正在尝试使用 mingw64 让现有的 JIT 在 Windows x86_64 上运行。

当 JIT 回调到预编译代码并且该代码调用 Windows API 时,我遇到了段错误,因为movaps在调用 Windows API 实现中的对齐移动指令时,调用的%rsp不是 16 的倍数,即堆栈未对齐一个 16 字节的边界。

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 文档:

在 x86 目标上,该force_align_arg_pointer属性可以应用于单个函数定义,生成备用序言和尾声,必要时重新对齐运行时堆栈。这支持将使用 4 字节对齐堆栈运行的旧代码与保留 16 字节堆栈以实现 SSE 兼容性的现代代码混合。

但是,添加__attribute__((force_align_arg_pointer))到函数说明符对输出程序集没有影响。

我也试过-mpreferred-stack-boundary=4,它明确要求2**4 == 16所有功能对齐:

-mpreferred-stack-boundary=num 尝试保持堆栈边界与 2 提升到num字节边界对齐。

这也没有效果。

事实上,我发现影响输出程序集的第一件事是-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
4

1 回答 1

3

正确的解决方法是-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

(注意注释是如何引用属性的,但代码显然不是这样工作的)

于 2020-10-14T08:02:08.103 回答