Reading — step 1 of 5
Learn
Hello via syscall
A CPU only knows how to move bytes and do arithmetic on them. It has no concept of a screen, a file, or a keyboard — the operating system owns all of that. When your program wants to print text, it has to ask the kernel. On Linux x86_64 that request is a syscall: you put a request number and its arguments into specific registers, execute the syscall instruction, and the CPU traps into the kernel, which does the work and hands control back.
This course uses NASM (the Netwide Assembler) targeting x86_64 Linux. There is no libc and no printf here — every effect your program has on the outside world goes through a syscall.
Anatomy of a NASM program
section .data
msg db "Hello, Assembly!", 10
len equ $ - msg
section .text
global _start
_start:
mov rax, 1 ; syscall number: 1 = write
mov rdi, 1 ; arg 1: file descriptor (1 = stdout)
mov rsi, msg ; arg 2: pointer to the bytes
mov rdx, len ; arg 3: how many bytes
syscall ; trap into the kernel
mov rax, 60 ; syscall number: 60 = exit
xor rdi, rdi ; arg 1: exit status 0
syscall
Piece by piece:
section .dataholds initialized bytes.db("define bytes") lays them out exactly as written: 16 characters, then the raw byte10— the ASCII newline. Assembly has no string type, only bytes you choose to treat as text.len equ $ - msgis computed at assemble time.$means "the address right here", so subtractingmsggives the byte count: 17. Change the message andlenfollows automatically — hardcoding17is the fragile version.section .textholds code.global _startexports the entry label so the linker can see it. The kernel starts you at_start— notmain.mainis a C convention: libc's startup code calls it, and we link no libc.
The syscall convention (memorize this)
rax— the syscall number going in, the return value coming outrdi,rsi,rdx,r10,r8,r9— arguments 1 through 6
Common numbers: 0 = read, 1 = write, 2 = open, 60 = exit.
Two traps hide in this convention:
- The numbers are 64-bit-specific. On 32-bit Linux, write was 4 and exit was 1. On x86_64,
rax = 4isstat— using the wrong table doesn't fail loudly, it just runs a different syscall. If output silently never appears, check the number first. syscallclobbersrcxandr11. The kernel uses them to stash your return address and flags. Anything you kept in them is gone when the syscall returns; every other register survives.
Note what write does not care about: null terminators. It writes exactly rdx bytes starting at address rsi — no more, no less. Pass a length of 0 and it cheerfully writes nothing, reports success, and your program prints nothing at all.
Why exit is not optional
After your last instruction the CPU does not politely stop — it keeps fetching whatever bytes happen to come next in memory and tries to execute them, usually crashing with a segfault. exit (rax = 60, exit status in rdi) is how a process ends on purpose. xor rdi, rdi is the idiomatic way to zero a register — shorter than mov rdi, 0, same effect.
Your exercise
The starter already exits cleanly — but the write call exists only as a comment, so as written it prints nothing, and the grader fails it against the expected output Hello, Assembly! followed by a newline (the newline is already in msg as the trailing , 10). Add the write block before the exit block: rax = 1, rdi = 1, rsi = msg, rdx = len, then syscall. The classic mistake the grader catches: setting rax and rdi but forgetting rdx — write then sends 0 (or garbage) bytes and the diff shows missing text. And keep exit last: put it first and the process dies before it ever prints.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…