O que é um ELF ?

Os arquivos ELF são como mapas para o kernel e as ferramentas de sistema: eles dizem exatamente onde cada pedaço do binário deve ser colocado na memória, quem pode fazer o quê (como executar, escrever ou só ler), e onde tudo começa a rodar. Além disso, eles carregam instruções extras para os carregadores dinâmicos, indicando quais bibliotecas precisam ser conectadas em tempo de execução. Para as ferramentas de análise, como depuradores e linkers, o ELF entrega uma tabela bem organizada com símbolos, endereços, nomes de funções e muito mais.

1. ELF Header: O Cartão de Visitas do Executável

O ELF Header funciona como o cartão de visitas do binário, contendo informações essenciais que identificam o arquivo como ELF, definem a arquitetura para a qual ele foi compilado, Informa o endereço do entry point, o ponto de partida para a execução do código, e indicam o endianness (ordem dos bytes). Além disso, ele especifica os offsets que apontam para outras estruturas importantes do arquivo, como a Section Header Table (SHT) e a Program Header Table (PHT), além do tamanho do próprio ELF Header, entre outros dados. Esses detalhes serão explorados com mais profundidade nas próximas seções.

1.2. Explorando o ELF Header

A definição formal dos campos do ELF Header é declarada no arquivo de cabeçalho elf.h. Nesse arquivo, as informações são organizadas em uma struct C, cuja implementação varia conforme a arquitetura. Para sistemas de 64 bits (x86_64), utiliza-se a estrutura Elf64_Ehdr, que possui um tamanho fixo de 64 bytes e Para sistemas de 32 bits (x86), a estrutura correspondente é a Elf32_Ehdr, com um tamanho de 52 bytes.

------elf.h------
#define EI_NIDENT (16)

typedef struct
{
  unsigned char	e_ident[EI_NIDENT];	/* Magic number and other info */
  Elf64_Half	e_type;			          /* Object file type */
  Elf64_Half	e_machine;	         	/* Architecture */
  Elf64_Word	e_version;	        	/* Object file version */
  Elf64_Addr	e_entry;	          	/* Entry point virtual address */
  Elf64_Off	  e_phoff;             	/* Program header table file offset */
  Elf64_Off  	e_shoff;	            /* Section header table file offset */
  Elf64_Word	e_flags;	          	/* Processor-specific flags */
  Elf64_Half	e_ehsize;	          	/* ELF header size in bytes */
  Elf64_Half	e_phentsize;	       	/* Program header table entry size */
  Elf64_Half	e_phnum;	          	/* Program header table entry count */
  Elf64_Half	e_shentsize;	       	/* Section header table entry size */
  Elf64_Half	e_shnum;	          	/* Section header table entry count */
  Elf64_Half	e_shstrndx;	        	/* Section header string table index */
} Elf64_Ehdr;

1.2.1. E_IDENT

Como definido na estrutura Elf64_Ehdr, o campo e_ident é um array de 16 bytes (unsigned char e_ident[16]). Ele armazena informações fundamentais sobre o formato do binário ELF, sendo cada posição do array responsável por indicar uma característica específica do arquivo.

Offset Membro Descrição
0x00–0x03 EI_MAG0…EI_MAG3 Número Mágico (Magic Number): Contém a sequência 0x7F 'E' 'L' 'F' que identifica o arquivo como um binário no formato ELF.
0x04 EI_CLASS Classe do Arquivo: Define a arquitetura do processo. 1 (ELFCLASS32) para 32 bits ou 2 (ELFCLASS64) para 64 bits.
0x05 EI_DATA Endianness: Define a ordem dos bytes dos dados no arquivo. 1 (ELFDATA2LSB) para Little Endian ou 2 (ELFDATA2MSB) para Big Endian.
0x06 EI_VERSION Versão do ELF: Indica a versão do formato ELF. Atualmente, o valor padrão e único válido é 1.
0x07 EI_OSABI OS/ABI: Identifica a Application Binary Interface (ABI) específica do sistema operacional. Por exemplo, 0 para System V, 3 para GNU/Linux.
0x08 EI_ABIVERSION Versão da ABI: Indica a versão da ABI definida em EI_OSABI. O valor geralmente é 0.
0x09–0x0F EI_PAD Padding: Bytes de preenchimento não utilizados, reservados para futuras extensões da especificação. São sempre preenchidos com zero.

ELF HEADER

ELF HEADER

1.2.2. E_TYPE

Definição de Elf64/32_Half dentro de elf.h

Definição de Elf64/32_Half dentro de elf.h

Como vimos anteriormente, o campo e_type no cabeçalho ELF é do tipo Elf64_Half. Ao verificarmos no arquivo elf.h, constatamos que Elf64_Half é um alias para uint16_t, ou seja, um valor inteiro sem sinal de 16 bits. Isso significa que o campo e_type ocupa exatamente 2 bytes. Portanto, os dois bytes que vêm após o array e_ident (que tem 16 bytes) correspondem ao valor de e_type.

ELF HEADER

ELF HEADER

O campo e_type no cabeçalho ELF (Executable and Linkable Format) indica o tipo do arquivo ELF, ou seja, qual é o propósito do binário. Ele informa ao sistema operacional como interpretar e lidar com aquele arquivo.

Valor Hex Nome constante Significado
0x0000 ET_NONE Tipo indefinido (sem propósito específico)
0x0001 ET_REL Object File (.o)
0x0002 ET_EXEC Arquivo executável
0x0003 ET_DYN Shared Object (ex: .so)
0x0004 ET_CORE Arquivo de core dump
0xFE00–FFFF ET_LOPROC até ET_HIPROC Reservado para uso de processadores específicos

É importante notar que o tipo ET_DYN (valor 0x03) no cabeçalho ELF não se limita a bibliotecas compartilhadas (.so). Atualmente, ele também é o padrão para executáveis compilados como PIE (Position-Independent Executable).