C & gcc: stack growth and alignment - for a 64-bit machine

I have the following program. I wonder why it outputs -4 to the next 64-bit machine? Which of my assumptions went wrong?

[Linux ubuntu 3.2.0-23-generi # 36-Ubuntu SMP Tue 10 Apr 20:39:51 UTC 2012 x86_64 x86_64 x86_64 GNU / Linux]

  • In the above computer and the default gcc compiler, b should be pressed first and second. The stack is growing down. Thus, b must have a higher address and a lower address. The result should be positive. But I got -4. Can someone explain this?

  • Arguments are two characters that occupy 2 bytes on the stack. But I saw a difference as 4, where, as I expect 1. Even if someone says that it is due to alignment, then I wonder if the structure with 2 characters is not aligned by 4 bytes.

#include <stdio.h> #include <stdlib.h> #include <unistd.h> void CompareAddress(char a, char b) { printf("Differs=%ld\n", (intptr_t )&b - (intptr_t )&a); } int main() { CompareAddress('a','b'); return 0; } /* Differs= -4 */ 
+8
c gcc linux
source share
3 answers

The best way to answer such questions (about the behavior of a particular compiler on a particular platform) is to look at the assembler. You can get gcc to reset your assembler by passing the -S flag (and the -fverbose-asm flag is good too). Performance

 gcc -S -fverbose-asm file.c 

gives file.s , which is a bit like (I deleted all the non-essential bits, and the bits in brackets are my notes):

 CompareAddress: # ("allocate" memory on the stack for local variables) subq $16, %rsp # (put a and b onto the stack) movl %edi, %edx # a, tmp62 movl %esi, %eax # b, tmp63 movb %dl, -4(%rbp) # tmp62, a movb %al, -8(%rbp) # tmp63, b # (get their addresses) leaq -8(%rbp), %rdx #, b.0 leaq -4(%rbp), %rax #, a.1 subq %rax, %rdx # a.1, D.4597 (&b - &a) # (set up the parameters for the printf call) movl $.LC0, %eax #, D.4598 movq %rdx, %rsi # D.4597, movq %rax, %rdi # D.4598, movl $0, %eax #, call printf # main: # (put 'a' and 'b' into the registers for the function call) movl $98, %esi #, movl $97, %edi #, call CompareAddress 

( This question perfectly explains that [re]bp and [re]sp .)

The reason for the difference is negative: the stack grows down: i.e. if you push two things onto the stack, the one you push first will have a larger address, and a will be pushed to b .

The reason this is -4 , not -1 , is because the compiler decided that matching arguments with 4-byte boundaries is β€œbetter”, possibly because a 32-bit / 64-bit processor deals with 4 bytes at a point in time is better than it processes single bytes.

(Also, looking at assembler shows an effect that has -mpreferred-stack-boundary : essentially means that the memory on the stack is allocated in chunks of different sizes.)

+5
source share

Here is my hunch:

On Linux on x64, the calling convention indicates that the first few parameters are passed by register.

So, in your case, both a and b are passed by register, not on the stack. However, since you take its address, the compiler will store it somewhere on the stack after being called by the function.
(Not required in order down.)

It is also possible that this function is just direct.

In any case, the compiler creates a temporary stack space for storing variables. They can be in any order and are subject to optimization. Therefore, they cannot be in any particular order that you might expect.

+9
source share

I think the answer that you entered correctly, by default the preferred border of the GCC stack is 4, you can set -mpreferred-stack-boundary=num in the GCC option to change the boudary stack, then the program will give you a different answer according to your set .

0
source share

All Articles