32 位 x86 汇编中堆栈对齐的职责

2024-11-04 08:43:00
admin
原创
160
摘要:问题描述:我试图弄清楚谁(调用者或被调用者)负责堆栈对齐。64 位汇编的情况相当清楚,它由调用者负责。参照 System V AMD64 ABI,第 3.2.2 节“堆栈框架”:输入参数区域的末尾应与 16(如果 __m256 在堆栈上传递,则为 32)字节边界对齐。换句话说,可以安全地假设,对于被调用函数的...

问题描述:

我试图弄清楚谁(调用者或被调用者)负责堆栈对齐。64 位汇编的情况相当清楚,它由调用者负责。

参照 System V AMD64 ABI,第 3.2.2 节“堆栈框架”

输入参数区域的末尾应与 16(如果 __m256 在堆栈上传递,则为 32)字节边界对齐。

换句话说,可以安全地假设,对于被调用函数的每个入口点:

16 | (%rsp + 8)

保持(额外的八个是因为call隐式地将返回地址推送到堆栈上)。


在 32 位世界中看起来如何(假设是 cdecl)?我注意到gcc将对齐放在调用函数内部,其结构如下:

and esp, -16

这似乎表明,这是被调用者的责任。

为了更清楚起见,请考虑以下 NASM 代码:

global main
extern printf
extern scanf
section .rodata
    s_fmt   db "%d %d", 0
    s_res   db `%d with remainder %d
`, 0
section .text
main:
    start   0, 0
    sub     esp, 8
    mov     DWORD [ebp-4], 0 ; dividend
    mov     DWORD [ebp-8], 0 ; divisor

    lea     eax, [ebp-8]
    push    eax
    lea     eax, [ebp-4]
    push    eax
    push    s_fmt
    call    scanf
    add     esp, 12

    mov     eax, [ebp-4]
    cdq
    idiv    DWORD [ebp-8]

    push    edx
    push    eax
    push    s_res
    call    printf

    xor     eax, eax
    leave
    ret

在调用之前是否需要对齐堆栈?如果是这样,那么在将这两个参数推送到之前,scanf需要减少四个字节:%esp`scanf`

4 bytes (return address)
4 bytes (%ebp of previous stack frame)
8 bytes (for two variables)
12 bytes (three arguments for scanf)
= 28

解决方案 1:

GCC在 中进行额外的堆栈对齐main;该函数很特殊。 如果您查看任何其他函数的代码生成,您将看不到它,除非您有本地的 withalignas(32)或类似的东西。

GCC 只是采取了一种防御性的方法-m32,不假设main使用正确 16B 对齐的堆栈调用。或者这种特殊处理是从-mpreferred-stack-boundary=4一个好主意而不是法律1遗留下来的。

多年来,i386 System V ABI 一直保证/要求 ESP+4 在进入函数时是 16B 对齐的。(即,ESP在 CALL 指令之前必须是 16B 对齐的,因此堆栈上的参数从 16B 边界开始。这与 x86-64 System V 相同。) ESP % 16 == 0在调用之前、ESP % 16 == 12在函数进入时、在调用之后。

ABI 还保证新的 32 位进程以在 16B 边界上对齐的 ESP 启动(例如_start,在 ELF 入口点,其中 ESP 指向 argc,而不是返回地址),并且 glibc CRT 代码保持该对齐。

就调用约定而言,EBP 只是另一个调用保留寄存器。但是,是的,编译器输出-fno-omit-frame-pointer确实会在其他调用保留寄存器(如 EBX)之前进行处理push ebp,因此保存的 EBP 值会形成一个链接列表。(因为它还负责mov ebp, esp在推送之后设置帧指针。)


也许 gcc 是防御性的,因为非常古老的 Linux 内核(从 i386 ABI 修订版之前开始,当时所需的对齐只有 4B)可能会违反该假设,并且它只是在进程的生命周期中运行一次的额外几个指令(假设程序不递归main调用)。


与 gcc 不同,clang 假定堆栈在进入主函数时正确对齐。(clang 还假定窄参数已被符号或零扩展为 32 位,即使当前 ABI 修订版尚未指定该行为。gcc 和 clang 都发出在调用方执行的代码,但只有 clang 在被调用方依赖于它。这发生在 64 位代码中,但我没有检查 32 位。)

如果您好奇的话,请查看http://gcc.godbolt.org/上的 main 函数和除 main 之外的其他函数的编译器输出。


我刚刚更新了x86标签 wiki。http: //x86-64.org/仍然处于死机状态并且似乎不会回来,因此我更新了 System V 链接以指向 HJ Lu 的 github repo 中当前修订版的 PDF,以及他的带有链接的页面。

请注意,SCO 网站上的最新版本不是当前修订版,并且不包括 16B 堆栈对齐要求。


ABI 从 4 字节对齐变为 16 字节对齐的历史

脚注 1:向 i386 SysV ABI 添加 16 字节对齐要求纯属意外;GCC 出于性能原因维持了 16 字节对齐(例如,8 字节double永远不会跨越缓存行边界)。

另请参阅我的答案底部关于为什么 x86-64 / AMD64 System V ABI 要求 16 字节堆栈对齐?的部分以获取更多详细信息。

在某些 GCC 版本中,SSE/SSE2 代码生成开始使用movaps溢出/重新加载__m128变量到堆栈,而无需手动对齐传入的 ESP。这使调整选择成为一项要求,但直到带有此类代码的库在一些长期稳定的 Linux 发行版中广泛部署时才被发现。

面对这一选择,GCC 开发人员/ABI 维护人员选择了最不坏的路径,将其作为官方要求。这破坏了调用其他函数的现有手写 asm。

请参阅https://sourceforge.net/p/fbc/bugs/659/了解一些历史记录,以及我在https://gcc.gnu.org/bugzilla/show_bug.cgi?id=40838#c91上的评论,尝试总结 i386 GNU/Linux + GCC 意外陷入这样一种境地的不幸历史:对 i386 System V ABI 进行向后不兼容的更改是两害相权取其轻。


大多数 BSD 版本和 i386 MacOS 未采用此 ABI 更改,并且仍然不需要16字节堆栈对齐。GCC 可能默认为-mpreferred-stack-boundary=4这些目标,但代码生成alignas(16) char buf[16];(或__m128从 regs 溢出的本地变量)需要手动对齐函数内的 ESP,以防它一开始就不是。

因此,从 4 字节对齐到 16 字节对齐的改变实际上针对的是 Linux,而不是其他操作系统。这可能是简化 GCC 源代码并始终为main32 位目标包含额外堆栈对齐代码的另一个原因。目前,Linux 的 32 位 x86 已经过时,现在不值得改变。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1120  
  IPD(Integrated Product Development,集成产品开发)流程是一种广泛应用于高科技和制造业的产品开发方法论。它通过跨职能团队的紧密协作,将产品开发周期缩短,同时提高产品质量和市场成功率。在IPD流程中,CDCP(Concept Decision Checkpoint,概念决策检查点)是一个关...
IPD培训课程   75  
  研发IPD(集成产品开发)流程作为一种系统化的产品开发方法,已经在许多行业中得到广泛应用。它不仅能够提升产品开发的效率和质量,还能够通过优化流程和资源分配,显著提高客户满意度。客户满意度是企业长期成功的关键因素之一,而IPD流程通过其独特的结构和机制,能够确保产品从概念到市场交付的每个环节都围绕客户需求展开。本文将深入...
IPD流程   66  
  IPD(Integrated Product Development,集成产品开发)流程是一种以跨职能团队协作为核心的产品开发方法,旨在通过优化资源分配、提高沟通效率以及减少返工,从而缩短项目周期并提升产品质量。随着企业对产品上市速度的要求越来越高,IPD流程的应用价值愈发凸显。通过整合产品开发过程中的各个环节,IPD...
IPD项目管理咨询   76  
  跨部门沟通是企业运营中不可或缺的一环,尤其在复杂的产品开发过程中,不同部门之间的协作效率直接影响项目的成败。集成产品开发(IPD)作为一种系统化的项目管理方法,旨在通过优化流程和增强团队协作来提升产品开发的效率和质量。然而,跨部门沟通的复杂性往往成为IPD实施中的一大挑战。部门之间的目标差异、信息不对称以及沟通渠道不畅...
IPD是什么意思   70  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用