>> Technologie Électronique >  >> Téléphones >> Accessoires pour téléphones

JMP vs CALL en assembleur x86 : La pile fait toute la différence

En assembleur x86, JMP et CALL transfèrent tous deux le contrôle vers une autre adresse, mais leur comportement sur la pile crée des sémantiques de flux de contrôle fondamentalement différentes. Comprendre cette distinction est essentiel pour écrire sous-routines, implémenter récursivité, et déboguer flux contrôle.

Différence fondamentale : Interaction pile

AspectJMP destinationCALL destination
Effet pileAucun — pile inchangéeEmpile adresse retour (EIP/RIP)
Mécanisme retourAucun — transfert aller simpleRET dépile adresse, reprend après CALL
Usage typiqueBoucles, conditionnels, branches inconditionnellesSous-routines, fonctions, procédures
Support imbricationNon — ne peut imbriquer proprementOui — pile permet imbrication arbitraire
RécursivitéImpossible sans gestion pile manuelleSupport natif via frames pile

Encodage instructions (x86-64)

 ; Variantes JMP EB cb JMP rel8 ; Saut court (-128 à +127 octets) E9 cw JMP rel16/32 ; Saut relatif proche FF /4 JMP r/m16/32/64 ; Saut indirect (registre/mémoire) EA cd JMP ptr16:16/32 ; Saut loin (segment:offset) - legacy ; Variantes CALL E8 cw CALL rel16/32 ; Appel relatif proche (empile RIP) FF /2 CALL r/m16/32/64 ; Appel indirect 9A cd CALL ptr16:16/32 ; Appel loin (legacy) 

Mécanique frame pile

Flux exécution CALL

 ; Avant CALL RIP = adresse instruction suivante RSP = pointeur pile actuel ; CALL cible exécute : 1. RSP ← RSP - 8 ; Décrémente pointeur pile (64-bit) 2. [RSP] ← RIP ; Empile adresse retour (instruction suivante) 3. RIP ← cible ; Saute vers sous-routine ; Dans sous-routine (prologue typique) : PUSH RBP ; Sauvegarde base pointer appelant MOV RBP, RSP ; Établit nouveau frame pointer SUB RSP, N ; Alloue espace pile local ; Exécution RET : 1. RIP ← [RSP] ; Dépile adresse retour 2. RSP ← RSP + 8 ; Restaure pointeur pile 3. Exécution reprend après CALL 

Flux exécution JMP

 ; JMP cible exécute : 1. RIP ← cible ; Saut inconditionnel ; Pointeur pile (RSP) inchangé ; Aucune adresse retour stockée ; Pas de retour automatique possible 

Implications pratiques

Pattern sous-routine (CALL/RET)

 ; Appelant CALL calculer_somme ; Empile adresse retour, saute ; ... reprend ici après RET ; Sous-routine calculer_somme: PUSH RBP MOV RBP, RSP ; ... calcul ... MOV RAX, résultat POP RBP RET ; Dépile adresse retour, saute retour 

Pattern boucle/machine états (JMP)

 boucle_etat: CMP AL, 'A' JE traiter_A CMP AL, 'B' JE traiter_B JMP gestionnaire_défaut traiter_A: ; ... traite A ... JMP caractère_suivant traiter_B: ; ... traite B ... JMP caractère_suivant gestionnaire_défaut: ; ... erreur ... JMP caractère_suivant caractère_suivant: ; ... récupère suivant ... JMP boucle_etat ; Reboucle 

Avancé : Optimisation appel terminal

Compilateurs remplacent parfois séquence CALL ... RET par JMP quand dernière action fonction est appel autre fonction :

 ; Sans optimisation foo: CALL bar RET ; Avec optimisation appel terminal (TCO) foo: JMP bar ; Réutilise frame pile appelant 

Avantages : Économie espace pile, retour plus rapide. Requiert : pas nettoyage après appel, même convention appel.

Considérations sécurité

  • Return-Oriented Programming (ROP) : Exploite chaînes RET — CALL crée adresses retour prévisibles
  • Control Flow Integrity (CFI) : Matériel (Intel CET, ARM PAC) valide cibles CALL/RET
  • Shadow Stacks : Pile séparée protégée pour adresses retour (CET)

Résumé

  • JMP = "Va là" (pas retour, pas changement pile)
  • CALL = "Va là, reviens ici" (empile adresse retour, permet RET)
  • CALL/RET = Fondations programmation procédurale, récursivité, code modulaire
  • JMP = Fondations boucles, machines états, flux conditionnel

Chaque appel fonction haut niveau (func() en C, def en Python, method() en Java) compile en machinerie CALL/RET. Chaque while, for, if, goto compile en variantes JMP. La pile est ce qui rend CALL puissant — et ce qui rend débordements tampon dangereux.

Accessoires pour téléphones