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

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

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用