How to write Hello World in Assembler for Linux

To run Hello World we just need to issue two system calls: One to print the string and the other to exit nicely.

Here is the src file you need hello-world.S

We typically use file suffix .s for assembler when machine generated. The capital .S is used for lovingly hand-crafted assembler files.

Contents of hello-world.S

# Hello World (C) 2010 D J Greaves, University of Cambridge Computer Laboratory
	.text
	.globl _start
_start:	
	mov    $0x1,%edi    # File descriptor no (stdout = 1)
	lea	hello(%rip), %esi  # String
	mov    $0x6,%edx    # 6 chars	
	mov    $1,%eax      # sys call number. 1 = write
	syscall 
 
	mov    $0x11,%edi    # return code
	mov    $60,%eax      # sys call number. 60 = exit
	syscall 


hello:
	.string "Hello!"

	# eof

Compiling and Running Hello World in Assembler

We must first assemble the file.

djg>$ as -o hello-world.o hello-world.S

djg>$ ls -l
-rw-rw-r-- 1 djg11 djg11 768 Oct 23 09:29 hello-world.o
-rw-rw-r-- 1 djg11 djg11 425 Oct 23 09:27 hello-world.S

To get a linux executable, we can run it through the linker. Just running "chmod +x" on the assembler output is not sufficient. Why?

djg>$ ld hello-world.o
djg>$ ls -lt
-rwxrwxr-x 1 djg11 djg11 4736 Oct 23 09:30  a.out
-rw-rw-r-- 1 djg11 djg11  768 Oct 23 09:29  hello-world.o
-rw-rw-r-- 1 djg11 djg11  425 Oct 23 09:27  hello-world.S

Now we can execute it natively and under strace:

djg>$ ./a.out
Hello!djg>$ 



djg>$ strace ./a.out
execve("./a.out", ["./a.out"], 0x7fffdfb599b0 /* 41 vars */) = 0
write(1, "Hello!", 6Hello!)                   = 6
exit(17)                                = ?
+++ exited with 17 +++
djg>$ 

strace reports the system calls invoked.

A successful program exits with return code 0 by convention. Here we deliberately returned 17 (0x11), which is non zero, to show how the shell reports it.

Disassembly

We can look at the generated code using a disasembler, such as objdump -d

djg>$ objdump -d hello-world.o
hello-world.o:     file format elf64-x86-64
Disassembly of section .text:

0000000000000000 <_start>:
   0:	bf 01 00 00 00       	mov    $0x1,%edi
   5:	8d 35 18 00 00 00    	lea    0x18(%rip),%esi        # 23 
   b:	ba 06 00 00 00       	mov    $0x6,%edx
  10:	b8 01 00 00 00       	mov    $0x1,%eax
  15:	0f 05                	syscall 
  17:	bf 11 00 00 00       	mov    $0x11,%edi
  1c:	b8 3c 00 00 00       	mov    $0x3c,%eax
  21:	0f 05                	syscall 

0000000000000023 :
  23:	48                   	rex.W
  24:	65 6c                	gs insb (%dx),%es:(%rdi)
  26:	6c                   	insb   (%dx),%es:(%rdi)
  27:	6f                   	outsl  %ds:(%rsi),(%dx)
  28:	21 00                	and    %eax,(%rax)


djg>$ objdump -d a.out
a.out:     file format elf64-x86-64
Disassembly of section .text:

0000000000401000 <_start>:
  401000:	bf 01 00 00 00       	mov    $0x1,%edi
  401005:	8d 35 18 00 00 00    	lea    0x18(%rip),%esi        # 401023 
  40100b:	ba 06 00 00 00       	mov    $0x6,%edx
  401010:	b8 01 00 00 00       	mov    $0x1,%eax
  401015:	0f 05                	syscall 
  401017:	bf 11 00 00 00       	mov    $0x11,%edi
  40101c:	b8 3c 00 00 00       	mov    $0x3c,%eax
  401021:	0f 05                	syscall 

0000000000401023 :
  401023:	48                   	rex.W
  401024:	65 6c                	gs insb (%dx),%es:(%rdi)
  401026:	6c                   	insb   (%dx),%es:(%rdi)
  401027:	6f                   	outsl  %ds:(%rsi),(%dx)
  401028:	21 00                	and    %eax,(%rax)
djg>$ 

Can we see any differences between the input and the output? We see the linker has relocated using a default origin. We see it does not understand ASCII and has dissassembled "hello" as machine instructions!


(C) DJ Greaves 2010.