Search code examples
assemblynasmcompiler-explorergodbolt

How to call printf() using NASM assembly on godbolt.org?


Trying to run linux NASM printf() on https://godbolt.org/z/G66bdzoof, but it's returning this error:

ASM generation compiler returned: 0 /usr/bin/ld: /app/example.o:/app/example.asm:14: undefined reference to `printf' Execution build compiler returned: 1

extern printf

global _start

section .data
    message db "Hello World!", 10, 0

section .text

_start:

    ; print message
    push dword message
    call printf 
    add esp, 4 

    ; exit with code 0
    mov eax,1
    mov ebx,0
    int 80h

Question

Are there some kind of compiler options missing?

The godbolt example is using these compilation options:

-g -f elf -F stabs /tmp/compiler-explorer-compiler2022626-8227-fdi87r.9yqas/example.asm

Solution

  • The problem isn't assembling, NASM executes fine. The problem is linking, with ld. The Compiler Explorer build is linking into a static executable without libraries, presumably just ld -m elf_i386 -o foo foo.o

    The Compiler Explorer UI only gives you control over nasm options, not ld options, so you can't pass -lc to link the C library. If you use the "library' dropdown on Godbolt, it says "no libraries are configured for this language". (vs. for C++, there are libs like Google::Benchmark you can add tell it to link with; some of the libraries are not header-only, so must actually generate linker options).


    You're going to want to use a debugger to single-step your code anyway, so install a dev setup locally. https://www.onlinegdb.com/ has assembly support, but only GCC (so GAS syntax, AT&T or Intel but not with NASM directives).

    And yes, using a debugger is basically essential. Without that you're just wasting your own time (and everyone else's if you ask other people to spend time looking at code you haven't single-stepped yourself). It's like trying to build a robot blindfolded. See the bottom of the x86 tag wiki for asm debugging tips for Linux or Windows. (Your current program will only work on Linux; it uses the 32-bit int 0x80 system-call ABI to exit.)


    Normally you'd want to write a main if you're using C library functions, and don't use a raw _exit system-call if using stdio functions like printf. With output to a pipe or network socket (not a terminal), stdout will be full-buffered not line-buffered, so you'll get no output when you _exit without calling fflush.

    But linking with libc will work even when you write your own _start (skipping the CRT startup code), if you dynamically link, not static. glibc on Linux has dynamic linker hooks that let it initialize itself before _start runs (unlike on some other systems, e.g. cygwin), but if you statically link then your _start is truly the first instructions that run in user-space. Calling functions like printf that depend on data structures like global stdout will crash.