If you do not move the stack pointer back *exactly* where you found it, when you return your program will crash.
Almost instantly.
So be careful with your stack manipulation!
Here's how we allocate some space on the stack, then read and write it.
sub rsp,16 ; I claim the next sixteen bytes in the name of... me!
mov QWORD [rsp],1492 ; store a long integer into our stack space
mov rax,QWORD [rsp] ; read our long from where we stored it
add rsp,16 ; Hand back the stack space
ret
Yes, the QWORD is only 8 bytes, but I'm allocating 16 bytes. This is
OK, but wastes 8 bytes. Of course, claiming 8 and using 16 would be
disaster, so err on the side of too much allocation!
sub rsp,800 ; I claim the next eight hundred bytes
mov rdi,rsp ; points to the start of our 100-integer array
add rdi,320 ; jump down to integer 40 in the array
mov QWORD [rdi],1492 ; store an integer into our stack space
mov rax,QWORD [rdi] ; read our integer from where we stored it
add rsp,800 ; Hand back the stack space
ret
push 17
push 23
pop rax
pop rcx
ret
After the first "push", the stack just has one value:
17
After the second "push", the stack has two values:
17 23
So the first "pop" picks up the 23, leaving the stack with one value:
17
The second "pop" picks up that value, leaving the stack clean. If
the stack is not clean, "ret" stops working. Let me say that
again:
If you do not pop *exactly* the same number of times as you push, your program will crash.
Horribly.
So be careful with your pushes and pops!
push rbp ; save old copy of this registerMain might be storing something important in rbp, and will complain if you just change it, but as long as you put it back exactly how it was before you return, main is perfectly happy letting you use it!
mov rbp,23
mov rax,rbp
pop rbp ; restore main's copy from the stack
ret
push rbp ; save old copy of this registerOne big advantage to saved registers: you can call other functions, and know that they won't change their values. All the scratch registers, by contrast, are likely to get overwritten by any function you call.
push r15
mov rbp,23
mov rax,rbp
pop r15 ; restore main's copy from the stack
pop rbp
ret
mov rax,17; say I want to keep this value while calling a function...
push rax; Just save rax to the stack
mov rdi,3 ; now call the function
extern print_long
call print_long
pop rax; And we can now restore rax afterwards, and safely return 17
ret
(Try this in NetRun now!)Again, you can save as many registers as you want, but you need to pop them in the opposite order--otherwise you've flipped their values around!