Why reset the stack pointer register in FreeBSD?
I am trying to grab assembler in FreeBSD. In the reference code example for a UNIX filter, the esp register is reset after each system call. This code:
%include 'system.inc'
section .data
hex db '0123456789ABCDEF'
buffer db 0, 0, ' '
section .text
global _start
_start:
; read a byte from stdin
push dword 1
push dword buffer
push dword stdin
sys.read
add esp, byte 12 ; <--------- Is this necessary?
or eax, eax
je .done
; convert it to hex
movzx eax, byte [buffer]
mov edx, eax
shr dl, 4
mov dl, [hex+edx]
mov [buffer], dl
and al, 0Fh
mov al, [hex+eax]
mov [buffer+1], al
; print it
push dword 3
push dword buffer
push dword stdout
sys.write
add esp, byte 12 ; <--------- Is this necessary?
jmp short _start
.done:
push dword 0
sys.exit
This differs from the example on the previous documentation page :
1: %include 'system.inc'
2:
3: section .data
4: hello db 'Hello, World!', 0Ah
5: hbytes equ $-hello
6:
7: section .text
8: global _start
9: _start:
10: push dword hbytes
11: push dword hello
12: push dword stdout
13: sys.write ; <--------- ESP not adjusted after this. Why?
14:
15: push dword 0
16: sys.exit
Why are these two examples different? Why is something like that add esp, byte 12
necessary? Values โโthat are not called by a system call? Is this necessary on 64-bit FreeBSD where no arguments are passed onto the stack? I thought the stack pointer would take care of itself.
source to share
FreeBSD uses a calling convention that the caller pops the stack after another function call. Once called, the stack contains all the arguments to the function. Requesting to clear the stack of function arguments by adjusting its position immediately after the function call is the easiest way to ensure that the stack is stored correctly. But this is not the only way. For example, you can write:
; print a first thing
push dword len1
push dword buffer1
push dword stdout
sys.write
; here, 12 bytes in stack aren't retired yet
; print a second thing
push dword len2
push dword buffer2
push dword stdout
sys.write
add esp, byte 24 ; clean stack after _both_ write() calls
This optimization is actually used, for example, in GCC.
If you return from a function, you must restore the stack position after all function calls. But how exactly this is done is entirely up to you if all stack manipulations are correct.
So what's special about the last example (with sys.exit)? The peculiarity is that you do not return from it. Sys.exit doesn't return, it just stops the whole process. Thus, restoring the position of the stack is not important here.
source to share