Reputation:
I'm analyzing the disassembly of the following (very simple) C program in GDB on X86_64.
int main()
{
int a = 5;
int b = a + 6;
return 0;
}
I understand that in X86_64 the stack grows down. That is the top of the stack has a lower address than the bottom of the stack. The assembler from the above program is as follows:
Dump of assembler code for function main:
0x0000000000400474 <+0>: push %rbp
0x0000000000400475 <+1>: mov %rsp,%rbp
0x0000000000400478 <+4>: movl $0x5,-0x8(%rbp)
0x000000000040047f <+11>: mov -0x8(%rbp),%eax
0x0000000000400482 <+14>: add $0x6,%eax
0x0000000000400485 <+17>: mov %eax,-0x4(%rbp)
0x0000000000400488 <+20>: mov $0x0,%eax
0x000000000040048d <+25>: leaveq
0x000000000040048e <+26>: retq
End of assembler dump.
I understand that:
-0x8(%rbp)
. Since in an int is 4 bytes shouldn't this be at next address in the stack which is -0x4(%rbp)
rather than -0x8(%rbp)
?.a
into %eax
, add 6 and then copy the value into the address at -0x4(%rbp)
.Using the this graphic for reference:
(source: thegreenplace.net)
it looks like the stack has the following contents:
|--------------|
| rbp | <-- %rbp
| 11 | <-- -0x4(%rbp)
| 5 | <-- -0x8(%rbp)
when I was expecting this:
|--------------|
| rbp | <-- %rbp
| 5 | <-- -0x4(%rbp)
| 11 | <-- -0x8(%rbp)
which seems to be the case in 7-understanding-c-by-learning-assembly where they show the assembly:
(gdb) disassemble
Dump of assembler code for function main:
0x0000000100000f50 <main+0>: push %rbp
0x0000000100000f51 <main+1>: mov %rsp,%rbp
0x0000000100000f54 <main+4>: mov $0x0,%eax
0x0000000100000f59 <main+9>: movl $0x0,-0x4(%rbp)
0x0000000100000f60 <main+16>: movl $0x5,-0x8(%rbp)
0x0000000100000f67 <main+23>: mov -0x8(%rbp),%ecx
0x0000000100000f6a <main+26>: add $0x6,%ecx
0x0000000100000f70 <main+32>: mov %ecx,-0xc(%rbp)
0x0000000100000f73 <main+35>: pop %rbp
0x0000000100000f74 <main+36>: retq
End of assembler dump.
Why is the value of b
is being put into a higher memory address in the stack than a
when a
is clearly declared and initialized first?
Upvotes: 11
Views: 2938
Reputation: 20392
The value of b
is put on the stack wherever the compiler feels like it. You have no influence over it. And you shouldn't. It's possible that the order will change between minor versions of the compiler because some internal data structure was changed or some code rearranged. Some compilers will even randomize the layout of the stack on different compilations on purpose because it can make certain bugs harder to exploit.
In fact, the compiler might not use the stack at all. There's no need to. Here's the disassembly of the same program compiled with some optimizations enabled:
$ cat > foo.c
int main()
{
int a = 5;
int b = a + 6;
return 0;
}
$ cc -O -c foo.c
$ objdump -S foo.o
foo.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <main>:
0: 31 c0 xor %eax,%eax
2: c3 retq
$
With some simple optimizations the compiler figured out that you don't use the variable 'b', so there's no need to calculate it. And because of that you don't use the variable 'a' either, so there's no need to assign it. Only a compilation with no optimizations (or a very bad compiler) will put anything on the stack here. And even if you use the values basic optimizations will put them into registers because touching the stack is expensive.
Upvotes: 8