In some tutorials it is said that the stack pointer points TO the top element of the stack:
+-------------+
| stack |
+-------------+
| top element | <-- esp
+-------------+
In others it is said that it points RIGHT BEHIND it, so to the first memory address which can be written to when the stack grows.
+-------------+
| stack |
+-------------+
| top element |
+-------------+ <-- esp
On this german Wickipedia site https://de.wikipedia.org/wiki/Register_(Computer)#Stapelregister it is said that both versions exist and that it depends on the CPU architecture.
My question is, how is it on x86-CPUs? And does it also depend on the operating system?
Let's say you are in 32b x86 mode, and you have memory from address 100 to 200 reserved as stack (unreal, too low, would clash with IVT, but will do for this example). There are already some values in stack, so esp
is 160.
Now the "top of the stack value" occupies memory at addresses 160, 161, 162 and 163 (four bytes, because in 32b mode the single value in stack is 32b = 4B big), let's say there's stored value 0xaabbccdd
.
If you now do push 0x12345678
, the CPU will first subtract 4 from esp
-> new esp = 156; (160-4)
. And then it will write the 32b value, broken down in little-endian way as four bytes: mem[156] = 0x78, mem[157] = 0x56, mem[158] = 0x34, mem[159] = 0x12
.
Now if you will execute mov eax,[esp]
, it will load 32 bit value from address 156, which means it will compose 32b value from four bytes at addresses 156, 157, 158, 159 into dword
value 0x12345678.
Finally when you will view the memory in debugger after the push, viewing it from ss:esp
address, it will contain these bytes (hexa):
0000009C: 78 56 34 12 DD CC BB AA ....
(0x9C = 156 = address where the memory view starts). The esp
points at the first byte of the value, which is considered to be "top of stack".
Or when you switch memory view to display dword
values, to avoid the little-endian composing in head, it will show:
0000009C: 12345678 AABBCCDD ....