【问题标题】:How should I get gcc to realign the stack pointer to a 16-byte boundary on the way in to a function?我应该如何让 gcc 在进入函数的过程中将堆栈指针重新对齐到 16 字节边界?
【发布时间】:2021-01-28 07:04:32
【问题描述】:

我正在尝试使用 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

在我期望的快速解决方法中,我想我会在进入由 JIT 代码调用的预编译函数并最终调用 Windows API 函数的途中让 gcc 强制重新对齐堆栈。

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

【问题讨论】:

  • 最终,我将通过将 JIT 中的堆栈分配修复为对齐 16——通过手动修补机器代码块——来消除对这种对齐修复的需求——但是我很好奇为什么这些 gcc 选项不像它说的那样工作。

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


【解决方案1】:

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

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

【讨论】:

  • 啊。我昨天在i386.c 的其他地方追逐force_align_arg_pointer 属性的使用,看到ix86_minimum_incoming_stack_boundary() (github.com/gcc-mirror/gcc/blob/…) 将incoming_stack_boundary 转储到最低限度,如果设置了该属性,但我在杂草中迷路了协调以下代码中的覆盖条件,而无需真正弄清楚这是否应该工作。
猜你喜欢
  • 2013-03-05
  • 2019-04-10
  • 2011-08-24
  • 2011-02-15
  • 2013-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多