2009-06-30 8 views
40

J'ai essayé de mieux comprendre comment les compilateurs génèrent du code machine, et plus précisément comment GCC gère la pile. Ce faisant, j'ai écrit des programmes C simples, les compilant en assemblée et faisant de mon mieux pour comprendre le résultat. Voici un programme simple et la sortie qu'il génère:Allocation de pile, remplissage et alignement

asmtest.c:

void main() { 
    char buffer[5]; 
} 

asmtest.s:

pushl %ebp 
movl %esp, %ebp 
subl $24, %esp 
leave 
ret 

Ce qui est déconcertant pour moi est la raison pour laquelle 24 octets sont alloués pour la pile. Je sais qu'en raison de la façon dont le processeur adresse la mémoire, la pile doit être allouée par incréments de 4, mais si c'était le cas, nous devrions seulement déplacer le pointeur de pile de 8 octets, pas 24. Pour mémoire, un tampon de 17 octets produit un pointeur de pile déplacé 40 octets et aucun tampon du tout déplace le pointeur de pile 8. Un tampon entre 1 et 16 octets inclus déplace ESP 24 octets. Maintenant que nous supposons que les 8 octets sont une constante nécessaire (que faut-il pour?), Cela signifie que nous allouons en morceaux de 16 octets. Pourquoi le compilateur s'alignerait-il de cette manière? J'utilise un processeur x86_64, mais même un mot 64 bits ne devrait nécessiter qu'un alignement de 8 octets. Pourquoi la divergence?

Pour référence, je compile ceci sur un Mac fonctionnant sous 10.5 avec gcc 4.0.1 et aucune optimisation activée.

Répondre

43

Il s'agit d'une fonctionnalité gcc contrôlée par -mpreferred-stack-boundary=n où le compilateur essaie de maintenir les éléments de la pile alignés sur 2^n. Si vous avez changé n en 2, il n'allouerait que 8 octets sur la pile. La valeur par défaut pour n est 4, c'est-à-dire qu'elle va essayer de s'aligner sur des limites de 16 octets.

Pourquoi il y a le « défaut » 8 octets puis 24 = 8 + 16 octets est parce que la pile contient déjà 8 octets pour leave et ret, de sorte que le code compilé doit régler la pile d'abord par 8 octets pour l'obtenir aligné sur 2^4 = 16.

+0

fait "push% ebp" fait esp diminué de 8 octets? plus 8 octets de ret, il devrait déjà être aligné avec 16 octets. Pourquoi le compilateur de dose a besoin de 8 octets supplémentaires? –

+1

oh, j'ai compris. C'est un machince 32 bits. Pardon.Il doit être rét 4 octets + ebp 4 octets + alignés 8 octets + tampon 16 –

+1

Les versions actuelles des ABI System V i386 et x86-64 nécessitent un alignement de pile 16B (avant une instruction 'call'), donc les fonctions sont supposées assumer cette. Historiquement, l'i386 ABI ne nécessitait qu'un alignement 4B. (Voir https://stackoverflow.com/tags/x86/info pour les liens vers les documents ABI). GCC maintient également '% esp' aligné même dans les fonctions de feuille (qui n'appellent pas d'autres fonctions), quand il doit réserver de l'espace, et c'est ce qui se passe ici. –

3

J'ai trouvé this site, qui a une explication décente au bas de la page sur les raisons pour lesquelles la pile pourrait être plus grande. Mettre à l'échelle le concept jusqu'à une machine 64 bits et cela pourrait expliquer ce que vous voyez.

-1

Les 8 octets sont là parce que la première instruction pousse la valeur de départ de% ebp sur la pile (en supposant 64 bits).

+1

L'adresse de retour et le pointeur de base sont tous deux poussés sur la pile. – dreamlax

11

La famille d'instructions SSEx EXIGE que les vecteurs de 128 bits compressés soient alignés sur 16 octets. Dans le cas contraire, une erreur de segmentation tente de les charger/stocker. C'est à dire. Si vous souhaitez transmettre en toute sécurité des vecteurs de 16 octets à utiliser avec SSE sur la pile, la pile doit être régulièrement alignée sur 16. GCC en tient compte par défaut.

+0

J'ai peut-être trop peu d'expérience pour affirmer que votre réponse est fausse. Mais n'utilisez-vous pas des instructions 'movupd' et des instructions similaires ** ** alignées exactement à cet effet (chargement/stockage de _unaligned_ packed data)? D'après ce que je comprends, vous pouvez obtenir un comportement erroné lorsque vous essayez d'utiliser 'movapd' et des instructions similaires sur des données non alignées, mais les données non alignées ne devraient pas poser de problème en général. – andreee

1

L'ABI Mac OS X/Darwin x86 nécessite un alignement de pile de 16 octets. Ce n'est pas le cas sur d'autres plates-formes x86 telles que Linux, Win32, FreeBSD ...

+1

L'exigence ABI réelle est que la pile soit alignée sur 16 octets * aux limites d'appel de fonction *. –

+2

Ceci est vrai, mais comme les prologues/épilogues de fonction concernent les seuls endroits où le pointeur de pile est modifié, cela revient presque à dire qu'il doit être aligné à tout moment. – Ringding