Do código -fonte ao código binário
A programação começa com uma idéia inteligente e escrevendo código -fonte em uma linguagem de programação de sua escolha, por exemplo C, e salvar o código -fonte em um arquivo. Com a ajuda de um compilador adequado, por exemplo, GCC, seu código -fonte é traduzido para o código do objeto, primeiro. Eventualmente, o vinculador traduz o código do objeto em um arquivo binário que vincula o código do objeto com as bibliotecas referenciadas. Este arquivo contém as instruções únicas como código de máquina que são entendidas pela CPU e são executadas assim que o programa compilado é executado.
O arquivo binário mencionado acima segue uma estrutura específica, e um dos mais comuns é chamado Elf que abrevia o formato executável e vinculado. É amplamente utilizado para arquivos executáveis, arquivos de objeto relocáveis, bibliotecas compartilhadas e despejos principais.
Vinte anos atrás - em 1999 - O projeto 86open escolheu o elfo como o formato de arquivo binário padrão para sistemas do tipo UNIX e UNIX nos processadores x86. Felizmente, o formato ELF havia sido documentado anteriormente na interface binária do System V Application e no padrão da interface da ferramenta [4]. Esse fato simplificou enormemente o acordo sobre a padronização entre os diferentes fornecedores e desenvolvedores de sistemas operacionais baseados em UNIX.
A razão por trás dessa decisão foi o design do ELF - flexibilidade, extensibilidade e suporte de plataforma cruzada para diferentes formatos e tamanhos de endereço endianos. O design da ELF não se limita a um processador específico, conjunto de instruções ou arquitetura de hardware. Para uma comparação detalhada dos formatos de arquivo executável, dê uma olhada aqui [3].
Desde então, o formato ELF está em uso por vários sistemas operacionais diferentes. Entre outros, isso inclui Linux, Solaris/Illumos, Livre, Net- e OpenBSD, QNX, Beos/Haiku e Fuchsia OS [2]. Além disso, você o encontrará em dispositivos móveis executando Android, Maemo ou Meego OS/Sailfish OS, bem como em consoles de jogo como o PlayStation Portable, Dreamcast e Wii.
A especificação não esclarece a extensão do nome do arquivo para arquivos ELF. Em uso está uma variedade de combinações de cartas, como .AXF, .BIN, .duende, .o, .Prx, .sopro, .Ko, .então e .mod, ou nenhum.
A estrutura de um arquivo ELF
Em um terminal Linux, o Comando Man Elf fornece um resumo útil sobre a estrutura de um arquivo ELF:
Listagem 1: a manpra da estrutura do elfo
$ MAN ELFComo você pode ver na descrição acima, um arquivo ELF consiste em duas seções - um cabeçalho de elfo e dados do arquivo. A seção de dados de arquivos pode consistir em uma tabela de cabeçalho do programa que descreve zero ou mais segmentos, uma tabela de cabeçalho de seção que descreve zero ou mais seções, seguidas de dados referidos pelas entradas da tabela de cabeçalho do programa e a tabela de cabeçalho da seção. Cada segmento contém informações necessárias para a execução do tempo de execução do arquivo, enquanto as seções contêm dados importantes para vincular e realocação. A Figura 1 ilustra isso esquematicamente.
O cabeçalho do elfo
O cabeçalho do elfo tem 32 bytes de comprimento e identifica o formato do arquivo. Começa com uma sequência de quatro bytes únicos que são 0x7f, seguidos por 0x45, 0x4c e 0x46, que se traduz nas três letras e, l e f. Entre outros valores, o cabeçalho também indica se é um arquivo ELF para formato de 32 ou 64 bits, usa pouco ou grande endianness, mostra a versão ELF, bem como para qual sistema operacional o arquivo foi compilado para interoperar com o Interface binária do aplicativo direito (ABI) e conjunto de instruções da CPU.
O hexdump do arquivo binário touch parece o seguinte:
.Listagem 2: O hexdump do arquivo binário
$ hd/usr/bin/touch | cabeça -5Debian GNU/Linux oferece o comando de leitura que é fornecido no pacote GNU 'Binutils'. Acompanhado pelo switch -h (versão curta para "-file-header"), ele exibe bem o cabeçalho de um arquivo ELF. Listagem 3 ilustra isso para o comando toque.
.Listagem 3: Exibindo o cabeçalho de um arquivo ELF
$ leitel -h/usr/bin/touchO cabeçalho do programa
O cabeçalho do programa mostra os segmentos usados em tempo de execução e diz ao sistema como criar uma imagem de processo. O cabeçalho da Listagem 2 mostra que o arquivo ELF consiste em 9 cabeçalhos do programa que têm um tamanho de 56 bytes cada, e o primeiro cabeçalho começa no Byte 64.
Novamente, o comando de leitura ajuda a extrair as informações do arquivo ELF. O Switch -l (abreviação para -programas ou -segmentos) revela mais detalhes, como mostrado na Listagem 4.
.Listagem 4: Exibir informações sobre os cabeçalhos do programa
$ leitel -l/usr/bin/touchO cabeçalho da seção
A terceira parte da estrutura dos elfos é o cabeçalho da seção. Deve listar as seções únicas do binário. O Switch -s (abreviação de cabeçalhos ou -seções) lista os diferentes cabeçalhos. Quanto ao comando Touch, existem 27 cabeçalhos de seção, e a lista 5 mostra os quatro primeiros deles mais o último, apenas. Cada linha cobre o tamanho da seção, o tipo de seção, bem como seu endereço e deslocamento de memória.
.Listagem 5: Detalhes da seção revelados pelo READELL
$ readfl -s/usr/bin/touchFerramentas para analisar um arquivo ELF
Como você deve ter observado nos exemplos acima, GNU/Linux é desenvolvido com várias ferramentas úteis que ajudam a analisar um arquivo ELF. O primeiro candidato que vamos dar uma olhada é o utilitário de arquivos.
Arquivo Exibe informações básicas sobre arquivos ELF, incluindo a arquitetura do conjunto de instruções para o qual o código em um arquivo de objeto relocável, executável ou compartilhado é pretendido. Na Listagem 6, ele diz que/bin/touch é um arquivo executável de 64 bits seguindo a base padrão do Linux (LSB), ligada dinamicamente e construída para o kernel GNU/Linux versão 2.6.32.
.Listagem 6: Informações básicas usando o arquivo
$ arquivo /bin /touchO segundo candidato está pertencente. Ele exibe informações detalhadas sobre um arquivo ELF. A lista de comutadores é comparável e abrange todos os aspectos do formato ELF. Usando o switch -n (curta para -notes) Listagem 7 mostra as seções de anotações, apenas, que existem no toque do arquivo -a tag de versão da ABI e o idiota Build ID BitString.
.Listagem 7: Exibir seções selecionadas de um arquivo ELF
$ readefl -n/usr/bin/touchObserve que em Solaris e FreeBSD, o utilitário elfdump [7] corresponde ao Readfl. A partir de 2019, não houve um novo lançamento ou atualização desde 2003.
O número três é o pacote chamado Elfutils [6] que está puramente disponível para Linux. Ele fornece ferramentas alternativas para o GNU binutils e também permite validar os arquivos ELF. Observe que todos os nomes dos utilitários fornecidos no pacote começam com a UE para 'elfs utils'.
Por último, mas não menos importante, mencionaremos o objdump. Esta ferramenta é semelhante ao Readef, mas se concentra nos arquivos de objeto. Ele fornece uma gama semelhante de informação sobre arquivos ELF e outros formatos de objeto.
.Listagem 8: Informações de arquivo extraídas por Objdump
$ objdump -f /bin /touchHá também um pacote de software chamado 'elfkickers' [9] que contém ferramentas para ler o conteúdo de um arquivo ELF, além de manipulá -lo. Infelizmente, o número de lançamentos é bastante baixo, e é por isso que apenas mencionamos, e não mostra mais exemplos.
Como desenvolvedor, você pode dar uma olhada em 'pax-utils' [10,11], em vez disso. Este conjunto de utilitários fornece uma série de ferramentas que ajudam a validar arquivos ELF. Como exemplo, o DumpEfl analisa o arquivo ELF e retorna um arquivo de cabeçalho C contendo os detalhes - veja a Figura 2.
Graças a uma combinação de design inteligente e excelente documentação, o formato ELF funciona muito bem e ainda está em uso após 20 anos. Os utilitários mostrados acima permitem uma visão de insight em um arquivo ELF e permite descobrir o que um programa está fazendo. Estes são os primeiros passos para analisar o software - Happy Hacking!
O escritor gostaria de agradecer a Axel Beckert por seu apoio em relação à preparação deste artigo.