Carregamento de executáveis ELF
Autor: Hermano | Publicado: 2013-11-21T07:40:00-08:00


O formato ELF (Executable
and Linking Format) foi originalmente desenvolvido e publicado pelo UNIX Systems Laboratories como parte do Application Binary Interface. [1]. O ELF foi o formato de arquivo executável escolhido para a criação dos
executáveis do sistema FESO. Tanto o núcleo do sistema, quanto os drivers de dispositivos e aplicativos foram
desenvolvidos nesse formato.



O sistema FESO possui suporte a programas criados no formato ELF
que tenham sido especialmente compilados por um compilador cruzado (cross-compiler). Os executáveis do
sistema FESO devem usar apenas as bibliotecas e recursos da linguagem para as
quais existe suporte. Outra restrição, é que os aplicativos devem estar
estaticamente linkados, isto é, todas
as bibliotecas utilizadas pelo programa devem estar no próprio arquivo. O
sistema operacional ainda não suporta o uso de bibliotecas compartilhadas.



A figura mostra a estrutura de um arquivo ELF. O processo de
carregamento começa com a leitura do arquivo executável, e a criação de sua
imagem na memória do computador. Em seguida, as estruturas contidas no arquivo são
analisadas, para que seja possível posicionar o código executável corretamente
na memória.



A primeira estrutura analisada é o cabeçalho (ELF header) posicionado no começo do
arquivo. Neste cabeçalho, encontram-se, entre outras informações, o ponto de
entrada do arquivo executável, que nada mais é do que o endereço da primeira
instrução do programa. No cabeçalho existe ainda a posição inicial de outra
estrutura, a tabela de cabeçalho de programa (Program Header Table).



A tabela de cabeçalho de programa contém uma lista de estruturas
que especifica cada um dos segmentos (segments)
do arquivo. Os segmentos são partes do programa executável, que contém o código
e os dados. O algoritmo de carregamento de programas percorre a tabela, e
analisa cada uma de suas entradas. A estrutura de uma entrada dessa tabela
contém a posição do segmento dentro do arquivo executável, o tipo do segmento e
em qual endereço de memória ele deve ser carregado. 







A partir dessas informações, o algoritmo de carregamento é capaz
de criar um novo espaço de endereçamento para o novo processo, alocando frames de memória física, os mapeando neste
novo espaço e carregando os segmentos do arquivo para as suas devidas posições.
Uma vez que as seções estejam copiadas, uma nova estrutura de processo e outra
de thread são criadas e adicionadas
as suas respectivas árvores.



Os estados dos registradores dentro da estrutura do novo thread são configurados, de forma que o
EIP apontará para instrução indicada como o ponto de entrada do arquivo. Os
registradores da pilha são ajustados para as posições finais do espaço de
endereçamento. Os frames das pilhas
também precisam ser alocados, e mapeados em suas posições corretas no espaço de
endereçamento. A pilha é ajustada para receber os parâmetros passados ao
programa, quando estes existem, e o heap
do novo processo é devidamente inicializado, ficando pronto para ser expandido
à medida que necessário.



Quando todo esse processo é completado, o novo thread é adicionado à lista de threads do algoritmo de escalonamento.
No momento de sua primeira execução, o algoritmo de escalonamento simplesmente
coloca os valores armazenados nas estruturas nos registradores da CPU, e altera
o espaço de endereçamento (configurando um registrador da CPU, com o endereço
da Page Directory recém-criada). Finalmente,
o novo programa estará em execução, começando pelo seu ponto de entrada
especificado nas estruturas do arquivo ELF.&nbsp;&nbsp;

[1]&nbsp;TOOL INTEFACE STANDARD (TIS). Portable format specification. 1993. 262 p.