ELF signifie format de fichier exécutable et pouvant être lié.
ELF est utilisé comme format de fichier standard pour les fichiers objets sous Linux. Auparavant, le format de fichier a.out était utilisé comme standard, mais dernièrement, ELF a pris le relais en tant que standard.
ELF prend en charge :
- Différents processeurs
- Encodage de données différent
- Différentes classes de machines
Cet article explique les différents types de fichiers d'objet ELF et d'en-tête ELF.
Fichiers objet ELF
Un fichier contenant du code compilé est appelé fichier objet. Un fichier objet peut être de l'un des types suivants :
1. Fichier déplaçable
Ce type de fichier objet contient des données et du code qui peuvent être liés à d'autres fichiers transférables pour produire un binaire exécutable ou un fichier objet partagé. En termes simples, un fichier relocalisable est identique au fichier .o produit lorsque nous compilons un code de la manière suivante :
gcc -Wall -c test.c -o test.o
Ainsi, le test.o produit après l'opération ci-dessus serait un fichier relocalisable.
2. Fichier objet partagé
Ce type de fichier objet est utilisé par l'éditeur de liens dynamique pour le combiner avec l'exécutable et/ou d'autres fichiers objet partagés afin de créer une image de processus complète. En termes simples, un fichier objet partagé est identique au fichier .so produit lorsque le code est compilé avec le drapeau -fPIC de la manière suivante :
gcc -c -Wall -Werror -fPIC shared.c gcc -shared -o libshared.so shared.o
Une fois les deux commandes ci-dessus effectuées, un fichier objet partagé libshared.o est produit en sortie.
REMARQUE :Pour en savoir plus sur les bibliothèques partagées Linux, consultez notre article Bibliothèques partagées Linux
3. Fichier exécutable
Ce type de fichier objet est un fichier capable d'exécuter un programme lorsqu'il est exécuté. En termes simples, il s'agit de la sortie de commandes comme celle-ci :
gcc -Wall test.c -o test
Ainsi, la sortie "test" serait un exécutable qui, lorsqu'il serait exécuté, exécuterait la logique écrite dans le fichier test.c.
Ainsi, comme on peut le déduire des types de fichiers objets ci-dessus, un fichier objet participe de la construction du programme à son exécution ou nous pouvons dire de la liaison à l'étape d'exécution (pour en savoir plus sur les étapes de compilation Linux/GCC, reportez-vous à notre article Journey of un programme C).
En-tête ELF
Tous les fichiers objets décrits ci-dessus sont de type ELF sous Linux.
Cela peut facilement être prouvé en examinant chacun de ces fichiers. Par exemple, j'ai examiné chacun des trois fichiers de mon système :
Fichier réadressable :
$ vim func.o ^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^A^@>^@^A^@^@^@^@^@^@^@^@^@^@^@ ^@UHå¿^@^@^@^@è^@^@^@^@¸^@^@^@^@è^@^@^@^@ÉÃ^@^@ Inside func()^@^@GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3^@^T^@^@^@^@ ^@^@^@^AzR^@^Ax^P^A^[^L^G^H^A^@^@^\^@^.symtab^@.strtab^@.shstrtab^ @.rela.text^@.data^@.bss^@.rodata^@.comment^@.note.GNU-stack^@.rela.eh_frame ^@^@^@^@^@^@^@^@^@^@^@^@^@
Fichier objet partagé :
$ vim libshared.so ^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^C^@>^@^A^@^@^@ ^@^@^@^@^@^A^@^@^@^F^@^ ^@^@^@^@^@è^A^@^@^@^@^@^@è^A^@^@^@^@^@^@^A^@^@^@^@^@^@^@^D^@^@^@^T^@^@^ @^C^@^@^@GNU^@·YG®z^L^ZÊ7uÈí,?^N^@^@^@^@^C^@^@^@^L^@^@^@
Fichier exécutable :
$ vim test ^?ELF^B^A^A^@^@^@^@^@^@^@^@^@^B^@>^@^A^@^@^@P@^@^@^@^@^@@^@^@^@^@^@< ^@^@^@D^@^@^^B^@^@^@^@^@^@^A^@^@^@^@^@^@^@/lib64/ld-linux-x86-64.so.2^@^D^ @^@^@^D^@^@^@^T^@^@^@^C^@^@^@GNU^@òÁ}CKbE;ära`6"^O^N\^C^@^@^@
Nous voyons donc que, comme il s'agit de fichiers binaires, rien de plus n'est compréhensible à l'exception de la chaîne ELF au début de chaque fichier. Cela montre que ces fichiers sont uniquement au format ELF.
Chaque fichier commence par un en-tête ELF qui indique à peu près l'organisation complète du fichier. Par exemple, les fichiers d'objets réadressables et partagés contiennent des sections, mais à l'autre extrémité, le fichier exécutable est composé de segments. Ainsi, selon le type de fichier objet, l'en-tête ELF donne des informations détaillées sur le fichier.
Généralement dans le cas de fichiers exécutables, un en-tête ELF est suivi d'une table d'en-tête de programme. Une table d'en-tête de programme aide à créer une image de processus. Puisqu'il aide à créer une image de processus (qui est créée après l'exécution de l'exécutable), la table d'en-tête du programme devient obligatoire pour les fichiers exécutables mais est facultative pour les fichiers d'objets transférables et partagés.
Voici l'organisation de l'en-tête ELF :
#define EI_NIDENT 16 typedef struct { e_ident[EI_NIDENT]; unsigned char e_type; Elf32_Half e_machine; Elf32_Half e_version; Elf32_Word e_entry; Elf32_Addr e_phoff; Elf32_Off e_shoff; Elf32_Off e_flags; Elf32_Word e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shstrndx; } Elf32_Ehdr;
On voit donc que l'organisation présentée ci-dessus se présente sous la forme d'une structure. Expliquer chaque membre en détail ici rendrait les choses complexes, alors passons en revue la signification de base et les informations détenues par chaque membre de cette structure pour avoir une idée de ce domaine particulier.
1. e_ident
Comme nous le savons déjà, le format ELF prend en charge différentes classes de machines, de processeurs, etc. Ainsi, afin de prendre en charge tout cela, les informations initiales du fichier ELF contiennent des informations sur la manière d'interpréter le fichier indépendamment du processeur sur lequel l'exécutable est exécuté. Le tableau ‘e_ident’ fournit exactement les mêmes informations :
Name Value Purpose EI_MAG 0 File identification EI_MAG1 1 File identification EI_MAG2 2 File identification EI_MAG3 3 File identification EI_CLASS 4 File class EI_DATA 5 Data encoding EI_VERSION 6 File version EI_PAD 7 Start of padding bytes EI_NIDENT 16 Size of e_ident[]
- EI_MAG Les quatre premiers octets ci-dessus contiennent le nombre magique "0x7fELF".
- EI_CLASS Un ELF peut avoir deux classes, 32 bits ou 64 bits. Cela rend le format de fichier portable.
- EI_DATA Ce membre donne les informations sur l'encodage des données. En termes simples, ces informations indiquent si les données sont au format big endian ou little endian.
- EI_VERSION Ce membre fournit des informations sur la version du fichier objet.
- EI_PAD Ce membre marque le début des octets inutilisés dans le tableau d'informations e_indent.
- EI_NIDENT Ce membre fournit la taille du tableau e_indent. Cela aide à analyser le fichier ELF.
2. e_type
Ce membre identifie le type de fichier objet. Par exemple, un fichier objet peut être des types suivants :
Name Value Meaning ET_NONE 0 No file type ET_REL 1 Relocatable file ET_EXEC 2 Executable file ET_DYN 3 Shared object file ET_CORE 4 Core file
REMARQUE :La liste ci-dessus n'est pas exhaustive, mais donne tout de même des informations sur les principaux types de fichiers objets auxquels ELF peut se référer.
3. e_machine
Ce membre donne des informations sur l'architecture requise par un fichier ELF.
Name Value Meaning ET_NONE 0 No machine EM_M32 1 AT&T WE 32100 EM_SPARC 2 SPARC EM_386 3 Intel Architecture EM_68K 4 Motorola 68000 EM_88K 5 Motorola 88000 EM_860 7 Intel 80860 EM_MIPS 8 MIPS RS3000 Big-Endian EM_MIPS_RS4_BE 10 MIPS RS4000 Big-Endian RESERVED 11-16 Reserved for future use
4. Membres supplémentaires
Outre les trois membres ci-dessus, il compte également les membres suivants :
- e_version :ce membre fournit les informations de version du fichier objet ELF.
- e_entry :ce membre fournit les informations d'adresse virtuelle du point d'entrée auquel le système doit transférer le contrôle afin que le processus puisse être lancé.
- e_phoff :ce membre contient le décalage vers la table d'en-tête du programme. Ces informations sont stockées en termes d'octets. En l'absence de table d'en-tête de programme, l'information contenue par ce membre est nulle.
- e_shoff :ce membre contient le décalage vers la table d'en-tête de section. Comme avec e_phoff, cette information est également stockée sous forme d'octets et en l'absence d'une table d'en-tête de section, l'information contenue par ce champ est nulle.
- e_flags :ce membre contient des informations relatives à des drapeaux spécifiques au processus.
- e_ehsize :ce membre contient des informations relatives à la taille de l'en-tête ELF en byes.
- e_phentsize :ce membre contient des informations relatives à la taille d'une entrée dans la table d'en-tête du programme du fichier objet. Notez que toutes les entrées ont la même taille.
- e_phnum :ce membre contient les informations relatives au nombre d'entrées dans la table d'en-tête du programme.
- e_shentsize :ce membre contient les informations relatives à la taille d'une entrée dans la table d'en-tête de section. La taille est représentée sous forme de nombre d'octets.
- e_shnum :ce membre donne les informations relatives au nombre d'entrées dans la table d'en-tête de section.
Notez que le produit de ephnum et ephentsize donne la taille totale de la table d'en-tête du programme en octets et de la même manière le produit de eshnum et eshentsize donne la taille totale de la table d'en-tête de section en octets.