qu'est-ce que "l'alignement de la pile"?

Qu'est-ce que l'alignement de la pile? Pourquoi est-il utilisé? Peut-il être contrôlé par les paramètres du compilateur?

les détails de cette question sont tirés d'un problème rencontré lors de l'utilisation de bibliothèques ffmpeg avec msvc, cependant ce qui m'intéresse vraiment est une explication de ce qu'est "l'alignement de la pile".

Les Détails:

  • quand runnig mon programme MSVC conforme qui lie à avcodec je reçois le erreur suivante: "le compilateur n'a pas aligné les variables de la pile. Libavcodec a a été mal composé", suivi d'un crash à avcodec.DLL.
  • avcodec.dll n'a pas été compilé avec msvc, donc je suis incapable de voir ce qui se passe à l'intérieur.
  • Lors de l'exécution de ffmpeg.exe et utilisant le même avcodec.dll, tout fonctionne bien.
  • ffmpeg.exe n'a pas été compilé avec msvc, il a été respecté avec gcc / mingw (comme avcodec.dll)

Merci,

Dan

39
demandé sur Stu Thompson 2009-03-23 10:21:17

4 réponses

alignement des variables en mémoire (une courte histoire).

dans le passé, les ordinateurs disposaient d'une base de données de 8 bits. Cela signifie que chaque cycle d'horloge 8 bits d'information pourraient être traités. Ce qui était bien alors.

puis sont venus les ordinateurs 16 bits. En raison de la compatibilité vers le bas et d'autres problèmes, le octet 8 bits a été conservé et le mot 16 bits a été introduit. Chaque mot était de 2 octets. Et chaque cycle d'horloge 16 bits d'information pourraient être traités. Mais cela pose un petit problème.

regardons une carte mémoire:

+----+
|0000| 
|0001|
+----+
|0002|
|0003|
+----+
|0004|
|0005|
+----+
| .. |

a chaque adresse il y a un byte auquel on peut accéder individuellement. Mais les mots ne peuvent être trouvés qu'à des adresses paires. Donc si on lit un mot à 0000, on lit les octets à 0000 et 0001. Mais si nous voulons lire le mot à la position 0001, nous avons besoin de deux accès. D'abord 0000,00001 et ensuite 0002,00003 et on garde seulement 0001,00002.

bien sûr, cela a pris un peu de temps et qui n'a pas été apprécié. Donc, c'est pourquoi ils ont inventé l'alignement. Nous stockons donc des variables word aux limites des mots et des variables byte aux limites des bytes.

par exemple, si nous avons une structure avec un champ byte (B) et un champ word (W) (et un compilateur très naïf), nous obtenons ce qui suit:

+----+
|0000| B
|0001| W
+----+
|0002| W
|0003|
+----+

Qui n'est pas amusant. Mais en utilisant l'alignement des mots nous trouvons:

+----+
|0000| B
|0001| -
+----+
|0002| W
|0003| W
+----+

ici la mémoire est sacrifiée pour la vitesse d'accès.

vous pouvez imaginer que lorsque vous utilisez le mot double (4 bytes) ou quad word (8 octets) c'est encore plus important. C'est pourquoi la plupart les compilateurs modernes, vous pouvez choisir l'alignement que vous utilisez lors de la compilation du programme.

97
répondu Toon Krijthe 2017-07-19 01:36:56

certaines architectures CPU nécessitent un alignement spécifique de différents types de données, et vont lancer des exceptions si vous n'honorez pas cette règle. En mode standard, x86 ne l'exige pas pour les types de données de base, mais peut subir des pénalités de performance (check www.agner.org pour les conseils d'optimisation de bas niveau).

cependant, le SSE instruction set (souvent utilisé pour haute performance) audio / video procesing a des exigences strictes d'alignement, et lancera des exceptions si vous tentez de utilisez-le sur les données non alignées (à moins que vous n'utilisiez, sur certains processeurs, des versions beaucoup plus lentes non alignées).

votre problème est probablement qu'un compilateur attend le appelant pour garder la pile alignée, tandis que l'autre prévoit appelé pour aligner la pile si nécessaire.

EDIT: en ce qui concerne la raison pour laquelle l'exception se produit, une routine dans la DLL veut probablement utiliser des instructions SSE sur certaines données temporaires de la pile, et échoue parce que les deux compilateurs ne sont pas d'accord pour appeler les conventions.

11
répondu snemarch 2009-03-23 07:45:58

IIRC, l'alignement de la pile est quand les variables sont placées sur la pile "alignées" à un nombre particulier d'octets. Donc, si vous utilisez un alignement de pile de 16 bits, chaque variable sur la pile va commencer à partir d'un byte qui est un multiple de 2 octets du pointeur de pile courant dans une fonction.

Cela signifie que si vous utilisez une variable qui est < 2 octets, comme un char (1 octet), il y aura 8 bits inutilisés "padding" entre elle et la variable suivante. Cela permet à certains des optimisations avec des hypothèses basées sur des emplacements variables.

lors de l'appel de fonctions, une méthode pour passer des arguments à la fonction suivante est de les placer sur la pile (par opposition à les placer directement dans des registres). Que l'alignement soit utilisé ou non ici est important, car la fonction d'appel place les variables sur la pile, pour être lue par la fonction d'appel en utilisant des offsets. Si la fonction d'appel aligne les variables, et la fonction appelée attend d'eux d'être non alignées, alors la fonction appelée ne pourra pas les trouver.

il semble que le code compilé par msvc ne soit pas d'accord sur l'alignement des variables. Essayez de compiler avec toutes les optimisations éteint.

10
répondu Shaun Bouckaert 2009-03-23 16:11:30

autant que je sache, les compilateurs n'alignent généralement pas les variables qui sont sur la pile. La bibliothèque peut dépendre d'un ensemble d'options de compilateur qui n'est pas supporté par votre compilateur. La correction normale est de déclarer les variables qui doivent être alignées comme statiques, mais si vous faites cela dans le code d'autres personnes, vous voudrez être sûr que les variables en question sont initialisées plus tard dans la fonction plutôt que dans la déclaration.

// Some compilers won't align this as it's on the stack...
int __declspec(align(32)) needsToBe32Aligned = 0;
// Change to
static int __declspec(align(32)) needsToBe32Aligned;
needsToBe32Aligned = 0;

en Alternance, trouver un commutateur de compilateur qui aligne les variables sur la pile. Évidemment, la syntaxe d'alignement" _ _ _ declspec " que j'ai utilisée ici n'est peut-être pas ce que votre compilateur utilise.

2
répondu Dan Olson 2009-03-23 07:53:42