Assembly Language - Environment Setup
This chapter will guide you through setting up the NASM assembly development environment on a Linux system and completing your first assembly program.
Linux Environment Preparation
If you are using Windows, it is recommended to installWSL(Windows Subsystem for Linux)or use a virtual machine to obtain a Linux environment.
macOS users can install Linux in a virtual machine or use Docker containers.
All examples in this tutorial are based onUbuntu/Debiansystem. For other distributions, please adjust the package manager commands accordingly.
Install NASM Assembler
On Ubuntu/Debian systems, install NASM using the apt package manager:
$ sudo apt update $ sudo apt install nasm
After installation, verify that NASM was installed successfully:
$ nasm -version NASM version 2.16.01
If you see the version number output, it means NASM has been installed successfully.
Install Linker
The assembler generatesobject files (.o files), which cannot be run directly.
You need a linker to convert the object file into an executable. We use the linker that comes with GCC:
$ sudo apt install gcc
Alternatively, you can directly use the GNU linker ld:
$ ld --version
In this tutorial, we mainly usegccfor linking, because it automatically handles some low-level details and is more suitable for beginners.
Install Debugging Tool GDB
GDB(GNU Debugger)is the most commonly used debugger on Linux, allowing you to step through programs, view register states, and inspect memory contents.
$ sudo apt install gdb
Verify GDB installation:
$ gdb --version GNU gdb (Ubuntu 12.1-0ubuntu1) 12.1
First Assembly Program: Hello, example
Create a new filehello.asm, and enter the following content:
Example
; First assembly program: print Hello, example!
; NASM syntax, Linux 32-bit
section .data ; Data section: stores initialized data
msg db 'Hello, example!', 0xA ; Define string msg, 0xA is newline character
len equ $ - msg ; Compute string length: current address - msg start address
section .text ; Code section: stores executable instructions
global _start ; Declare _start as the program entry point
_start: ; Program entry
; System call sys_write (4): output string to stdout
mov eax, 4 ; System call number 4 = sys_write
mov ebx, 1 ; File descriptor 1 = stdout (standard output)
mov ecx, msg ; Address of the string to output
mov edx, len ; String length
int 0x80 ; Trigger interrupt to execute system call
; System call sys_exit (1): exit program normally
mov eax, 1 ; System call number 1 = sys_exit
mov ebx, 0 ; Return value 0 = normal exit
int 0x80 ; Trigger interrupt to execute system call
Compile and Run
The entire compilation and execution flow is shown in the diagram:
Use the following commands to compile and run this program:
$ nasm -f elf32 hello.asm -o hello.o # 汇编:将 .asm 转为 .o 目标文件 $ ld -m elf_i386 hello.o -o hello # 链接:将 .o 转为可执行文件 $ ./hello # 运行程序 Hello, example!
The purpose of each of the three commands above:
| Step | Command | Description |
|---|---|---|
| 1. Assemble | nasm -f elf32 hello.asm -o hello.o | -f elf32 specifies the output format as 32-bit ELF, -o specifies the output file name |
| 2. Link | ld -m elf_i386 hello.o -o hello | -m elf_i386 specifies 32-bit linking mode, generating the executable file hello |
| 3. Run | ./hello | Execute the program in the current directory |
Linking with GCC (Recommended)
If the ld configuration in your environment is complex, you can use gcc to link, which will automatically handle details such as the C runtime:
$ nasm -f elf32 hello.asm -o hello.o $ gcc -m32 hello.o -o hello -nostartfiles $ ./hello Hello, example!
-nostartfilesThe option tells GCC not to add default startup code, because we define our own_startentry point.
Code Structure Analysis
Let's understand the above program segment by segment:
| Part | Code | Description |
|---|---|---|
| Data segment declaration | section .data | Declares the data segment, storing variables and constants |
| String definition | msg db 'Hello, example!', 0xA | db defines a byte sequence; 0xA is the ASCII code for newline |
| Length calculation | len equ $ - msg | $ represents the current address; subtracting the msg address gives the string length |
| Code segment declaration | section .text | Declares the code segment, storing instructions |
| Entry declaration | global _start | Export _start as a global symbol; the linker needs it |
| Entry label | _start: | Where the program starts execution |
int 0x80It is the Linux 32-bit system call instruction. It triggers a software interrupt, allowing the kernel to execute the system call we specify through eax. This is the bridge between user programs and the operating system kernel.
Debugging with GDB
Add debug information during compilation, then step through with GDB:
$ nasm -f elf32 -g hello.asm -o hello.o # -g 添加调试信息 $ ld -m elf_i386 hello.o -o hello $ gdb ./hello (gdb) break _start # 在 _start 处设置断点 (gdb) run # 运行程序 (gdb) stepi # 单步执行一条指令 (gdb) info registers # 查看所有寄存器 (gdb) x/s msg # 查看 msg 字符串内容 (gdb) quit # 退出 GDB
| GDB command | Function |
|---|---|
break _start | Set a breakpoint at the _start label |
run | Run the program, stop at breakpoint |
stepi | Execute one machine instruction at a time |
info registers | Display current values of all registers |
x/s 地址 | View content at specified address in string format |
Frequently Asked Questions
If you compile 32-bit assembly on a 64-bit system, you need to install 32-bit compatibility libraries:
sudo apt install gcc-multilib
Other ExtensionsIf ld reports "cannot find entry symbol _start" during linking, check whether you wrote it correctly in your code
global _startand whether the label name exactly matches (note the underscore).