L'alignement de pile avec malloc

Fermé
Mordekeiser Messages postés 156 Date d'inscription samedi 31 octobre 2015 Statut Membre Dernière intervention 3 juillet 2018 - 30 juin 2018 à 11:37
Mordekeiser Messages postés 156 Date d'inscription samedi 31 octobre 2015 Statut Membre Dernière intervention 3 juillet 2018 - 3 juil. 2018 à 11:36
Bonjour,

je voudrais en savoir un peu plus sur l'alignement de pile et l'allocation dynamique, notamment lorsque l'on ne connait pas vraiment à l'avance les valeurs qui seront traitées dans le bloc en cours (j'utile l'assembleur FASM sous x86-64, proche NASM).

Habituellement, lorsque l'on traite des données, on doit soustraire la taille de ces données à la pile, au début du label. Par exemple :

foo:
    push ebp
    mov ebp, esp
    sub ebp 8 ; Car on traite 2 valeurs du type int (4 octets chacune)
    mov dword [ebp-4], 76 ; 1ère valeur
    mov dword [ebp-8], 24 ; 2ème valeur


Je voudrais savoir, dans un premier temps, s'il était possible de soustraire la taille de ces données à la pile au fur et à mesure que l'on traite ces données, quelque chose comme ceci :

foo:
    push ebp
    mov ebp, esp
    sub ebp, 4
    mov dword [ebp-4], 76 ; 1ère valeur
    sub ebp, 4
    mov dword [ebp-8], 24 ; 2ème valeur


Ensuite, comment ferions-nous pour utiliser malloc et l’alignement de pile à bon escient ?
En effet, la taille que permet d'allouer malloc peut être définie de manière non-statique :

cinvoke malloc, SizeToAllocate


Je voudrais donc savoir comment traiter les données depuis l'emplacement définit par malloc avec un alignement de pile adéquat. Si le 2ème code que j'ai donné est valide, j'imagine que l'on peut faire la même chose dans ce cas-ci ?

Je vous remercie !

1 réponse

nicocorico Messages postés 799 Date d'inscription dimanche 19 juin 2011 Statut Membre Dernière intervention 3 juillet 2018 138
3 juil. 2018 à 07:36
Bonjour,

Il est tout à fait possible de soustraire la taille des données au fur et à mesure oui, c'est à dire que dans ce cas on utilise tout simplement l'empilement, à la manière d'un push (si ce n'est qu'il se fait sur esp):
Un push équivaut à:
Sub esp,04
Mov [esp],04

Il faut savoir que le registre esp pointe toujours à l'endroit judicieux concernant le programme en cours (le notre en l'occurence) car s'il y a une interruption il est sauvegardé puis restauré.
C'est à dire que le simple fait d'ajouter 16 au pointeur de pile esp, réserve 16 octets pour notre programme jusqu'à ce qu'on ajoute 16 pour les libérer. C'est exactement ce que fait un Push suivi d'un Pop, ou un Call suivi d'un Ret (l'un sauvegardant l'adresse de retour sur la pile et l'autre la dépilant pour savoir ou revenir).

Donc toute utilisation de pile se fait en réservant une portion mémoire, qu'on libère ensuite dans l'ordre exactement inverse.
Elle s'utilise à l'envers car de cette manière esp pointe systématiquement sur le début du bloc qu'on vient de réserver, sinon il pointerait sur la fin du bloc.

L'utilisation du cadre de pile avec ebp est une convention, en pratique on peut très bien utiliser tout autre registre en cadre de pile.
Le cadre de pile fondé sur ebp à pour intérêt de stabiliser l'adresse du bloc réservé, car esp est modifié par les opérations sur pile tels que les push ou les call, ce qui est vite gênant dans une lognue routine compliquée.
Cependant dans une routine simple le cadre de pile n'a pas vraiment d'intérêt, on peut très bien utiliser esp directement. Par exemple il est parfois utile de sauvegarder une valeur avec un push et l'utiliser à plusieurs reprises, pour ce faire on peut la relire avec (ex: mov eax,[esp] ) sans la dépiler.
Ou bien il est possible de se passer des push et des pops en prévoyant de stocker toutes les valeurs sauvegardées dans un enregistrement sur pile, que l'on réserve en soustrayant sa taille à esp.
Le plus gênant c'est l'appel de fonction, car dans ce cas la fonction appelée ne peut pas accéder aisément aux variables sur pile réservées par la fonction appelante puisque esp a été décalé de -4 ou -8 par le Call.
Sinon l'accès avec cadre de pile se fait ainsi:
push ebp
mov ebp, esp
sub ESP, 8 ; c'est sur esp qu'il faut réserver la taille totale
mov dword [ebp-4], 76 ; 1ère valeur
mov dword [ebp-8], 24 ; 2ème valeur

Par ailleurs on peut très bien faire le mov ebp,esp après ajustement, ainsi on pointe simplement sur l'enregistrement:

push ebp
sub ESP, 8 ; c'est sur esp qu'il faut réserver la taille totale
mov ebp, esp
mov dword [ebp], 76 ; 1ère valeur
mov dword [ebp+04], 24 ; 2ème valeur

J'ai inversé l'ordre des valeurs pour être plus logique)

0
Mordekeiser Messages postés 156 Date d'inscription samedi 31 octobre 2015 Statut Membre Dernière intervention 3 juillet 2018 7
3 juil. 2018 à 11:36
Merci, je comprends mieux les utilisations que l'on peut faire de la pile à présent :)
0