Because why not? Software is fun.
I know this sounds insane; assembly for fun—but trust me, you feel alive.
I’m lucky to be at a point where I can build almost anything, so I’m testing the waters for a low-level series combining Node.js and assembly as a shared library.
In this article, we’re saying hello to the world in x86 Linux assembly.
Hello in Assembly
You need a Linux environment. On Windows, you can use WSL2.
Create a file called hello.s
and add the following code:
.section .data
msg:
.asciz "Hello, World!\n" # Define a null-terminated string with a newline
msg_len = . - msg # Length from current location to msg
.section .text
.globl _start # Declare _start as a global symbol (entry point)
_start:
# --- sys_write(int fd, const void *buf, size_t count) ---
movl $4, %eax # sys_write into %eax
movl $1, %ebx # fd stdout into %ebx
movl $msg, %ecx # msg address
movl $msg_len, %edx # msg length
int $0x80 # Trigger the kernel system call
# --- sys_exit(int status) ---
movl $1, %eax # sys_exit into %eax
xorl %ebx, %ebx # Zero out %ebx (exit code 0)
int $0x80 # Kernel call
Compile the program using as
. Installing it on Linux is simple—just install the GNU tools with:
sudo apt update && sudo apt install -y build-essential gdb libgtk-3-dev
Then compile your assembly file:
as ./hello.s -o hello.o
This command creates the object file. Next, link it:
ld ./hello.o -o hello
Finally, run your program:
./hello
This might be the most complex "Hello World" you’ve ever seen, especially if you’re new to assembly.
Let’s break it down.
Sections in Assembly
Assembly programs are divided into sections:
-
.section .data
→ Where we store data, like "Hello, World!" -
.section .text
→ Where we write instructions -
.globl _start
→ Defines the program’s entry point
Symbols and Labels
Symbols are names for memory locations.
For example, msg
is a symbol. It tells assembly, "Hey, remember this location—we’ll use it later." It’s like a variable.
When you add a :
, it becomes a label, defining the value of a symbol.
msg:
.asciz "Hello, World!\n"
This means: "At memory location msg
, store the string 'Hello, World!\n'."
This pattern is everywhere. _start
is also a label:
.globl _start
_start:
Making _start
global lets the assembler and linker find it.
Keeping Track of Memory
Assembly doesn’t track memory for you—you have to do it manually.
msg_len = . - msg
What’s happening here?
msg
is just an address. It points to the start of "Hello, World!\n"
, but it doesn’t know where it ends.
.
is the current memory location—right after the string.
So, msg_len = . - msg
means:
Take the address after
"Hello, World!\n"
and subtract the start address.
That gives us the string’s length, which we need for the sys_write
call.
CPU Architecture
Registers store data temporarily and operate way faster than RAM.
In our program, %eax
is a register.
General-purpose registers:
-
%eax
-
%ebx
-
%ecx
-
%edx
-
%edi
-
%esi
Special-purpose registers:
-
%ebp
-
%esp
-
%eip
-
%eflags
Here’s how we move data between registers:
movl $4, %eax # sys_write into %eax
movl $1, %ebx # fd stdout into %ebx
movl $msg, %ecx # msg address
movl $msg_len, %edx # msg length
int $0x80 # Trigger the kernel system call
But before we can execute anything, we need to talk about...
The Kernel
The kernel is the middleman between your program and hardware. Every system call (file access, memory allocation, exiting a program) goes through the kernel.
Whenever you see this:
int $0x80 # Kernel call
You’re asking the kernel for help.
But the kernel has rules. Before calling it, you must set up registers with the correct values.
For example, to exit a program, the kernel expects:
movl $1, %eax # sys_exit command
movl $0, %ebx # Exit status
The sys_exit
call expects:
-
%eax = 1
→ "I want to exit." -
%ebx = 0
→ "Exited successfully."
It’s the same as returning 0 in C:
int main() {
return 0; // Exit status 0 (success)
}
Writing to the Console
Unlike sys_exit
, writing to the console requires more information:
movl $4, %eax # sys_write command
movl $1, %ebx # File descriptor 1 (stdout)
movl $msg, %ecx # Message address
movl $msg_len, %edx # Message length
int $0x80 # Kernel call
Here’s what’s happening:
-
movl $4, %eax
→ The number 4 tells the kernel, "I want to write." -
movl $1, %ebx
→ The number 1 means "write to stdout." -
movl $msg, %ecx
→ Where’s the message? It’s atmsg
. -
movl $msg_len, %edx
→ How long is the message? That’s inmsg_len
. -
int $0x80
→ Call the kernel to execute the write.
Once we’ve written our message, we exit:
movl $1, %eax # sys_exit
xorl %ebx, %ebx # Exit code 0
int $0x80 # Kernel call
AT&T vs. Intel Syntax
This program uses AT&T syntax, which is common in GNU assemblers.
Intel syntax, used in NASM, looks slightly different. A major difference is that AT&T syntax prefixes registers with %
.
Some people prefer Intel syntax, but most books stick with AT&T, so it’s worth learning.
You Made It!
That’s it! You just wrote and understood x86 assembly’s "Hello, World!"
I might start a low-level Node.js series soon. Let me know if that sounds interesting!
You can find me on x
Meanwhile, check out this series where we build a Node-native message broker from scratch:
Here's what you'll learn:
- Long live TCP with heartbeat functionality
- Buffers
- Queue management: acknowledgments and cleanup
- A client driver based on events and event queues
- Pub/Sub
- BSON serialization and deserialization for durable queues
- Handshake and authentication 🚀
Top comments (20)
I'll just
`
that thanks.
Let me join. lol
gist.github.com/lolzballs/2152bc0f...
😭😭😂 solved!!
Great to see assembly again. I used to code in assembly on the Amiga in the good old days.
ahhh the old days 🏖️ I remember, I am lying I started this year 😂, I think I am a jr assembler? Loving it thou, so good to touch the metal
Enjoy the ride. IMHO it will teach you a lot.
Protect your images in WordPress with adding watermarks automatically in just a few clicks with RMBG.PRO 🚀
youtu.be/Xqhtht95P-c
WordPress #Watermark #WordPressPlugin #ImageProtection #WebDesign #Photography #BloggingTips #WordPressTutorial
People having entire conversations through code in comments lol. 🤣
Between, awesome post. 🔥
Everyone who is commenting that they rather use high-level programming language for "Hello World", you should understand that even author is not recommending to use assembly in real life. We developed High Level languages to make our lives easier, everyone agrees on that. Its so sad that they cant appreciate assembly and not at all curious to understand how things work.
Future will be just like,
When someone writes a C program to print hello world, commentors like these will comment like "I would rather use chatGPT prompt, this is so stupid!"
this is quite a cool blog post,
so I began to learn Computer Architecture & Organization and I found the assembly section to be the most interesting (because it's practical, and you could see the theory about registers etc in action).
But honestly I didn't find a right motive to learn it and be proficient in it, I'm learning it only for the curiosity at the moment but yk I still didn't find the right purpose.
So I ask you, could it possibly be useful in something like say... Malware development ? because code could be obfuscated etc on a very low level.
What do you think?
1
Some comments have been hidden by the post's author - find out more