Qu’est-ce qu’un “alignement de stack”?

Qu’est-ce que l’alignement de la stack? Pourquoi est-il utilisé? Peut-il être contrôlé par les parameters du compilateur?

Les détails de cette question proviennent d’un problème rencontré lors de la tentative d’utilisation des bibliothèques ffmpeg avec msvc, mais ce qui m’intéresse vraiment, c’est une explication de ce qu’est “l’alignement de la stack”.

Les détails:

  • Lorsque j’exécute mon programme compatible msvc lié à avcodec, j’obtiens l’erreur suivante: “Le compilateur n’a pas aligné les variables de la stack. Libavcodec a été mal compilé”, suivi d’un plantage dans avcodec.dll.
  • avcodec.dll n’a pas été compilé avec msvc, donc je ne peux pas voir ce qui se passe à l’intérieur.
  • Lors de l’exécution de ffmpeg.exe et en 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 (identique à avcodec.dll)

Merci,

Dan

Alignement des variables en mémoire (un bref historique).

Dans le passé, les ordinateurs avaient un bus de données à 8 bits. Cela signifie que chaque cycle d’horloge 8 bits d’information pourraient être traités. Ce qui était bien alors.

Puis viennent les ordinateurs 16 bits. En raison de la compatibilité descendante et d’autres problèmes, l’octet à 8 bits a été conservé et le mot à 16 bits a été introduit. Chaque mot était 2 octets. Et chaque cycle d’horloge 16 bits d’information pourraient être traités. Mais cela posait un petit problème.

Regardons une carte mémoire:

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

A chaque adresse, il y a un octet auquel on peut accéder individuellement. Mais les mots ne peuvent être récupérés qu’à des adresses égales. Donc, si nous lisons un mot à 0000, nous lisons les octets à 0000 et 0001. Mais si nous voulons lire le mot à la position 0001, nous avons besoin de deux access en lecture. Premièrement 0000,0001 et ensuite 0002 0003 et nous ne conservons que 0001 0002.

Bien sûr, cela a pris du temps et cela n’a pas été apprécié. C’est pourquoi ils ont inventé l’alignement. Nous stockons donc des variables de mot aux limites de mots et aux variables d’octet aux limites d’octet.

Par exemple, si nous avons une structure avec un champ d’octets (B) et un champ de mots (W) (et un compilateur très naïf), nous obtenons les éléments suivants:

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

Ce qui n’est pas amusant Mais en utilisant l’alignement des mots, on trouve:

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

Ici, la mémoire est sacrifiée pour la vitesse d’access.

Vous pouvez imaginer que lorsque vous utilisez un mot double (4 octets) ou un mot quadruplé (8 octets), cela est encore plus important. C’est pourquoi, avec la plupart des compilateurs modernes, vous pouvez choisir l’alignement que vous utilisez lors de la compilation du programme.

Certaines architectures de processeurs nécessitent un alignement spécifique des différents types de données et émettent des exceptions si vous ne respectez pas cette règle. En mode standard, x86 ne l’exige pas pour les types de données de base, mais peut être pénalisé en termes de performances (consultez le site http://www.agner.org pour obtenir des conseils d’optimisation de bas niveau).

Cependant, le jeu d’ instructions SSE (souvent utilisé pour le traitement audio / vidéo hautes performances) a des exigences d’alignement ssortingctes et générera des exceptions si vous tentez de l’utiliser sur des données non alignées (sauf si vous utilisez des versions non alignées beaucoup plus lentes sur certains processeurs) ).

Votre problème est probablement qu’un compilateur attend de l’ appelant qu’il maintienne la stack alignée, tandis que l’autre attend de l’ appelé qu’il aligne la stack si nécessaire.

EDIT : quant à savoir pourquoi l’exception se produit, une routine dans la DLL veut probablement utiliser les instructions SSE sur certaines données de stack temporaires et échoue car les deux compilateurs différents ne sont pas d’accord sur les conventions d’appel.

IIRC, l’alignement de la stack est lorsque les variables sont placées sur la stack “alignées” sur un nombre d’octets particulier. Donc, si vous utilisez un alignement de stack de 16 bits, chaque variable de la stack va commencer à partir d’un octet qui est un multiple de 2 octets du pointeur de stack actuel au sein d’une fonction.

Cela signifie que si vous utilisez une variable de moins de 2 octets, comme un caractère (1 octet), il y aura 8 bits de “remplissage” inutilisé entre cette variable et la variable suivante. Cela permet certaines 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 consiste à les placer sur la stack (au lieu de les placer directement dans des registres). Il est important de savoir si l’alignement est utilisé ou non, car la fonction appelante place les variables sur la stack, pour être lues par la fonction appelante à l’aide de décalages. Si la fonction appelante aligne les variables et que la fonction appelée s’attend à ce qu’elles ne soient pas alignées, 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 comstackr avec toutes les optimisations désactivées.

Pour autant que je sache, les compilateurs n’alignent généralement pas les variables figurant sur la stack. La bibliothèque peut dépendre de certaines options de compilation qui ne sont pas sockets en charge par votre compilateur. La solution normale consiste à déclarer les variables devant être alignées comme étant statiques, mais si vous faites cela dans le code d’autres personnes, vous devez vous assurer que les variables en question sont initialisées ultérieurement dans la fonction plutôt que dans la déclaration.

 // Some comstackrs 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; 

Vous pouvez également rechercher un commutateur de compilation qui aligne les variables sur la stack. Évidemment, la syntaxe d’alignement “__declspec” que j’ai utilisée ici n’est peut-être pas celle que votre compilateur utilise.