Article by Ayman Alheraki on January 11 2026 10:37 AM
In this section, we will guide you step-by-step through the process of writing, assembling, and running a simple assembly program using GAS (GNU Assembler). Assembly programming allows developers to write code that interacts directly with the CPU, providing fine-grained control over how a program is executed. The example we will use here is a ”Hello, World!” program written for the x86 architecture. However, the principles outlined here can be applied to other architectures as well.
This section will cover the basics of assembly syntax, register usage, system calls, and provide hands-on experience in assembling and linking a simple program.
Before diving into writing the program, let's ensure that your environment is properly set up for compiling and running assembly code.
You will need several tools to assemble and run your program. These are typically part of the GNU toolchain, which includes the assembler (as), the linker (ld), and optional tools like the debugger (gdb) for debugging.
– Linux/Unix: The necessary tools (GAS, ld, and gdb) are usually included by default in most Linux distributions. You can install them using package managers like apt or yum:
sudo apt-get install build-essentialsudo apt-get install binutils
– MacOS: For MacOS, you can install the required tools through Xcode Command Line Tools by running:
xcode-select --install– Windows: On Windows, you can use Cygwin or MinGW to emulate a Linux-like environment and use the GNU assembler tools. Both packages are available online and come with the necessary tools.
After installing the tools, you will be ready to begin writing assembly code.
A simple assembly program typically consists of two major sections: the data section and the
text section. Each section serves a different purpose:
This section is where variables, constants, and data structures are declared. In assembly language, you typically store any strings, numbers, or buffers here that will be accessed or manipulated during the program's execution.
The text section contains the actual instructions that the processor will execute. These are the commands written in assembly that will be translated to machine code. It is the place where the logic of the program resides.
In addition to the data and text sections, there are also directives that help with the organization of the code, such as defining entry points for the program.
Here’s an example of a simple assembly program structure:
.section .data # Data sectionmessage: .asciz "Hello, World!" # Declare a null-terminated string
.section .text # Text section .globl _start # Define the entry point_start: ; Instructions to print the message
Let’s now write a simple ”Hello, World!” program in x86 assembly using GAS.
.section .data # Data sectionmessage: .asciz "Hello, World!" # Declare a null-terminated ASCII string
.section .text # Text section .globl _start # Global entry point for the program_start: mov $4, %eax # syscall number 4 (sys_write) mov $1, %ebx # file descriptor 1 (stdout) mov $message, %ecx # address of the message string mov $13, %edx # length of the string (13 characters) int $0x80 # trigger syscall (write to stdout)
mov $1, %eax # syscall number 1 (sys_exit) xor %ebx, %ebx # exit status 0 int $0x80 # trigger syscall (exit program)
Data Section
.section .datamessage: .asciz "Hello, World!"
.section .data: The data section is where all the data—like constants, strings, and variables—are declared. The .data directive tells the assembler that the following lines will define static data elements.
message:: This is a label representing the memory address where the string
"Hello, World!" will be stored.
.asciz "Hello, World!": This defines a null-terminated ASCII string.
The .asciz directive adds a zero byte (0x00) at the end of the string, which is necessary to signify the end of the string in C-like systems.
.section .text .globl _start_start:
.section .text: This section is where the executable code resides. The text section contains the actual assembly instructions that the CPU will execute.
.globl start: This line makes the label start globally visible, marking it as the entry point of the program. This is where the execution begins.
mov $4, %eaxmov $1, %ebxmov $message, %ecxmov $13, %edxint $0x80
mov $4, %eax: This sets the EAX register to the value 4, which corresponds to the system call number for sys write. The sys write system call is used to output data to a file or terminal.
mov $1, %ebx: The EBX register is set to 1, representing the file descriptor for standard output (stdout).
mov $message, %ecx: This sets the ECX register to the address of the string
"Hello, World!". This tells the kernel where to find the message.
mov $13, %edx: The EDX register is set to 13, which is the length of the string
"Hello, World!".
int $0x80: This triggers a software interrupt that invokes the kernel to perform the system call. In this case, it will execute the sys write system call to print the string to the screen.
mov $1, %eaxxor %ebx, %ebxint $0x80
mov $1, %eax: The EAX register is set to 1, corresponding to the system call number for sys exit, which is used to terminate the program.
xor %ebx, %ebx: The EBX register is cleared by using the xor instruction. This sets the exit code of the program to 0, which usually means a successful exit.
int $0x80: This triggers the system call to exit the program.
Once the assembly code is written, the next step is to convert the human-readable assembly code into machine code. This process involves two main steps: assembling and linking.
The first step is to assemble the source code into an object file. This is done using the
To assemble the code, open a terminal and run the following command:
as -o hello.o hello.sHere:
hello.s is the assembly source file.
hello.o is the object file that will be produced after the assembly step.
The object file (hello.o) contains machine code but is not yet executable. It must be linked before running.
Next, you will use a linker (in this case, ld) to combine the object file into an executable program.
To link the object file and create an executable, use the following command:
ld -o hello hello.oThis command tells the linker (ld) to:
Combine the object file hello.o into an executable named hello.
The resulting executable is now ready to be run.
Now that the program is assembled and linked, you can execute it. Use the following command to run the program:
./helloIf everything is set up correctly, you should see the following output:
Hello, World!This demonstrates a simple interaction between your assembly code and the operating system: you wrote assembly instructions that used a system call (sys write) to print a message to the terminal.
This section introduced you to the basics of writing assembly programs using the GNU Assembler (GAS). You learned how to:
Write simple assembly code using basic instructions and system calls.
Structure your code into data and text sections.
Assemble the code and link the resulting object file to create an executable.
Use system calls to interact with the operating system and perform tasks like printing output and exiting the program.
Mastering these fundamentals is essential for anyone wishing to understand how low-level programming works, how programs interact with hardware, and how assembly language can be used to optimize software for performance or for specialized hardware environments.