了解堆栈对齐实施 [英] Understanding stack alignment enforcement

查看:74
本文介绍了了解堆栈对齐实施的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑以下C代码:

#include <stdint.h>

void func(void) {
   uint32_t var = 0;
   return;
}

GCC 4.7.2为上述代码生成的未经优化的(即 -O0 选项)汇编代码为:

The unoptimized (i.e.: -O0 option) assembly code generated by GCC 4.7.2 for the code above is:

func:
    pushl %ebp
    movl %esp, %ebp
    subl $16, %esp
    movl $0, -4(%ebp)
    nop
    leave
    ret

根据 System V ABI 堆栈对齐要求,必须在每个 call 指令之前将堆栈对齐16个字节(如果未使用选项 -mpreferred-stack-boundary 进行更改,则堆栈边界默认为16个字节.因此,在函数调用之前, ESP modulo 16的结果必须为零.

According to the stack alignment requirements of the System V ABI, the stack must be aligned by 16 bytes before every call instruction (the stack boundary is 16 bytes by default when not changed with the option -mpreferred-stack-boundary). Therefore, the result of ESP modulo 16 has to be zero prior to a function call.

考虑到这些堆栈对齐要求,在执行 leave 指令之前,我假设以下堆栈的状态表示是正确的:

Bearing in mind these stack alignment requirements, I assume the following stack's status representation just before executing the leave instruction to be right:

Size (bytes)       Stack          ESP mod 16      Description
-----------------------------------------------------------------------------------

             |     . . .      |             
             ------------------........0          at func call
         4   | return address |
             ------------------.......12          at func entry
         4   |   saved EBP    |
     ---->   ------------------........8          EBP is pointing at this address
     |   4   |      var       |
     |       ------------------........4
 16  |       |                |
     |  12   |                |
     |       |                |
     ---->   ------------------........8          after allocating 16 bytes

牢记这种堆栈表示法,有两点使我感到困惑:

With this representation of the stack in mind, there are two points that puzzle me:

  1. var 显然在堆栈上未对齐为16个字节.这个问题似乎与我在此答案中 所读内容相矛盾.q/1061818/8012646>这个问题(重点在于我自己):

  1. var is obviously not aligned on the stack to 16 bytes. This issue seems to contradict what I have read in this answer to this question (the emphasis is of my own):

-mpreferred-stack-boundary = n ,编译器在其中尝试将堆栈上的项目保持对齐到2 ^ n .>

-mpreferred-stack-boundary=n where the compiler tries to keep items on the stack aligned to 2^n.

在我的情况下,未提供 -mpreferred-stack-boundary ,因此根据

In my case -mpreferred-stack-boundary wasn't provided, so it is set by default to 4 (i.e.: 2^4=16 bytes boundary) according to this section of GCC's documentation (I got indeed the same results with -mpreferred-stack-boundary=4).

在堆栈上分配16个字节的目的(即 subl $ 16,%esp 指令)而不是仅分配8个字节:分配16个字节后,堆栈均未对齐16个字节,也不保留任何存储空间.通过仅分配8个字节,堆栈将按16个字节对齐,并且不会浪费额外的8个字节.

The purpose of allocating 16 bytes on the stack (i.e.: the subl $16, %esp instruction) instead of allocating just 8 bytes: after allocating 16 bytes neither the stack is aligned by 16 bytes nor any memory space is spared. By allocating just 8 bytes instead, the stack gets aligned by 16-bytes and no additional 8 bytes are wasted.

推荐答案

查看 -O0 生成的机器代码通常是徒劳的.编译器将以最简单的方式发出任何有效的作品.这通常会导致奇异的伪像.

Looking at -O0-generated machine code is usually a futile exercise. The compiler will emit whatever works, in the simplest possible way. This often leads to bizarre artifacts.

堆栈对齐仅指堆栈框架的对齐.它与堆栈上对象的对齐没有直接关系.GCC将分配具有所需对齐方式的堆栈对象.如果GCC知道堆栈帧已经提供了足够的对齐方式,那么这会更简单,但如果没有,GCC将使用帧指针并执行显式对齐.

Stack alignment only refers to alignment of the stack frame. It is not directly related to the alignment of objects on the stack. GCC will allocate on-stack objects with the required alignment. This is simpler if GCC knows that the stack frame already provides sufficient alignment, but if not, GCC will use a frame pointer and perform explicit alignment.

这篇关于了解堆栈对齐实施的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆