“Hello World” in assembly language on Linux

Apr. 22, 2015

One of my side-projects this summer is to become minimally literate in assembly language. So I began by reading up a tutorial on the x86-64 and the Linux ABI.

And here for your reading pleasure is hello.s. For this example we need to know only of the following bits of the ABI:

  • The stack grows downwards (towards lower addresses) with %rsp pointing to the bottom of the stack.
  • The call instruction decrements %rsp by 8, stores the address of the instruction after itself on the stack and then jumps to the address pointed to by its operand.
  • The ret instruction jumps to the address pointed to by %rsp and increments %rsp by 8 before resuming execution.
  • Integer or pointer parameters to functions are passed in registers %rdi, %rsi, %rdx, %rcx, %r8 and %r9 in that order and only if there are more than six such parameters is the stack used. An integer or pointer return value is returned in %rax.
/*
 A simple standalone hello world program.
 Usage: hello [arg]

 Prints "Hello $arg" if [arg] supplied,
 else "Hello world".
*/

.text
/* We will link our program using gcc,
   which will take care of initialization and trasfer
   control to our 'main'. For this to work 'main'
   has to be marked as a symbol visible to the linker.
*/
  .global main
  .type main, @function

main:
/* The ABI requires %rsp to be aligned to a 16-byte
   boundary just before a call.

   Since the %rsp is currently at a 8-byte
   boundary because of the return address
   from main, and because none of the calls we make pass
   any arguments on the stack, the following adjustment
   is enought for all the calls we make.
*/
  subq $8, %rsp

/*Compare argc to 1*/
  cmpl $1,%edi
  jne .HAS_ARG
  movq $.DEF_GREET, %rsi
  jmp .PRINT
.HAS_ARG:
  /*argv[1]*/
  movq 8(%rsi), %rsi
.PRINT:
  movq $.FMT_STR, %rdi
  /*When calling variable argument
  functions like 'printf' we must 
  place an upper bound on the number of
  vector arguments in %al*/
  xorb %al,%al
  call printf

/*Ready to return. Reverse our adjustment 
  to %rsp
*/
  addq $8,%rsp
  movq $0, %rax
  ret

  .section .rodata
.FMT_STR:
  .string "hello %s\n"
.DEF_GREET:
  .string "world"

The program above calls printf and so has to be linked to the C library. I spent all afternoon feeding strange incantations to the GNU linker ld to create a working executable. While I finally did get something which worked, I did not feel confident that I was doing exactly right. Finally I wimped out and called gcc to do the linking, after seeing that even the famous GHC did this. Here’s the Makefile

hello: hello.o
	gcc -o $@ $<

hello.o: hello.s

Next target, learning more about the instruction set, specially the SIMD instructions.