x86-64-assembly
Use when writing x86-64 assembly for Linux — small programs without libc that use raw system calls, parsing text from standard input, integer arithmetic and decimal output, NASM or GNU as with Make, a
SKILL.mdskills/languages/x86-64-assembly/SKILL.md
GitHubx86-64 assembly (Linux, no libc)
Default: NASM (Intel syntax) or GNU as, ld directly with _start as the entry point, System V AMD64 conventions for your own functions, Linux syscalls for I/O, a Makefile with build and test targets.
Minimal program
section .text
global _start
_start:
mov eax, 60 ; sys_exit
xor edi, edi ; status 0
syscall
prog: prog.o ; ld -o $@ $<
prog.o: prog.asm ; nasm -f elf64 -g -F dwarf -o $@ $<
Syscall conventions
| Item | Register |
|---|---|
| Syscall number | rax |
| Arguments 1–6 | rdi, rsi, rdx, r10, r8, r9 |
| Return value | rax (negative = -errno) |
Clobbered by syscall |
rcx, r11 |
Common numbers: read 0, write 1, exit 60. Check them against the kernel's table, not memory.
Reading standard input correctly
readmay return fewer bytes than requested, especially from pipes; loop until it returns 0 (EOF).- Process the buffer as a stream: a number can be split across two reads, so keep parser state (current value, "inside a number") across iterations.
- Treat a negative return as an error and exit non-zero.
Example: summing amounts in text
- Scan bytes; when a digit starts, accumulate
value = value * 10 + (byte - '0'). - Decide which numbers count (e.g. only those followed by a currency marker or on lines with a keyword) and document the rule.
- Add to a 64-bit total; check for overflow with
joafteraddif inputs are untrusted. - Convert the total to decimal by repeated
divby 10 into a buffer filled from the end, then onewrite.
Your own functions
- Follow System V: arguments in
rdi, rsi, rdx, rcx, r8, r9, return inrax; preserverbx, rbp, r12–r15. - Keep
rsp16-byte aligned beforecallif you ever call C code.
Verification loop
- Build with debug info; run under
gdb(layout regs,stepi) for the first versions. - A test script that pipes known inputs (including empty input, input split across lines, very long input) and compares the output and exit status.
strace ./prog < inputshows every syscall and its return value — the fastest way to see a wrong argument.
Pitfalls
- Writing to a 32-bit register zero-extends into the 64-bit one (
mov eax, 1clears the top half); writing 8/16-bit parts does not. divusesrdx:raxas the dividend; clearrdx(xor edx, edx) first or you get a wrong result or a crash.rcxandr11are destroyed bysyscall; do not keep loop counters there.- No libc means no buffered I/O: batch output into a buffer and write once.
- Exit properly: falling off the end of
_startexecutes whatever bytes follow.