当前位置: 首页 > 知识库问答 >
问题:

32位x86程序集中堆栈对齐的责任

岳曦
2023-03-14

我试图清楚地了解谁(调用者或被调用者)负责堆栈对齐。64位程序集的情况相当清楚,它是由调用者完成的。

参考System V AMD64 ABI,第3.2.2节堆栈框架

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

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

16 |(%rsp 8)

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

它在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\n`, 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个答案

岳晟
2023-03-14
匿名用户

GCC只在main中进行额外的堆栈对齐;这个功能很特别。如果您查看任何其他函数的code gen,您将看不到它,除非您有一个带有alignas(32)的局部函数。

GCC只是对m32采取了一种防御方法,没有假设使用正确的16B对齐堆栈调用主。或者这种特殊处理是在堆栈边界=4时遗留下来的,这只是一个好主意,而不是定律。

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

ABI还保证新的32位进程从16B边界上对齐的ESP开始(例如,在ELF入口点,ESP指向argc,而不是返回地址),glibc CRT代码保持这种对齐。

就呼叫约定而言,EBP只是另一个保留呼叫的寄存器。但是可以肯定的是,编译器输出不省略帧指针时,确实会将ebp推到其他调用保留寄存器(如EBX)之前,因此保存的ebp值形成一个链表。(因为它还执行mov ebp、esp在推送后设置帧指针的部分。)

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

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

查看上的编译器输出http://gcc.godbolt.org/对于main和函数,如果您好奇的话,可以使用main以外的函数。

前几天我刚刚更新了x86标签wiki中的ABI链接。http://x86-64.org/还是死了,似乎不会回来了,所以我更新了System V链接,指向HJ Lu的github repo中当前版本的PDF文件,以及他的带有链接的页面。

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

我认为某些BSD版本仍然不需要/维护16字节堆栈对齐。

 类似资料:
  • 问题内容: 我试图清楚地了解谁(调用方或被调用方)负责堆栈对齐。64位汇编的情况很清楚,它是由 caller进行的 。 参考系统V AMD64 ABI,第3.2.2节 堆栈框架 : 输入参数区域的末尾应在16(如果在堆栈上通过__m256,则为32)字节边界对齐。 换句话说, 应该 安全地假设,对于被调用函数的每个入口点: 保持(额外的八个是因为隐式将返回地址压入堆栈)。 在32位世界中的外观(假

  • 我在不同的地方读到过这样做是出于“性能原因”,但我仍然想知道这种16字节对齐方式在哪些特定情况下提高了性能。或者,无论如何,选择它的原因是什么。 编辑:我认为我写这个问题的方式有误导性。我不是在问为什么处理器使用16字节对齐的内存会更快,这在文档中随处都有解释。相反,我想知道的是,强制的16字节对齐如何优于在需要时让程序员自己对齐堆栈。我这样问是因为根据我在汇编方面的经验,堆栈强制有两个问题:它只

  • 我有vframe函数并生成了如下的汇编代码 如果我们看到从8到11行,我们没有在堆栈上推送p的地址,但汇编已经假设 如果我们希望某些参数不被破坏,我们会推送一些寄存器并将被调用者保存的寄存器移动到推送的寄存器。但是,在这种情况下,似乎不是。关于局部变量还有其他约定吗,比如i和

  • 本文向大家介绍Intel x86 Assembly& Microarchitecture 32位寄存器,包括了Intel x86 Assembly& Microarchitecture 32位寄存器的使用技巧和注意事项,需要的朋友参考一下 示例 英特尔生产80386时,他们从16位处理器升级到了32位处理器。32位处理意味着两件事:要处理的数据都是32位,而要访问的内存地址是32位。为此,他们仍然

  • 当我运行我的Jenkins工作时,我得到以下错误。我已经安装了MSBuild15和17,并指定了正确的路径。 有人能解释一下吗?问题在哪里。谢了。 gyp错误!堆栈错误:失败,退出代码:1 gyp错误!在childprocess.onexit上堆栈(C:\program files\nodejs\node_modules\npm\node-gyp\lib\build.js:262:23)gyp错误

  • 我来自C/C++背景,在这里一个进程内存分为: null 我想把我的注意力集中在这一点上,当我阅读JVM中的堆和堆栈时,我们是在谈论堆栈和堆的概念吗?并且整个JVM的实际内存驻留在堆上(这里指的是堆的C++概念)?