ELF sta per formato file eseguibile e collegabile.
ELF viene utilizzato come formato di file standard per i file oggetto su Linux. Prima di questo, il formato di file a.out veniva utilizzato come standard, ma recentemente ELF ha assunto l'incarico come standard.
ELF supporta :
- Diversi processori
- Diversa codifica dei dati
- Diverse classi di macchine
Questo articolo spiega i diversi tipi di file oggetto ELF e di intestazione ELF.
File oggetto ELF
Un file che contiene codice compilato è noto come file oggetto. Un file oggetto può essere di uno qualsiasi dei seguenti tipi:
1. File trasferibile
Questo tipo di file oggetto contiene dati e codice che possono essere collegati insieme ad altri file riposizionabili per produrre un file binario eseguibile o un file oggetto condiviso. In parole povere, un file rilocabile è lo stesso del file .o prodotto quando compiliamo un codice nel modo seguente:
gcc -Wall -c test.c -o test.o
Quindi il test.o prodotto dopo l'operazione di cui sopra sarebbe un file rilocabile.
2. File oggetto condiviso
Questo tipo di file oggetto viene utilizzato dal linker dinamico per combinarlo con l'eseguibile e/o altri file oggetto condivisi per creare un'immagine di processo completa. In parole povere, un file oggetto condiviso è uguale al file .so prodotto quando il codice viene compilato con il flag -fPIC nel modo seguente:
gcc -c -Wall -Werror -fPIC shared.c gcc -shared -o libshared.so shared.o
Dopo aver eseguito i due comandi precedenti, viene prodotto come output un file oggetto condiviso libshared.o.
NOTA:per saperne di più sulle librerie condivise Linux, fare riferimento al nostro articolo Librerie condivise Linux
3. File eseguibile
Questo tipo di file oggetto è un file in grado di eseguire un programma durante l'esecuzione. In parole povere, è l'output di comandi come questo:
gcc -Wall test.c -o test
Quindi, l'output "test" sarebbe un eseguibile che, una volta eseguito, eseguirà la logica scritta nel file test.c.
Quindi, come si può concludere dai suddetti tipi di file oggetto, un file oggetto partecipa dalla creazione del programma alla sua esecuzione o possiamo dire dal collegamento alla fase di esecuzione (per saperne di più sulle fasi di compilazione Linux/GCC, fare riferimento al nostro articolo Viaggio di un programma C).
Intestazione ELF
Tutti i file oggetto descritti sopra sono di tipo ELF su Linux.
Questo può essere facilmente dimostrato esaminando ciascuno di questi file. Ad esempio, ho esaminato ciascuno dei tre file nel mio sistema:
File trasferibile:
$ 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 ^@^@^@^@^@^@^@^@^@^@^@^@^@
File oggetto condiviso:
$ 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^@^@^@
File eseguibile:
$ 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^@^@^@
Quindi vediamo che poiché si tratta di file binari, nulla è comprensibile tranne la stringa ELF all'inizio di ogni file. Questo mostra che questi file sono solo in formato ELF.
Ogni file inizia con un'intestazione ELF che indica praticamente l'organizzazione completa del file. Ad esempio, i file oggetto rilocabili e condivisi contengono sezioni ma all'altra estremità il file eseguibile è composto da segmenti. Quindi, a seconda del tipo di file oggetto, l'intestazione ELF fornisce informazioni dettagliate sul file.
Per lo più in caso di file eseguibili, un'intestazione ELF è seguita da una tabella di intestazione del programma. Una tabella di intestazione del programma aiuta nella creazione dell'immagine di processo. Poiché aiuta nella creazione di un'immagine di processo (che viene creata dopo l'esecuzione dell'eseguibile), la tabella di intestazione del programma diventa obbligatoria per i file eseguibili ma è facoltativa per i file oggetto riposizionabili e condivisi.
Quella che segue è l'organizzazione dell'intestazione 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;
Quindi vediamo che l'organizzazione mostrata sopra ha la forma di una struttura. Spiegare ogni membro in dettaglio qui renderebbe le cose complesse, quindi esaminiamo il significato di base e le informazioni detenute da ciascun membro di questa struttura per avere un'idea di quel particolare campo.
1. e_ident
Come già sappiamo, il formato ELF supporta varie classi di macchine, processori ecc. Quindi, per supportare tutto ciò, le informazioni iniziali nel file ELF contengono informazioni su come interpretare il file indipendentemente dal processore su cui è in esecuzione l'eseguibile. L'array 'e_ident' fornisce esattamente le stesse informazioni :
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 I primi quattro byte sopra contengono il numero magico "0x7fELF".
- EI_CLASS Un ELF può avere due classi, 32 bit o 64 bit. Questo rende il formato file portatile.
- EI_DATA Questo membro fornisce le informazioni sulla codifica dei dati. In poche parole, queste informazioni indicano se i dati sono in formato big endian o little endian.
- EI_VERSION Questo membro fornisce informazioni sulla versione del file oggetto.
- EI_PAD Questo membro segna l'inizio dei byte non utilizzati nell'array di informazioni e_indent.
- EI_NIDENT Questo membro fornisce la dimensione dell'array e_indent. Questo aiuta nell'analisi del file ELF.
2. e_type
Questo membro identifica il tipo di file oggetto. Ad esempio, un file oggetto può essere dei seguenti tipi:
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
NOTA:L'elenco sopra non è esaustivo ma fornisce comunque informazioni sui principali tipi di file oggetto a cui ELF può fare riferimento.
3. e_macchina
Questo membro fornisce informazioni sull'architettura richiesta da un file 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. Membri aggiuntivi
Oltre ai tre membri di cui sopra, ha anche i seguenti membri:
- e_version:questo membro fornisce le informazioni sulla versione del file oggetto ELF.
- e_entry:questo membro fornisce le informazioni sull'indirizzo virtuale del punto di ingresso a cui il sistema deve trasferire il controllo in modo che il processo possa essere avviato.
- e_phoff:questo membro mantiene l'offset nella tabella di intestazione del programma. Queste informazioni sono memorizzate in termini di byte. In assenza di una tabella di intestazione del programma, le informazioni contenute da questo membro sono zero.
- e_shoff:questo membro mantiene l'offset alla tabella di intestazione della sezione. Come per e_phoff, anche queste informazioni sono memorizzate sotto forma di byte e in assenza di una tabella di intestazione di sezione, le informazioni contenute in questo campo sono zero.
- e_flags:questo membro contiene informazioni relative al processo di flag specifici.
- e_ehsize:questo membro contiene informazioni relative alla dimensione dell'intestazione ELF in bye.
- e_phentsize:questo membro contiene informazioni relative alla dimensione di una voce nella tabella dell'intestazione del programma del file oggetto. Nota che tutte le voci hanno le stesse dimensioni.
- e_phnum:questo membro contiene le informazioni relative al numero di voci nella tabella dell'intestazione del programma.
- e_shentsize:questo membro contiene le informazioni relative alla dimensione di una voce nella tabella dell'intestazione della sezione. La dimensione è rappresentata sotto forma di numero di byte.
- e_shnum:questo membro fornisce le informazioni relative al numero di voci nella tabella dell'intestazione della sezione.
Nota che il prodotto di ephnum ed ephentsize fornisce la dimensione totale della tabella di intestazione del programma in byte e allo stesso modo il prodotto di eshnum ed eshentsize fornisce la dimensione totale della tabella di intestazione della sezione in byte.