2

I'm on Ubuntu using nasm 2.14.02.

I'm trying to write a simple hello world program with nasm by pushing the variable onto the stack and then calling printf:

; Import c-lib printf
extern printf

; Define Message and Length
section .data:
  msg:      db "Hello People!", 0xA
  msg_len   equ  $-msg

section .text:
  global _start

_start:
  push msg
  call printf

  mov eax, 1
  mov ebx, 0
  int 80h

Assembling the program with nasm works fine, but when I try to link it with ld it throws this error:

ld: hello_world.o: in function `_start':
hello_world.asm:(.text:+0x6): undefined reference to `printf'

So it seems extern does not import printf. What am I doing wrong. This is literally my first assembler program, so please use simple words.

Edit:

The commands I was using are:

nasm hello_world.asm -f elf64
ld hello_world.o -o hello_world
PixelRayn
  • 392
  • 2
  • 13
  • 2
    `extern` just tells the assembler that you will provide the definition of the given symbol externally. You still need to provide it, that is link with libc. If you wish to use C functions, it is recommended you use `main` as entry point and not `_start` and use `gcc` to link. Also do not use direct system call to exit, instead return from `main` or `call exit`. PS: make sure you are creating a 32 bit program. – Jester Aug 19 '20 at 17:52
  • Hey, I'm sure that made a lot of sense to someone who knows what they are doing, but I don't. :) How do I use main as an entry point? I tried that and I get an error that _start isn't used as an entry point. Why should I do so? How do I tell nasm to use gcc? What does that even mean? Why is it important to compile it into a 32 bit program and not 64, even though I am on a x86 infrastructure? Does the c lib use 32 bits? I have so many questions and I thank you for your efforts but that didn't really help. – PixelRayn Aug 19 '20 at 19:20
  • Maybe a duplicate: [Assembling 32-bit binaries on a 64-bit system (GNU toolchain)](https://stackoverflow.com/q/36861903) has a NASM section, but it's mostly about GAS. – Peter Cordes Aug 19 '20 at 19:31
  • What shell commands exactly did you use to try to build an executable from your NASM source? Hopefully `nasm -felf32`, then `ld`, but you left out `-lc` to link the C library that contains `printf`. – Peter Cordes Aug 19 '20 at 21:03
  • 1
    @PixelRayn Use `main` as the entry point, then link through the C compiler. Also, call `exit` instead of doing a system call to cleanly shut down the C runtime. – fuz Aug 19 '20 at 22:12
  • @PeterCordes I edited my commands in, although I do not fully understand what you mean with -lc since neither nasm or ld has such an option. – PixelRayn Aug 20 '20 at 09:32
  • 1
    `ld` has a `-l` option that takes a library name. `c` is the name of the C library. `-lfoo` links `libfoo.so`. `-lc` links `libc.so`. But really do what fuz said; unless you understand exactly what you're doing, if you want to use libc functions like printf then write a `main` and link using `gcc -m32 foo.o` to invoke `ld` with the correct libraries and so on. (use `-v` to see what options it uses.) – Peter Cordes Aug 20 '20 at 09:38

1 Answers1

1

You need to link your code with a library that provides printf; that's provided by the C standard library. The easiest way to get that is to use gcc to call the linker for you.

nasm -felf32 hello_world.asm
gcc -static -o hello_world hello_world.o

The above will not work, however, because gcc will try to add some additional code found in crt1.o that contains _start to start the C runtime and then invoke main.

The easiest fix for your code is to write a main method instead.

  section .text:
  global main

main:
  push ebp     ; these two lines are the usual function entry
  mov ebp, esp

  push msg
  call printf

  xor eax, eax ; set eax to zero as the return value

  mov esp, ebp ; these two lines are the usual function exit
  pop ebp
  ret

I'm not on a 32-bit machine, so I can't easily test that, but I think that's what you need. Use nasm and then gcc, as above, to assemble and link it.

If you do want to use _start, that's fine. You need to link with the C standard library (statically... using GOT is a whole other thing), probably with -lc.

Stacy J
  • 158
  • 7
  • *I'm not on a 32-bit machine, so I can't easily test that* - Most x86-64 GNU/Linux distros have a `gcc-multilib` or `lib32-*` package(s) you can install so `gcc -m32 -fno-pie -no-pie hello.o` will work, and the kernel itself can execute 32-bit ELF executables thanks to CONFIG_IA32_EMULATION. (gcc `-static` implies `-no-pie`, but you don't *need* to statically link libc). – Peter Cordes Feb 22 '21 at 03:34
  • Setting up EBP as a frame pointer is optional, and in this case unneeded. To strictly follow the i386 System V ABI as used on Linux, ESP should be aligned by 16 before a call. So that means 3 pushes after function entry (the return address was pushed by call). e.g. `sub esp,8` / `push msg` / `call printf` / `add esp,12` / `ret`. Or just tailcall it: `mov dword [esp+4], msg` / `jmp printf`, overwriting main's incoming argc. – Peter Cordes Feb 22 '21 at 03:43