Dissecting a Simple x86-64 bit Assembly Hello World Program

Vishal Rashmika published on
3 min, 545 words

Categories: Assembly

Today's post takes a fun detour into the world of x86-64 assembly language! We'll dissect a short program that accomplishes a familiar task: printing "Hello, World!" on the screen. While assembly might seem intimidating, understanding its basic structure can be surprisingly rewarding.

x86-64 bit assembly code for printing "Hello, World!":
; filename :- helloworld.asm

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

section .text
        global _start

_start:
        mov rax, 1
        mov rdi, 1
        mov rsi, message
        mov rdx, 14
        syscall

        mov rax, 60
        mov rdi, 0
        syscall
Assembling Instructions
nasm -felf64 helloworld.asm
ld helloworld.o -o helloworld 
./helloworld

Let's break down the code step-by-step:

1. Data Section (section .data):

  • This section stores data used by the program. Here, we have a line that defines a byte array db named message containing the string "Hello, World!". The 10 at the end specifies a newline (\n).

2. Text Section (section .text):

  • This section holds the program's instructions. The line global _start declares the entry point of our program, which is the _start label.

3. The _start Function:

  • This is where the program execution begins. Each instruction line manipulates registers, which are the CPU's internal storage units.

  • mov rax, 1 : moves the value 1 into the rax register. This value serves as a system call number, indicating what operation the program wants the operating system to perform. In this case, 1 stands for sys_write.

  • mov rdi, 1 : moves the value 1 into the rdi register. This register often holds the file descriptor for system calls like sys_write. Here, 1 typically refers to standard output (the console).

  • mov rsi, message : moves the address of the message array (stored in memory) into the rsi register. This tells the system call where to find the data to be written.

  • mov rdx, 14 : moves the value 14 into the rdx register. This register usually specifies the length of the data to be written. Here, 14 is the size of the message array (including the newline character).

  • syscall : triggers a system call. The CPU intercepts execution and calls the operating system with the information provided in the registers (rax, rdi, rsi, and rdx). In this case, the system call writes "Hello, World!" to the console.

  • mov rax, 60 moves the value 60 into the rax register. This system call number signifies sys_exit.

  • mov rdi, 0 moves the value 0 into the rdi register. This argument often indicates the exit status of the program. Here, 0 signifies successful execution.

  • syscall triggers another system call, this time telling the operating system the program is finished and should exit.

Putting it Together:

This program demonstrates how low-level assembly instructions interact with the operating system to perform a basic task. By understanding these fundamental steps, you gain a deeper appreciation for how computers execute programs!

Additional Notes:

This is a simplified explanation. x86-64 has a vast instruction set, and there are often multiple ways to achieve the same outcome. Assembly language is generally less common than higher-level languages like C++, but it's still used in specific situations where fine-grained control over hardware is necessary.

I hope this explanation empowers you to explore the fascinating world of assembly language further! Feel free to ask any questions in the comments below.