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.

+3


source to share


1 answer


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.

+2


source







All Articles