Les appels système (syscalls) sous Linux en assembleur x86-64
Convention de registres System V ABI, tableau des numéros et Hello World autonome sans libc en assembleur x86-64.
Écrire en assembleur x86-64 sur GNU/Linux implique de dialoguer directement avec le noyau via des appels système (syscalls), sans passer par la bibliothèque C standard (libc).
Chaque appel système possède un numéro unique placé dans le registre rax, des arguments transmis dans un ordre strict via des registres dédiés, puis l'instruction syscall déclenche la transition vers l'espace noyau.
La convention d'appel System V AMD64 ABI
Sous Linux 64 bits (x86-64), la convention de passage des registres pour un appel système est la suivante :
| Rôle | Registre | Description |
|---|---|---|
| Numéro du syscall | rax |
Identifiant de la fonction noyau à exécuter |
| Argument 1 | rdi |
Premier argument |
| Argument 2 | rsi |
Deuxième argument |
| Argument 3 | rdx |
Troisième argument |
| Argument 4 | r10 |
Quatrième argument (attention : rcx en fonction standard C, mais r10 pour syscall) |
| Argument 5 | r8 |
Cinquième argument |
| Argument 6 | r9 |
Sixième argument |
| Valeur de retour | rax |
Résultat de l'opération (si négatif entre -1 et -4095, il s'agit de -errno) |
Attention aux registres écrasés : L'instruction processeur
syscallmodifie systématiquement les registresrcx(qui sauvegarde l'adresse de retour RIP) etr11(qui sauvegarde les drapeaux RFLAGS).
Tableau des principaux appels système x86-64
| %rax | Nom du Syscall | %rdi (arg1) | %rsi (arg2) | %rdx (arg3) | %r10 (arg4) | %r8 (arg5) | Description |
|---|---|---|---|---|---|---|---|
| 0 | sys_read |
fd |
buf |
count |
- | - | Lit des octets depuis un descripteur de fichier |
| 1 | sys_write |
fd |
buf |
count |
- | - | Écrit des octets vers un descripteur de fichier |
| 2 | sys_open |
filename |
flags |
mode |
- | - | Ouvre un fichier et retourne un descripteur |
| 3 | sys_close |
fd |
- | - | - | - | Ferme un descripteur de fichier ouvert |
| 4 | sys_stat |
filename |
statbuf |
- | - | - | Récupère les métadonnées d'un fichier par son chemin |
| 5 | sys_fstat |
fd |
statbuf |
- | - | - | Récupère les métadonnées d'un fichier par son fd |
| 8 | sys_lseek |
fd |
offset |
whence |
- | - | Déplace le curseur de lecture/écriture |
| 9 | sys_mmap |
addr |
length |
prot |
flags |
fd |
Mappe des fichiers ou de la mémoire anonyme |
| 10 | sys_mprotect |
addr |
length |
prot |
- | - | Modifie les permissions d'une zone mémoire |
| 11 | sys_munmap |
addr |
length |
- | - | - | Libère une zone mémoire mappée |
| 12 | sys_brk |
brk |
- | - | - | - | Ajuste la fin du segment de données (heap) |
| 39 | sys_getpid |
- | - | - | - | - | Retourne le PID du processus courant |
| 41 | sys_socket |
family |
type |
protocol |
- | - | Crée un point de communication réseau |
| 42 | sys_connect |
fd |
uservaddr |
addrlen |
- | - | Établit une connexion vers une adresse distante |
| 43 | sys_accept |
fd |
upeer_sockaddr |
upeer_addrlen |
- | - | Accepte une connexion réseau entrante |
| 57 | sys_fork |
- | - | - | - | - | Crée un nouveau processus fils |
| 59 | sys_execve |
filename |
argv |
envp |
- | - | Exécute un nouveau programme |
| 60 | sys_exit |
error_code |
- | - | - | - | Termine immédiatement le processus |
| 62 | sys_kill |
pid |
sig |
- | - | - | Envoie un signal à un processus |
Exemple complet : Hello World sans libc en NASM
Voici un exemple minimaliste et autonome qui n'utilise aucune bibliothèque externe :
section .data
msg db "Bonjour le monde en assembleur x86-64 !", 0x0A
len equ $ - msg
section .text
global _start
_start:
; 1. Appel sys_write (rax = 1, rdi = 1 (stdout), rsi = msg, rdx = len)
mov rax, 1 ; Numéro du syscall sys_write
mov rdi, 1 ; Descripteur de fichier : 1 = stdout
mov rsi, msg ; Pointeur vers la chaîne
mov rdx, len ; Longueur du message
syscall ; Déclenche l'appel système
; 2. Appel sys_exit (rax = 60, rdi = 0 (code de retour succès))
mov rax, 60 ; Numéro du syscall sys_exit
xor rdi, rdi ; Code de retour 0
syscall
Compilation et exécution :
# Assembler le fichier objet en 64 bits ELF
nasm -f elf64 hello.asm -o hello.o
# Lier le binaire sans libc
ld hello.o -o hello
# Exécuter
./hello
En résumé
- Sous Linux x86-64, le numéro de syscall va toujours dans
raxet l'exécution se déclenche avecsyscall. - La convention passe les arguments dans l'ordre :
rdi,rsi,rdx,r10,r8,r9. - La valeur de retour est renvoyée dans
rax(les codes d'erreurs étant négatifs entre -1 et -4095) ;)