Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding stack alignment enforcement

Tags:

intel

Consider the following C code:

#include <stdint.h>

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

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

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.

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 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 where the compiler tries to keep items on the stack aligned to 2^n.

    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).

  2. 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.

like image 463
ネロク・ゴ Avatar asked Nov 21 '17 10:11

ネロク・ゴ


People also ask

What is stacked alignment?

IIRC, stack alignment is when variables are placed on the stack "aligned" to a particular number of bytes. So if you are using a 16 bit stack alignment, each variable on the stack is going to start from a byte that is a multiple of 2 bytes from the current stack pointer within a function.

Why does the stack need to be aligned?

Alignment is limiting addresses where data can be placed and is not limited to stacks. For example, 4-byte alignment would mean that all addresses have the lowest 2 bits always 0. The alignment often corresponds to the memory bus width in the hardware which can be several bytes wide.

When allocating the stack frame the stack pointer's address should be aligned on a <UNK> byte boundary?

The stack pointer must always be aligned on a 16-byte boundary in AARCH64 mode. This instruction subtracts from the address in the frame pointer register and stores the result in register r3 , ready to be passed to the read function.

What is stack alignment x86?

"Stack alignment" just means the address of the stack (SP or ESP) is a multiple of the machine word size (so always divisible by 8 for 64-bit mode, 4 for 32-bit, 2 for 16-bit).


1 Answers

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.

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.

like image 77
Florian Weimer Avatar answered Sep 23 '22 12:09

Florian Weimer