Skip to content
| Marketplace
Sign in
Visual Studio Code>Other>ArthurNRJ Theme for VSCodeNew to Visual Studio Code? Get it now.
ArthurNRJ Theme for VSCode

ArthurNRJ Theme for VSCode

arthurnrj

|
1 install
| (0) | Free
Says hello when VS Code opens
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Assembly x86-64 - Notes et Corrigés

Les préfixes REP

Préfixe Signification Condition
rep REPeat Répète %rcx fois
repe REPeat while Equal Répète tant que %rcx != 0 ET ZF = 1
repz REPeat while Zero Identique à repe
repne REPeat while Not Equal Répète tant que %rcx != 0 ET ZF = 0
repnz REPeat while Not Zero Identique à repne

Quand utiliser quoi ?

  • rep : opérations inconditionnelles (memset, memcpy)
  • repe/repz : comparaisons qui s'arrêtent sur différence (memcmp, strcmp)
  • repne/repnz : recherche qui s'arrête quand trouvé (strlen, strchr)

Instructions String (STOS, LODS, MOVS, SCAS, CMPS)

Vue d'ensemble

STOS : AL ──────→ (RDI)     "STOre to String"
LODS : (RSI) ──→ AL         "LOaD from String"
MOVS : (RSI) ──→ (RDI)      "MOVe String"
SCAS : AL vs (RDI) → flags  "SCAn String"
CMPS : (RSI) vs (RDI) → flags  "CoMPare Strings"

Suffixes (taille)

Suffixe Taille Registre utilisé Incrément pointeur
b 1 octet %al +1
w 2 octets %ax +2
d 4 octets %eax +4
q 8 octets %rax +8

Mnémotechnique registres

  • %rsi = Source Index (d'où on lit)
  • %rdi = Destination Index (où on écrit)
  • %rcx = Counter (compteur pour rep)
  • %rax = Accumulateur (valeur à stocker/charger)

STOSB en détail (STOre String Byte)

Action : Écrit %al à l'adresse (%rdi), puis incrémente %rdi de 1.

Avant:  %al = 'X',  %rdi = 0x1000
stosb
Après:  mémoire[0x1000] = 'X',  %rdi = 0x1001

Équivalent C :

*rdi = al;
rdi++;

Exemple : memset (remplir un tableau)

memset_rep:
    mov %rdi, %r8       
    mov %rdx, %rcx      
    mov %sil, %al       
    rep stosb           
    mov %r8, %rax
    ret

Trace pour memset(buf, 'A', 3) :

Début:  %rdi=buf, %rcx=3, %al='A'
rep stosb:
  - stosb: buf[0]='A', rdi++, rcx=2
  - stosb: buf[1]='A', rdi++, rcx=1
  - stosb: buf[2]='A', rdi++, rcx=0
  - rcx=0, stop

LODSB en détail (LOaD String Byte)

Action : Lit 1 byte depuis (%rsi) vers %al, puis incrémente %rsi de 1.

Avant:  mémoire[0x1000] = 'H',  %rsi = 0x1000
lodsb
Après:  %al = 'H',  %rsi = 0x1001

Équivalent C :

al = *rsi;
rsi++;

Exemple : parcourir une chaîne

print_chars:
    mov %rdi, %rsi
.loop:
    lodsb
    test %al, %al
    jz .done
    
    call print_char
    jmp .loop
.done:
    ret

MOVSB en détail (MOVe String Byte)

Action : Copie 1 byte de (%rsi) vers (%rdi), puis incrémente les deux.

Avant:  mémoire[0x1000] = 'X',  %rsi = 0x1000,  %rdi = 0x2000
movsb
Après:  mémoire[0x2000] = 'X',  %rsi = 0x1001,  %rdi = 0x2001

Équivalent C :

*rdi = *rsi;
rsi++;
rdi++;

Exemple : memcpy

memcpy_rep:
    mov %rdi, %rax
    mov %rdx, %rcx
    rep movsb
    ret

Trace pour memcpy(dest, src, 3) :

Début:  %rsi=src, %rdi=dest, %rcx=3
rep movsb:
  - movsb: dest[0]=src[0], rsi++, rdi++, rcx=2
  - movsb: dest[1]=src[1], rsi++, rdi++, rcx=1
  - movsb: dest[2]=src[2], rsi++, rdi++, rcx=0
  - rcx=0, stop

SCASB en détail (SCAn String Byte)

Action : Compare %al avec (%rdi), met à jour les flags, puis incrémente %rdi.

Avant:  %al = 'X',  mémoire[0x1000] = 'X',  %rdi = 0x1000
scasb
Après:  ZF = 1 (égaux),  %rdi = 0x1001

Équivalent C :

flags = compare(al, *rdi);
rdi++;

Exemple : strlen avec repne scasb

strlen_rep:
    mov %rdi, %rsi
    xor %al, %al
    mov $-1, %rcx
    repne scasb
    not %rcx
    dec %rcx
    mov %rcx, %rax
    ret

Explication strlen :

  1. %al = 0 (on cherche le '\0')
  2. %rcx = -1 (0xFFFFFFFFFFFFFFFF, quasi-infini)
  3. repne scasb : répète tant que *rdi != al ET rcx != 0
    • Quand on trouve '\0', ZF=1, la boucle s'arrête
  4. not %rcx : inverse les bits (rcx était décrémenté à chaque itération)
  5. dec %rcx : enlève 1 (le '\0' ne compte pas)

CMPSB en détail (CoMPare String Byte)

Action : Compare (%rsi) avec (%rdi), met à jour les flags, incrémente les deux.

Avant:  src[0]='A', dest[0]='A',  %rsi=src, %rdi=dest
cmpsb
Après:  ZF = 1 (égaux),  %rsi++,  %rdi++

Équivalent C :

flags = compare(*rsi, *rdi);
rsi++;
rdi++;

Exemple : memcmp

memcmp_rep:
    mov %rdx, %rcx
    repe cmpsb
    jne .not_equal
    xor %eax, %eax
    ret
.not_equal:
    movzbl -1(%rsi), %eax
    movzbl -1(%rdi), %ecx
    sub %ecx, %eax
    ret

Explication memcmp :

  1. repe cmpsb : répète tant que *rsi == *rdi ET rcx != 0
  2. Si différence trouvée → ZF=0, sort de la boucle
  3. -1(%rsi) car rsi a été incrémenté après la comparaison

Récapitulatif avec REP

Combo Usage S'arrête quand
rep stosb memset rcx = 0
rep movsb memcpy rcx = 0
rep lodsb rare rcx = 0
repe cmpsb memcmp rcx = 0 OU différence
repe scasb rare rcx = 0 OU différence
repne scasb strlen/strchr rcx = 0 OU trouvé
repne cmpsb rare rcx = 0 OU égalité

Syscalls Linux x86-64

Syscall Numéro rdi rsi rdx
read 0 fd buf count
write 1 fd buf count
open 2 path flags mode
close 3 fd - -
lseek 8 fd offset whence
exit 60 code - -

lseek whence :

  • SEEK_SET = 0 : depuis le début
  • SEEK_CUR = 1 : depuis position actuelle
  • SEEK_END = 2 : depuis la fin

_start et argv :

(%rsp)     = argc
8(%rsp)    = argv[0]
16(%rsp)   = argv[1]
...

Corrigés d'exercices


plusminus

Prototype : int64_t plusminus(int64_t a, int64_t b, int64_t c)

Retourne : a + b - c

.text
.global plusminus

plusminus:
    movq %rdi, %rax     # rax = a (1er argument)
    addq %rsi, %rax     # rax = a + b (2ème argument)
    subq %rdx, %rax     # rax = a + b - c (3ème argument)
    ret                 # retourne rax

Ligne par ligne : | Ligne | Instruction | Explication | |-------|-------------|-------------| | 1 | movq %rdi, %rax | Copie a dans le registre de retour | | 2 | addq %rsi, %rax | Ajoute b au résultat | | 3 | subq %rdx, %rax | Soustrait c du résultat | | 4 | ret | Retourne (résultat dans %rax) |


memset_rep

Prototype : char *memset_rep(char *array, char value, size_t size)

Retourne : Le pointeur array original

.text
.global memset_rep

memset_rep:
    mov %rdi, %r8       # sauvegarde le pointeur original dans r8
    mov %rdx, %rcx      # rcx = size (compteur pour rep)
    mov %sil, %al       # al = value (byte à écrire)
    rep stosb           # répète: écrit al à (rdi), rdi++, rcx--
    mov %r8, %rax       # retourne le pointeur original
    ret

Ligne par ligne : | Ligne | Instruction | Explication | |-------|-------------|-------------| | 1 | mov %rdi, %r8 | Sauvegarde array car rep stosb va modifier %rdi | | 2 | mov %rdx, %rcx | Met size dans %rcx (compteur pour rep) | | 3 | mov %sil, %al | Met value dans %al (stosb utilise %al) | | 4 | rep stosb | Écrit %al à (%rdi) size fois, incrémente %rdi à chaque fois | | 5 | mov %r8, %rax | Met le pointeur original sauvegardé dans %rax pour le retour | | 6 | ret | Retourne |

Pourquoi %r8 ?

  • %rdi est modifié par rep stosb
  • %rax serait corrompu par mov %sil, %al (écrase le byte bas)
  • %r8 est un registre volatile libre qu'on peut utiliser comme scratch

endian_sum

Prototype : int32_t endian_sum(int32_t *arr, size_t n)

Retourne : La somme des éléments avec swap d'endianness

.text
.global endian_sum

endian_sum:
    xor %rax, %rax      # sum = 0
.loop:
    cmp $0, %rsi        # compare n avec 0
    je .end             # si n == 0, fin
    dec %rsi            # n--
    movl (%rdi), %edx   # edx = arr[i] (charge 4 bytes)
    bswap %edx          # inverse les bytes (endianness swap)
    addl %edx, %eax     # sum += edx
    add $4, %rdi        # avance le pointeur de 4 bytes (sizeof int32_t)
    jmp .loop           # continue la boucle
.end:
    ret                 # retourne sum (dans eax/rax)

Ligne par ligne : | Ligne | Instruction | Explication | |-------|-------------|-------------| | 1 | xor %rax, %rax | Initialise la somme à 0 | | 2 | cmp $0, %rsi | Compare le compteur n avec 0 | | 3 | je .end | Si n == 0, sort de la boucle | | 4 | dec %rsi | Décrémente le compteur | | 5 | movl (%rdi), %edx | Charge l'entier 32-bit pointé par %rdi | | 6 | bswap %edx | Inverse l'ordre des 4 bytes (little↔big endian) | | 7 | addl %edx, %eax | Ajoute la valeur swappée à la somme | | 8 | add $4, %rdi | Avance le pointeur au prochain int32_t | | 9 | jmp .loop | Retourne au début de la boucle | | 10 | ret | Retourne la somme |

Note sur bswap :

Avant: edx = 0x12345678
bswap %edx
Après: edx = 0x78563412

my_atoui

Prototype : uint64_t my_atoui(const char *str)

Retourne : La valeur numérique de la chaîne

.text
.global my_atoui

my_atoui:
    xor %rax, %rax      # result = 0
.loop:
    xor %ecx, %ecx      # clear ecx (pour zero-extend)
    movb (%rdi), %cl    # cl = *str (charge 1 caractère)
    sub $'0', %cl       # cl = cl - '0' (convertit ASCII en digit)
    cmp $9, %cl         # compare avec 9
    ja .end             # si > 9 (unsigned), pas un chiffre, fin
    imulq $10, %rax     # result *= 10
    add %rcx, %rax      # result += digit
    inc %rdi            # str++ (caractère suivant)
    jmp .loop           # continue
.end:
    ret                 # retourne result

Ligne par ligne : | Ligne | Instruction | Explication | |-------|-------------|-------------| | 1 | xor %rax, %rax | result = 0 | | 2 | xor %ecx, %ecx | Met %rcx à 0 (zero-extend implicite) | | 3 | movb (%rdi), %cl | Charge le caractère courant dans %cl | | 4 | sub $'0', %cl | Convertit ASCII '0'-'9' en valeur 0-9 | | 5 | cmp $9, %cl | Compare avec 9 | | 6 | ja .end | Si > 9 (unsigned), ce n'est pas un chiffre | | 7 | imulq $10, %rax | Multiplie le résultat par 10 | | 8 | add %rcx, %rax | Ajoute le nouveau chiffre | | 9 | inc %rdi | Passe au caractère suivant | | 10 | jmp .loop | Continue la boucle | | 11 | ret | Retourne le résultat |

Astuce ja (Jump if Above) :

  • Si le caractère < '0' : après sub, cl devient négatif = très grand en unsigned
  • Si le caractère > '9' : après sub, cl > 9
  • Dans les deux cas, ja (comparaison unsigned) saute → fin

fibo_loop

Prototype : uint32_t fibo_loop(uint32_t n)

Retourne : Le n-ième nombre de Fibonacci (F0=0, F1=1, F2=1, F3=2...)

.text
.globl fibo_loop

fibo_loop:
    cmp $0, %edi        # compare n avec 0
    je .ret_zero        # si n == 0, retourne 0
    cmp $1, %edi        # compare n avec 1
    je .ret_one         # si n == 1, retourne 1
    
    mov $0, %eax        # a = F(0) = 0
    mov $1, %ecx        # b = F(1) = 1
    mov $2, %edx        # i = 2 (on commence à F(2))
    
.loop:
    mov %ecx, %esi      # temp = b
    add %eax, %ecx      # b = a + b (nouveau Fibonacci)
    mov %esi, %eax      # a = temp (ancien b)
    inc %edx            # i++
    cmp %edi, %edx      # compare i avec n
    jbe .loop           # si i <= n, continue
    
    mov %ecx, %eax      # retourne b (le dernier Fibonacci calculé)
    ret

.ret_zero:
    mov $0, %eax        # retourne 0
    ret

.ret_one:
    mov $1, %eax        # retourne 1
    ret

Ligne par ligne : | Ligne | Instruction | Explication | |-------|-------------|-------------| | 1-2 | cmp $0, %edi / je .ret_zero | Cas spécial : F(0) = 0 | | 3-4 | cmp $1, %edi / je .ret_one | Cas spécial : F(1) = 1 | | 5 | mov $0, %eax | a = 0 (F(i-2)) | | 6 | mov $1, %ecx | b = 1 (F(i-1)) | | 7 | mov $2, %edx | i = 2 (compteur) | | 8 | mov %ecx, %esi | Sauvegarde b dans temp | | 9 | add %eax, %ecx | b = a + b (calcule F(i)) | | 10 | mov %esi, %eax | a = temp (ancien b devient nouveau a) | | 11 | inc %edx | i++ | | 12 | cmp %edi, %edx | Compare i avec n | | 13 | jbe .loop | Si i <= n, continue | | 14 | mov %ecx, %eax | Met le résultat dans %eax pour le retour |

Trace pour n=5 (attendu: 5) :

Début: a=0, b=1, i=2
i=2: temp=1, b=0+1=1, a=1, i=3  → F(2)=1
i=3: temp=1, b=1+1=2, a=1, i=4  → F(3)=2
i=4: temp=2, b=1+2=3, a=2, i=5  → F(4)=3
i=5: temp=3, b=2+3=5, a=3, i=6  → F(5)=5
i=6 > n=5, exit, return b=5 ✓

facto_rec

Prototype : uint64_t facto_rec(uint8_t n)

Retourne : n! (factorielle de n)

.text
.globl facto_rec

facto_rec:
    movzbq %dil, %rdi   # zero-extend: rdi = (uint64_t)n
    cmp $0, %dil        # compare n avec 0
    je .base_case       # si n == 0, retourne 1
    push %rdi           # sauvegarde n sur la pile
    dec %rdi            # rdi = n - 1
    call facto_rec      # appel récursif: rax = facto_rec(n-1)
    pop %rdi            # récupère n depuis la pile
    imulq %rdi, %rax    # rax = n * facto_rec(n-1)
    ret

.base_case:
    mov $1, %rax        # 0! = 1
    ret

Ligne par ligne : | Ligne | Instruction | Explication | |-------|-------------|-------------| | 1 | movzbq %dil, %rdi | Zero-extend le byte n en 64-bit | | 2 | cmp $0, %dil | Compare n avec 0 | | 3 | je .base_case | Si n == 0, va au cas de base | | 4 | push %rdi | Sauvegarde n (sera écrasé par l'appel récursif) | | 5 | dec %rdi | n = n - 1 pour l'appel récursif | | 6 | call facto_rec | Appel récursif → résultat dans %rax | | 7 | pop %rdi | Récupère notre n sauvegardé | | 8 | imulq %rdi, %rax | rax = n * facto_rec(n-1) | | 9 | ret | Retourne le résultat | | 10-11 | .base_case | 0! = 1 |

Pourquoi push %rdi / pop %rdi ?

  • %rdi est un registre caller-saved (volatile)
  • Après call facto_rec, %rdi peut avoir changé
  • On doit sauvegarder n pour le multiplier après

Trace pour n=4 (attendu: 24) :

facto_rec(4):
  push 4
  call facto_rec(3):
    push 3
    call facto_rec(2):
      push 2
      call facto_rec(1):
        push 1
        call facto_rec(0):
          return 1
        pop 1, rax = 1 * 1 = 1
      pop 2, rax = 2 * 1 = 2
    pop 3, rax = 3 * 2 = 6
  pop 4, rax = 4 * 6 = 24
return 24 ✓

print_fibo

Prototype : void print_fibo(uint32_t n)

Affiche : fibo(n): F(n)\n

.section .rodata
format: .string "fibo(%u): %u\n"

.text
.global print_fibo

print_fibo:
    push %rbx           # sauvegarde rbx (callee-saved)
    push %r12           # sauvegarde r12 (callee-saved)

    mov %edi, %r12d     # r12 = n (sauvegarde pour printf)

    xor %eax, %eax      # a = 0 (F(0))
    mov $1, %ebx        # b = 1 (F(1))

    test %edi, %edi     # teste si n == 0
    jz .print           # si n == 0, résultat = 0 (déjà dans eax)

    dec %edi            # n--
    jz .use_b           # si n était 1, résultat = b = 1

.loop:
    mov %ebx, %ecx      # temp = b
    add %eax, %ebx      # b = a + b
    mov %ecx, %eax      # a = temp
    dec %edi            # n--
    jnz .loop           # continue tant que n > 0

.use_b:
    mov %ebx, %eax      # résultat = b

.print:
    mov %eax, %edx      # 3ème arg printf = fibo(n)
    mov %r12d, %esi     # 2ème arg printf = n original
    lea format(%rip), %rdi  # 1er arg printf = format string
    xor %eax, %eax      # al = 0 (pas d'args vectoriels)
    call printf@PLT     # appelle printf

    pop %r12            # restaure r12
    pop %rbx            # restaure rbx
    ret

Ligne par ligne : | Ligne | Instruction | Explication | |-------|-------------|-------------| | 1-2 | push %rbx/r12 | Sauvegarde les registres callee-saved qu'on utilise | | 3 | mov %edi, %r12d | Garde n original pour l'afficher après | | 4-5 | xor/mov | Initialise a=0, b=1 | | 6-7 | test/jz | Si n==0, saute directement à print (résultat=0) | | 8-9 | dec/jz | Si n==1, saute à .use_b (résultat=1) | | 10-14 | .loop | Calcule Fibonacci itérativement | | 15 | mov %ebx, %eax | Met le résultat dans %eax | | 16-19 | .print | Prépare les arguments pour printf | | 20 | call printf@PLT | Appelle printf (PLT pour le linking dynamique) | | 21-22 | pop | Restaure les registres sauvegardés |

Pourquoi lea format(%rip), %rdi ?

  • C'est du Position Independent Code (PIC)
  • %rip = adresse de l'instruction courante
  • Nécessaire pour les shared libraries et ASLR

Pièges courants

  1. Utiliser %edi au lieu de %rdi pour les pointeurs

    • Les pointeurs sont 64-bit, %edi tronque à 32-bit
  2. Oublier que mov %sil, %al écrase le byte bas de %rax

    • Si %rax contenait un pointeur, il est corrompu
  3. cmpb avec un registre 64-bit

    • cmpb $0, %rsi est invalide, utiliser test %rsi, %rsi
  4. Mauvais incrément dans les boucles

    • int32_t = 4 octets → add $4, %rdi
    • int64_t = 8 octets → add $8, %rdi
  5. Alignement de la pile avant call

    • RSP doit être aligné à 16 octets - 8 avant le call
    • Compter les push : nombre impair = OK, pair = ajouter sub $8, %rsp
  6. Oublier de sauvegarder les registres callee-saved

    • %rbx, %rbp, %r12-%r15 doivent être préservés
    • Si tu les utilises, push au début et pop à la fin
  7. Registres écrasés par un call

    • %rdi, %rsi, %rdx, %rcx, %r8, %r9, %r10, %r11 peuvent changer
    • Sauvegarde-les si tu en as besoin après le call
  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft