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

2024-11-04 08:43:00
admin
原创
40
摘要:问题描述:我试图弄清楚谁(调用者或被调用者)负责堆栈对齐。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 已经过时,现在不值得改变。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   601  
  华为IPD与传统研发模式的8大差异在快速变化的商业环境中,产品研发模式的选择直接决定了企业的市场响应速度和竞争力。华为作为全球领先的通信技术解决方案供应商,其成功在很大程度上得益于对产品研发模式的持续创新。华为引入并深度定制的集成产品开发(IPD)体系,相较于传统的研发模式,展现出了显著的差异和优势。本文将详细探讨华为...
IPD流程是谁发明的   7  
  如何通过IPD流程缩短产品上市时间?在快速变化的市场环境中,产品上市时间成为企业竞争力的关键因素之一。集成产品开发(IPD, Integrated Product Development)作为一种先进的产品研发管理方法,通过其结构化的流程设计和跨部门协作机制,显著缩短了产品上市时间,提高了市场响应速度。本文将深入探讨如...
华为IPD流程   9  
  在项目管理领域,IPD(Integrated Product Development,集成产品开发)流程图是连接创意、设计与市场成功的桥梁。它不仅是一个视觉工具,更是一种战略思维方式的体现,帮助团队高效协同,确保产品按时、按质、按量推向市场。尽管IPD流程图可能初看之下显得错综复杂,但只需掌握几个关键点,你便能轻松驾驭...
IPD开发流程管理   8  
  在项目管理领域,集成产品开发(IPD)流程被视为提升产品上市速度、增强团队协作与创新能力的重要工具。然而,尽管IPD流程拥有诸多优势,其实施过程中仍可能遭遇多种挑战,导致项目失败。本文旨在深入探讨八个常见的IPD流程失败原因,并提出相应的解决方法,以帮助项目管理者规避风险,确保项目成功。缺乏明确的项目目标与战略对齐IP...
IPD流程图   8  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用