Because you generally
manipulate the stack via the
"push" and
"pop" operations:
* The
"push" operation adds one more element to the top of the stack and increments the stack-pointer accordingly.
* The
"pop" operation removes and returns the top element from stack and decrements the stack-pointer accordingly.
Consequently, the
last element that you added (pushed) to the stack, will be the
first one that you take (pop) off the stack
→ LIFO.
However, this does
not mean that you can
not access (e.g. read) elements in the "middle" of the stack.
If you are able to figure out the address of an element in the "middle" of the stack, then you can read (or even overwrite) it
directly.
But
adding (or
removing) elements to (or from) the stack is only possible at the current
top of the stack!
Also note that, on the
x86 architecture, the
esp
register always points to the "top" of the stack, and the stack "grows"
downwards!
So, one x86 archtitecture,
pop
actually
increments the stack-pointer, and
push
actually
decrements the stack-pointer.
Also, the
ebp
register always points to the "stack frame", i.e. the address where the data for the
current function begins in the stack.
Note that the stack pointer (
esp
) can also be incremented or decremented using the
add
and
sub
instructions.
For example, a typical function
"prolog" looks like this:
1 2 3
|
push ebp # backup "old" stack frame
mov ebp, esp # store current stack pointer as the new stack frame
sub esp, N # decrement stack pointer to reserve N bytes of additional stack space for the local variables
|
The corresponding function
"epilog" then looks like this:
1 2 3
|
mov esp, ebp # restore "old" stack pointer value, which was stored as current stack frame value before
pop ebp # restore "old" stack frame value that was backed up before
ret # return from function
|
See also:
https://en.wikibooks.org/wiki/X86_Disassembly/The_Stack
https://www.felixcloutier.com/x86/pop
https://www.felixcloutier.com/x86/push