Langage d'assemblage - Procédures (Sous-programmes)
Une procédure est un mécanisme du langage assembleur permettant de mettre en œuvre la réutilisation de fonctions/sous-routines, rendant le code structuré et réutilisable. Comprendre la relation entre les procédures et la pile est une étape importante de la programmation en assembleur.
Qu'est-ce qu'une procédure
Une procédureest un bloc de code qui peut être appelé plusieurs fois, semblable aux fonctions ou sous-routines des langages de haut niveau.
En assembleur, une procédure est appelée viaCALLl'instruction CALL, et retourne viaRETl'instruction RET.CALLElle empile l'adresse de retour sur la pile,RETet elle dépile l'adresse de retour de la pile et saute en retour.
Exemple
; Exemple le plus simple d'un appel de procédure
section .data
msg db 'Hello, example!', 0xA
len equ $ - msg
section .text
global _start
_start:
call print_message ; Appeler la procédure (empiler l'adresse de l'instruction suivante sur la pile)
call print_message ; Appeler à nouveau
mov eax, 1
mov ebx, 0
int 0x80
; Procédure : afficher le message
print_message:
push eax ; Sauvegarder les registres à modifier
push ebx
push ecx
push edx
mov eax, 4
mov ebx, 1
mov ecx, msg
mov edx, len
int 0x80
pop edx ; Restaurer les registres (dans l'ordre inverse)
pop ecx
pop ebx
pop eax
ret ; Revenir à l'appelant (dépiler l'adresse de retour de la pile)
À l'intérieur d'une procédure, vous devez sauvegarder et restaurer tous les registres modifiés (sauf EAX lorsqu'il contient la valeur de retour). C'est une bonne pratique élémentaire en programmation assembleur. Si vous ne le faites pas, les valeurs des registres de l'appelant seront modifiées de façon inattendue.
Principes de CALL et RET
CALLetRETIls fonctionnent par paire et s'appuient sur la pile pour gérer les adresses de retour :
| Instruction | Opération réellement effectuée |
|---|---|
CALL label | push eip(Enregistrer l'adresse de l'instruction suivante) +jmp label |
RET | pop eip(Dépiler l'adresse de la pile et sauter) |
Passage de paramètres via les registres
Avant d'appeler une procédure, placez les paramètres dans les registres convenus :
Exemple
; Passage de paramètres via les registres
section .data
newline db 0xA
section .text
global _start
_start:
; Appeler la procédure add_two : calculer 10 + 20
mov eax, 10 ; Placer le premier paramètre dans eax
mov ebx, 20 ; Placer le deuxième paramètre dans ebx
call add_two ; Appeler la procédure
; La valeur de retour est dans eax = 30
mov ebx, eax ; Code de sortie = 30
mov eax, 1
int 0x80
; Procédure : calculer eax + ebx, renvoyer le résultat dans eax
add_two:
add eax, ebx ; eax = eax + ebx
ret
Passage de paramètres via la pile
Le passage de paramètres via la pile est plus flexible et constitue la méthode standard pour les langages de haut niveau tels que C :
Exemple
; Passage de paramètres via la pile (style cdecl)
section .data
msg db 'La somme est : '
msg_len equ $ - msg
newline db 0xA
section .bss
result_buf resb 4
section .text
global _start
_start:
; Appeler la procédure de somme : calculer 100 + 200
push dword 200 ; Empiler le deuxième paramètre
push dword 100 ; Empiler le premier paramètre
call sum ; Appeler la procédure
add esp, 8 ; ★ L'appelant nettoie la pile (convention cdecl)
; La valeur de retour est dans eax = 300
mov ebx, eax
mov eax, 1
int 0x80
; Procédure : sum(a, b) = a + b
sum:
push ebp ; ★ Sauvegarder l'ancien pointeur de base
mov ebp, esp ; ★ Initialiser la nouvelle base du cadre de pile
; Organisation de la pile (de l'adresse haute à l'adresse basse) :
; [ebp+12] = paramètre b (200)
; [ebp+8] = paramètre a (100)
; [ebp+4] = adresse de retour
; [ebp] = ancien ebp
; [ebp-4] = espace de variables locales
mov eax, [ebp + 8] ; Récupérer le premier paramètre (a)
add eax, [ebp + 12] ; Ajouter le deuxième paramètre (b)
pop ebp ; ★ Restaurer l'ancien pointeur de base
ret ; Retour
Schéma de la structure du cadre de pile :
Voici l'illustration textuelle correspondante :
高地址 +------------------+ | 参数2 (200) | <-- [ebp + 12] +------------------+ | 参数1 (100) | <-- [ebp + 8] +------------------+ | 返回地址 | <-- [ebp + 4] +------------------+ | 旧的 EBP | <-- [ebp] (当前 EBP 指向这里) +------------------+ | 局部变量区 | <-- [ebp - 4], [ebp - 8], ... +------------------+ <-- ESP 指向这里 低地址
Prologue de fonction
push ebp; mov ebp, espet Épiloguepop ebp; ret(ouleave; ret) est le modèle standard de gestion des cadres de pile ; presque toutes les fonctions en assembleur commencent et se terminent par cette structure.
Valeurs de retour des procédures
Les valeurs de retour sont généralement stockées dans le registre EAX (valeur 32 bits) ou EDX:EAX (valeur 64 bits) :
Exemple
; Retourne un int
call get_answer
; eax = 42
; Retourne une valeur 64 bits (par ex., long long)
call get_big_value
; edx:eax = résultat 64 bits
get_answer:
mov eax, 42 ; Valeur de retour placée dans eax
ret
get_big_value:
mov eax, 0x00000001 ; 32 bits de poids faible
mov edx, 0x00000000 ; 32 bits de poids fort
ret
Variables locales
Les variables locales utilisent l'espace de pile et sont allouées en décrémentant ESP dans le prologue de la procédure :
Exemple
; Utilisation de variables locales dans une procédure
section .text
global _start
_start:
push dword 10
push dword 20
call multiply_add
add esp, 8
; eax = 10 * 20 + 10 + 20 = 230
mov ebx, eax
mov eax, 1
int 0x80
; Procédure : multiply_add(x, y) = x*y + x + y
multiply_add:
push ebp
mov ebp, esp
sub esp, 8 ; ★ Allouer 8 octets d'espace de variable locale sur la pile
; Maintenant [ebp-4] et [ebp-8] sont des variables locales disponibles
mov eax, [ebp + 8] ; x
mov ebx, [ebp + 12] ; y
; [ebp-4] = x * y
imul eax, ebx
mov [ebp - 4], eax ; Variable locale 1 = x * y
; [ebp-8] = x + y
mov eax, [ebp + 8]
add eax, [ebp + 12]
mov [ebp - 8], eax ; Variable locale 2 = x + y
; Valeur de retour = [ebp-4] + [ebp-8]
mov eax, [ebp - 4]
add eax, [ebp - 8]
; ★ Nettoyer l'espace des variables locales et restaurer
mov esp, ebp ; Équivalent à add esp, 8
pop ebp
ret
Les variables locales sont allouées dans le cadre de pile et automatiquement libérées au retour de la procédure. En utilisant
leavel'instruction peut remplacermov esp, ebp; pop ebp, avec un effet équivalent.
Comparaison des conventions d'appel de procédure
| Convention | Passage de paramètres | Nettoyage de la pile | Préservation des registres | Valeur de retour |
|---|---|---|---|---|
| cdecl | Pile (empilée de droite à gauche) | Appelant | EAX, ECX, EDX sont sauvegardés par l'appelant | EAX |
| stdcall | Pile (empilée de droite à gauche) | Appelé | EAX, ECX, EDX sont sauvegardés par l'appelant | EAX |
| fastcall | ECX, EDX + pile | Appelé | EAX, ECX, EDX sont sauvegardés par l'appelant | EAX |