gcc - 我应该如何让 gcc 在进入函数的过程中将堆栈指针重新对齐到 16 字节边界?

标签 gcc x86-64 mingw-w64 memory-alignment stack-pointer

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

当 JIT 回调预编译代码时,我遇到段错误,并且该代码调用 Windows API,因为正在使用 调用 Windows API 实现中的对齐移动指令,例如 movaps %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 文档:

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/

相关文章:

arrays - C++ clang 数组比 clang 向量和 gcc 向量和数组快得多

linux - 将 Linux x86-64 程序集 hello world 程序与 ld 链接失败

c - -mx32 GCcflags是否(正确)实现?

c++ - 交叉编译时包含外部库

C++、MinGW、Windows:使用 std::cout 打印数字非常慢

c++ - 为什么 MinGW-w64 浮点精度取决于 winpthreads 版本?

c++ - 抛出 C++ 异常时在调试器中中断程序

c++ - 返回一个 2 元组是否比 std::pair 效率低?

assembly - 为什么这些实模式代码在虚拟机中可以运行,但在我的真机上却不能运行?

c - 是否不允许对操作的左值进行类型转换?