Boilerplate for NASM

Posted on
PL

Brief introduction to x86 using the _cdecl calling convention and linking with a CRT:

  • Every function builds and breaks down its own stack frame.
  • Caller pushes arguments onto the stack, calls the function, and then removes the arguments from the stack.
  • Function arguments are pushed right-to-left.
  • The return value of a function goes in eax.
  • The x86 stack grows downwards.

Example

global _main
extern _printf

section .text ; non-writable, executable

; function putstr( const char* )
putstr:
    push ebp    ; set up a stack frame
    mov ebp,esp ; this function is small enough + we're not using any local variables, so we could probably omit the frame pointer

    ; our arguments are [ebp+8], [ebp+12], ...

    push dword [ebp+8]
    push format_str
    call _printf
    add esp, 8   ; cdecl: remove our arguments from the stack

    mov eax, 0   ; return 0
    leave        ; leave our stack frame
    ret

; function main( )
_main:
    push ebp     ; set up a stack frame
    mov ebp,esp

    ; move the next stack pointer to make room for one local variable
    sub esp, 4

    ; copy the constant message pointer into our local variable
    ; (this obviously isn't necessary, just an example. p.s. mov is shit)
    push my_message
    pop [ebp-4]    ; equivalently [esp+4] but it's nice to base locals off something that isn't going to change

    ; call our putstr!
    push [ebp-4]
    call putstr
    add esp, 4      ; like popping, but into nowhere in particular.

    mov eax, 1337   ; return
    leave           ; close off stack frame
    ret

; Constants:
section .data ;  on-executable
    my_message: db "GARBAGE COLLECTION IS SHIT", 0x0D, 0x0A, 0 ; \r\n and null-terminated for printf
    format_str: db "%s", 0

Building

Assemble:

nasm -fwin32 -o test.obj test.asm

For debugging with Dr.MinGW:

gcc -gcoff -O0 -o test.exe test.obj

Otherwise:

gcc -s -O2 -o test.exe test.obj

It’s nice to run echo %ERRORLEVEL% to catch the return from main.

References

[1]