Les différences entre les systèmes
Nous pourrions nous poser la question de savoir pourquoi il y a
autant de différences entre les processeurs, l'évolution de ces
derniers étant un facteur important, mais nous nous attarderons
principalement à énumérer ces différences, et à identifier les
systèmes qui ont une configuration particulière.
Taille des mots
Tout d'abord, lorsque l'on souhaite télécharger et installer un
logiciel sur notre ordinateur aujourd'hui, il faut généralement
choisir entre la version 32 bits et la version 64 bits. Cette version
correspond en fait à la taille des mots que notre processeur est
capable de traiter. L'architecture historique
(si l'on peut
qualifier d'histoire une trentaine d'années.) est 32 bits,
c'est-à-dire que la taille des adresses en mémoire et des registres
est de 32 bits. Cette taille correspond également à la taille d'un int
lorsque l'on codera en C. Le fait d'avoir une adresse limitée à 32
bits ne permet d'adresser que 4 Gio
de mémoire vive. En passant à un processeur 64 bits, on peut alors
adresser jusqu'à 16 Eio.
En théorie, un programme compilé sur une machine 32 bits et exécuté sur une machine 64 bits devrait fonctionner tout à fait normalement, car le processeur est capable de passer en mode 32 bits, alors qu'évidement l'inverse n'est pas vraie. Cependant, des exceptions existent et il est préférable d'avoir une version du programme compilée pour la bonne architecture.
Endianness
L'endianness peut devenir un problème lorsque l'on souhaite exporter
un programme vers une architecture spécifique. La plupart du temps,
l'endianness est le même sur les processeurs classiques (x86). Il
définit l'ordre dans lequel un mot de plusieurs octets est stocké en
mémoire. Les deux principaux endianness existant sont le big
endian
et le little endian
.
Little endian
En little endian, c'est l'octet de poids le plus faible qui est stocké en premier, donc pour ce mot de 4 octets : 0x4D78AC20, le tableau représentant la mémoire sera le suivant :
Adresses | 0 | 1 | 2 | 3 |
Données | 20 | AC | 78 | 4D |
Big endian
En big endian, le stockage est effectué avec l'octet de poids fort en premier. En reprenant l'exemple précédent, la représentation de la mémoire serait la suivante :
Adresses | 0 | 1 | 2 | 3 |
Données | 4D | 78 | AC | 20 |
Les processeurs et l'endianness
L'endianness tend à ne plus être problématique, car certains processeurs, comme les ARM ou les intel x86_64 sont capables de fonctionner suivants les deux modes, il faut tout de mêmes le préciser soit au niveau logiciel, soit au niveau matériel. Il faut tout de même se poser la question lorsque par exemple on souhaite exporter un programme compilé sur un processeur x86 (little endian) vers une station SPARC (big endian).
Alignement mémoire
Certains processeurs ne sont capables de lire des mots complets de
32 bits uniquement sur des adresses multiples de 4. C'est le cas des
processeurs ARM, qui ne sont pas capables de lire un int
à
l'adresse 2 :
Adresses | 4 | 5 | 6 | 7 |
Données | AC | 20 | -- | -- |
Adresses | 0 | 1 | 2 | 3 |
Données | -- | -- | 4D | 78 |
Par contre, le processeur est capable de lire un int
s'il
commence à l'adresse 4 :
Adresses | 4 | 5 | 6 | 7 |
Données | 4D | 78 | AC | 20 |
Adresses | 0 | 1 | 2 | 3 |
Données | -- | -- | -- | -- |
L'alignement est donc à prendre en compte lors de la compilation du programme. Si l'on utilise GCC, certaines optimisations peuvent être appliquées pour aligner les données. On peut également tenter de le spécifier dans le programme ( en C) grâce à l'instruction suivante :
int x __attribute__((aligned(4))) = 42;
Cependant, cela demande la réécriture des programmes, alors que GCC contient des options pour la plupart des machines cibles ayant des contraintes d'alignements.
Types signés
Le fait que les types primitifs soient signés ou non pose des
problèmes assez conséquents. Un char
n'a pas la même valeur
si il est signé ou si il ne l'est pas. Par exemple :
unsigned char c1 = 0xFA // donne 250 signed char c2 = 0xFA // donne -6
Jeux d'instructions
Le jeu d'instructions d'un processeur rassemble toutes les commandes qu'il est capable d'exécuter. Ces instructions sont inhérentes au processeur et peuvent changer selon le fabricant. Évidemment, la plupart des fabricants utilisent un jeu d'instruction standard rassemblant les mêmes fonctionnalités et étant codé de la même manière. Ils peuvent néanmoins ajouter des instructions supplémentaires, et lorsqu'un programme utilise ces ajouts, il n'est plus portable vers un système différent.
Les librairies systèmes
Le dernier point problématique lors de l'export d'un programme compilé est évidemment le système d'exploitation qui recevra le programme. Si les deux systèmes sont différents, il est clair que l'export ne fonctionnera pas, car les bibliothèques systèmes pour faire des appels au noyau ne sont pas les mêmes.
Sous Linux, la librairie système la plus répandue est évidemment la libC. Même s'il existe des alternatives telle que µclibC, le fonctionnement reste le même, le support des divers jeux d'instructions des processeurs est effectué par le noyau, la librairie fait simplement le lien entre le noyau et le programme.
Pour Windows, il est difficile de savoir comment les liens sont effectués, on peut seulement connaitre le nom de la bibliothèque C qui est : MSVCRT.DLL