ELF significa formato de archivo ejecutable y enlazable.
ELF se utiliza como formato de archivo estándar para archivos de objetos en Linux. Antes de esto, el formato de archivo a.out se usaba como estándar, pero últimamente ELF asumió el cargo como estándar.
ELF admite:
- Distintos procesadores
- Diferente codificación de datos
- Diferentes clases de máquinas
Este artículo explica los diferentes tipos de archivos de objetos ELF y el encabezado ELF.
Archivos de objetos ELF
Un archivo que contiene código compilado se conoce como archivo de objeto. Un archivo de objeto puede ser cualquiera de los siguientes tipos:
1. Archivo reubicable
Este tipo de archivo de objeto contiene datos y código que se pueden vincular con otros archivos reubicables para producir un binario ejecutable o un archivo de objeto compartido. En términos sencillos, un archivo reubicable es lo mismo que el archivo .o producido cuando compilamos un código de la siguiente manera:
gcc -Wall -c test.c -o test.o
Entonces, el test.o producido después de la operación anterior sería un archivo reubicable.
2. Archivo de objeto compartido
El vinculador dinámico utiliza este tipo de archivo de objeto para combinarlo con el ejecutable y/u otros archivos de objeto compartidos para crear una imagen de proceso completa. En términos sencillos, un archivo de objeto compartido es lo mismo que el archivo .so producido cuando el código se compila con el indicador -fPIC de la siguiente manera:
gcc -c -Wall -Werror -fPIC shared.c gcc -shared -o libshared.so shared.o
Después de ejecutar los dos comandos anteriores, se produce como salida un archivo de objeto compartido libshared.o.
NOTA:Para obtener más información sobre las bibliotecas compartidas de Linux, consulte nuestro artículo Bibliotecas compartidas de Linux
3. Archivo ejecutable
Este tipo de archivo de objeto es un archivo que es capaz de ejecutar un programa cuando se ejecuta. En términos sencillos, es la salida de comandos como este:
gcc -Wall test.c -o test
Entonces, la 'prueba' de salida sería un ejecutable que, cuando se ejecutara, ejecutaría la lógica escrita en el archivo test.c.
Entonces, como se puede concluir de los tipos anteriores de archivos de objetos, un archivo de objetos participa desde la construcción del programa hasta su ejecución o podemos decir desde el enlace hasta la etapa de ejecución (para saber más sobre las etapas de compilación de Linux/GCC, consulte nuestro artículo Journey of un programa en C).
Encabezado ELF
Todos los archivos de objetos que se describen arriba son de tipo ELF en Linux.
Esto se puede probar fácilmente examinando cada uno de estos archivos. Por ejemplo, investigué cada uno de los tres archivos en mi sistema:
Archivo reubicable:
$ 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 ^@^@^@^@^@^@^@^@^@^@^@^@^@
Archivo de objeto compartido:
$ 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^@^@^@
Archivo ejecutable:
$ 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^@^@^@
Entonces vemos que, como estos son archivos binarios, nada es comprensible, excepto la cadena ELF al comienzo de cada archivo. Esto muestra que estos archivos solo tienen formato ELF.
Cada archivo comienza con un encabezado ELF que prácticamente indica la organización completa del archivo. Por ejemplo, los archivos de objetos compartidos y reubicables contienen secciones, pero en el otro extremo, el archivo ejecutable se compone de segmentos. Entonces, según el tipo de archivo de objeto, el encabezado ELF brinda información detallada sobre el archivo.
Generalmente, en el caso de archivos ejecutables, un encabezado ELF es seguido por una tabla de encabezado de programa. Una tabla de encabezado de programa ayuda a crear la imagen del proceso. Dado que ayuda a crear la imagen del proceso (que se crea después de ejecutar el ejecutable), la tabla de encabezado del programa se vuelve obligatoria para los archivos ejecutables, pero es opcional para los archivos de objetos compartidos y reubicables.
La siguiente es la organización del encabezado 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;
Entonces vemos que la organización que se muestra arriba tiene la forma de una estructura. Explicar cada miembro en detalle aquí complicaría las cosas, así que repasemos el significado básico y la información que tiene cada miembro de esta estructura para tener una idea de ese campo en particular.
1. e_ident
Como ya sabemos, el formato ELF admite varias clases de máquinas, procesadores, etc. Entonces, para admitir todo esto, la información inicial en el archivo ELF contiene información sobre cómo interpretar el archivo independientemente del procesador en el que se ejecuta el ejecutable. La matriz 'e_ident' proporciona exactamente la misma información:
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 Los primeros cuatro bytes anteriores contienen el número mágico '0x7fELF'.
- EI_CLASS Un ELF puede tener dos clases, 32 bits o 64 bits. Esto hace que el formato de archivo sea portátil.
- EI_DATA Este miembro brinda información sobre la codificación de datos. En pocas palabras, esta información indica si los datos están en formato big endian o little endian.
- EI_VERSION Este miembro proporciona información sobre la versión del archivo del objeto.
- EI_PAD Este miembro marca el comienzo de los bytes no utilizados en la matriz de información e_indent.
- EI_NIDENT Este miembro proporciona el tamaño de la matriz e_indent. Esto ayuda a analizar el archivo ELF.
2. e_tipo
Este miembro identifica el tipo de archivo de objeto. Por ejemplo, un archivo de objeto puede ser de los siguientes tipos:
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:La lista anterior no es exhaustiva, pero brinda información sobre los principales tipos de archivos de objetos a los que puede hacer referencia ELF.
3. e_máquina
Este miembro brinda información sobre la arquitectura que requiere un archivo 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. Miembros adicionales
Además de los tres miembros anteriores, también tiene los siguientes miembros:
- e_version:este miembro proporciona la información de la versión del archivo del objeto ELF.
- e_entry:este miembro proporciona la información de la dirección virtual del punto de entrada al que el sistema debe transferir el control para que se pueda iniciar el proceso.
- e_phoff:este miembro contiene el desplazamiento de la tabla de encabezado del programa. Esta información se almacena en términos de bytes. En ausencia de una tabla de encabezado de programa, la información contenida por este miembro es cero.
- e_shoff:este miembro contiene el desplazamiento de la tabla de encabezado de sección. Al igual que con e_phoff, esta información también se almacena en forma de bytes y, en ausencia de una tabla de encabezado de sección, la información contenida en este campo es cero.
- e_flags:este miembro contiene información relacionada con indicadores específicos del proceso.
- e_ehsize:este miembro contiene información relacionada con el tamaño del encabezado ELF en byes.
- e_phentsize:este miembro contiene información relacionada con el tamaño de una entrada en la tabla de encabezado del programa del archivo de objeto. Tenga en cuenta que todas las entradas tienen el mismo tamaño.
- e_phnum:este miembro contiene la información relacionada con el número de entradas en la tabla de encabezado del programa.
- e_shentsize:este miembro contiene la información relacionada con el tamaño de una entrada en la tabla de encabezado de sección. El tamaño se representa en forma de número de bytes.
- e_shnum:este miembro brinda la información relacionada con el número de entradas en la tabla de encabezado de sección.
Tenga en cuenta que el producto de ephnum y ephentsize da el tamaño total de la tabla de encabezado del programa en bytes y, de la misma manera, el producto de eshnum y eshentsize da el tamaño total de la tabla de encabezado de sección en bytes.