Memory layout
In a little-endian system, bytes are organized such that the most significant byte is at the higher address, while the least significant byte occupies the lower address.
Each address corresponds to a one-byte cell:
Offset => 0 1 2 3 4 5 6 7 8 9 10 11
Cell => | | | | | | | | | | | | |
When storing 0xdeadbeef
into memory, the layout on a 64-bit machine appears as follows:
Offset => 0 1 2 3 4 5 6 7 8 9 10 11
Cell => | ef | be | ad | de | 00 | 00 | 00 | 00 | | | | |
This representation makes little-endian format intuitive for those accustomed to reading right-to-left.
When an array is stored, it fills from lower to higher addresses. For instance, consider this C code:
int main() {
char a[4];
a[0] = 'a';
a[1] = 'b';
a[2] = 'c';
a[3] = 'd';
return 0;
}
Compiled on macOS 10.15.4 with:
Apple clang version 11.0.3 (clang-1103.0.32.29)
Target: x86_64-apple-darwin19.4.0
Thread model: posix
The generated assembly output:
100000f96: c7 45 fc 00 00 00 00 movl $0, -4(%rbp)
100000f9d: c6 45 f8 61 movb $97, -8(%rbp)
100000fa1: c6 45 f9 62 movb $98, -7(%rbp)
100000fa5: c6 45 fa 63 movb $99, -6(%rbp)
100000fa9: c6 45 fb 64 movb $100, -5(%rbp)
Visually, the memory layout looks like this:
rbp ----
|
v
Offset => 0 1 2 3 4 5 6 7 8 9 10 11
Cell => | | | | a | b | c | d | \0 | | | | xx |
To retrieve the value pointed to by RBP, data is accessed from [RBP+0] to [RBP+7]:
rbp ---- start of return address ----
| |
v v
Offset => 0 1 2 3 4 5 6 7 8 9 10 11
Cell => | 9a | 54 | 6a | 8c | da | d0 | 64 | 3c | e7 | 1f | ef | d8 |
If RSP is aligned with RBP, executing POP RAX
loads RAX with the value 0xe73c64d0da8c6a54
.
It is important to note that the value that RBP holds is a memory address. In this case, its address is at offset 1, while the value it points to is 0xe73c64d0da8c6a54
.