Stack alignment responsibility in x86 assembly

I am trying to get a clear idea of ​​who (the caller or the callee) is not responsible for stack alignment. The case for the 64-bit build is pretty clear, as it is called the caller.

Referring to System V AMD64 ABI, Section 3.2.2 "Stack Structure":

The end of the input argument area must be aligned at 16 (32 if __m256 is passed along the stack).

In other words, it should be safely assumed that for each entry point of the called function:

16 | (%rsp + 8)

(an extra eight is because call implicitly pushes the return address on the stack).

What does it look like in a 32-bit world (provided cdecl)? I noticed that gcc places alignment inside the called function with the following construct:

 and esp, -16 

which seems to indicate that it is the responsibility of the called person.

To make this clearer, consider the following code:

 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 

Do I need to align the stack before calling scanf ? If so, then you will need to reduce %esp by four bytes before pushing these two arguments to scanf as follows:

 4 bytes (return address) 4 bytes (%ebp of previous stack frame) 8 bytes (for two variables) 12 bytes (three arguments for scanf) = 28 
+5
source share
1 answer

gcc simply takes a defensive approach with -m32 , without assuming that main is invoked with a properly aligned stack in 16B.

The i386 V ABI system has guaranteed / required for many years that the ESP + 4 aligns to 16B when entering the function. (that is, the ESP must be aligned 16B before the CALL instruction, therefore it argues the beginning of the stack at the border of 16 B. This is the same as for the x86-64 V system).

ABI also ensures that new 32-bit processes start with an ESP aligned on the 16B boundary (for example, at _start , at the ELF entry point, where the ESP points to argc rather than the return address) and the CRT glibc code supports alignment.

Regarding the call agreement, EBP is another call-saving register. But yes, the compiler output using -fno-omit-frame-pointer will take care of push ebp in front of other registers to be saved codes (for example, EBX) and will do this even if functions do not need to use EBP, therefore stored EBP values ​​form a linked list.


Perhaps gcc is protected because the extremely ancient Linux kernel (from this version to i386 ABI, when the required alignment was only 4B) could violate this assumption, and these are just additional pair instructions that run once in a lifetime, time (at provided that the program does not call main recursively).


Unlike gcc, clang assumes the stack is correctly aligned when entering main. (clang also assumes that the narrow arguments were marked with a sign or equal to zero up to 32 bits , although the current ABI revision does not indicate this behavior (for now). gcc and clang both emit code that does on the caller side, but only clang depends on it in the called. This happens in 64-bit code, but I did not check 32-bit.)

Look at the compiler output at http://gcc.godbolt.org/ for the core and other functions besides the core, if you're interested.


I just updated the ABI links in the tag wiki the other day. http://x86-64.org/ is still dead and doesn't seem to be returning, so I updated System V links to point to PDF files of the current version in the HJ Lu github repo and its page with links .

Please note that the latest version on the SCO website is not the current version and does not include the requirement to align the 16B stack.

+3
source

Source: https://habr.com/ru/post/1258940/


All Articles