6.1 Arquivos em formato Objeto e Executável
6.1.1 Arquivos ELF em formato Objeto
O lado esquerdo da figura 18 mostra a organização de arquivos em formato objeto, onde: • é opcional a existência da tabela de cabeçalho do programa;
• o corpo principal do arquivo é dividido em seções. • é obrigatória a presença da tabela de cabeçalho das seções;
A tabela de cabeçalho das seções contém a localização (quantos bytes separam o início do arquivo, endereço zero, até o byte onde inicia a seção) de cada seção (por exemplo,.text,.data, etc) dentro do arquivo. As seções são informações pertinentes ao ligador, cujo conjunto de tarefas inclui agrupar as seções de vários arquivos objeto de entrada para gerar um arquivo executável onde as seções estão agrupadas.
Não está mostrado na figura, porém, cada seção contém um cabeçalho onde são armazenadas informações específicas da seção.
É importante perceber que o formato ELF não faz referência a nenhuma linguagem de programação. Isto permite a inte- gração de programas escritos em uma linguagem, digamosCcom programas escritos em outra linguagem, digamos assembly. Também permite a integração de arquivos objeto com programas em tempo de compilação. Para tal, o compilador primeiro gera o objeto do programa fonte.
Para tal, considere os programas fontea.c,b.c,c.c,d.cemain.capresentados nos algoritmos 48, 49, 50, 51 e 52 respecti- vamente. 1
#include <stdio.h>
2int globalA=1;
3void a (char* s)
4{
5printf("%s %d\n", s, globalA);
6}
Algorithm 48: Programa exemplo: arquivo “a.c”
1
#include <stdio.h>
2int globalB=2;
3void a (char* s)
4{
5printf("%s %d\n", s, globalB);
6}
Algorithm 49: Programa exemplo: arquivo “b.c”
1
#include <stdio.h>
2int globalC=3;
3void a (char* s)
4{
5printf("%s %d\n", s, globalC);
6}
Algorithm 50: Programa exemplo: arquivo “c.c”
1
#include <stdio.h>
2int globalD=4;
3void a (char* s)
4{
5printf("%s %d\n", s, globalD);
6}
Algorithm 51: Programa exemplo: arquivo “d.c”
Não é possível gerar o executável para o arquivomain.cpois ele não tem a implementação das funçõesa(),b(),c()ed(). Já os arquivosa.catéd.ctambém não permitem geração do arquivo executável pois ele não tem a implementação da função main.
> gcc main.c
/tmp/ccCFouSI.o: In function `main':
main.c:(.text+0x15): undefined reference to `a' collect2: error: ld returned 1 exit status > gcc a.c
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../x86_64-linux-gnu/crt1.o: In function `_start': (.text+0x20): undefined reference to `main'
collect2: error: ld returned 1 exit status
Existem várias formas de combinar os dois arquivos para gerar o arquivo executável: • Método 1: fonte main.c e fonte a.c
1
void a (char* s);
2void b (char* s);
3void c (char* s);
4void d (char* s);
5
int main (int argc, char** argv)
6{
7a("dentro de a");
8b("dentro de b");
9c("dentro de c");
10d("dentro de d");
11}
Algorithm 52: Programa exemplo: arquivo “main.c”
> gcc main.c a.c b.c c.c d.c -o main > ./main
dentro de a 1 dentro de b 2 dentro de c 3 dentro de d 4
• Método 2: fonte main.c e objeto a.o: > gcc -c a.c -o a.o
> gcc -c b.c -o b.o > gcc -c c.c -o c.o > gcc -c d.c -o d.o
> gcc main.c a.o b.o b.o d.o -o main • Método 3: fonte a.c e objeto main.o:
> gcc -c main.c -o main.o > gcc main.o a.c -o main • Método 4: com dois arquivos objeto:
> gcc -c main.c -o main.o > gcc -c a.c -o a.o > gcc main.o a.o -o main
Existem vários programas capazes de listar o conteúdo dos arquivos ELF objeto. Utilizaremos oreadelfpara examinar algumas questões pontuais.
A primeira questão está relacionada com as seções. Como já explicado, as seções de um arquivo ELF objeto contém informa- ções úteis para o programa ligador. Isto fica mais claro no método 4 acima, onde as entradas são arquivos ELF objeto.
Conforme já explicado, a figura 19 não indica, mas cada seção contém um cabeçalho que contém informações daquela seção. Este cabeçalho está presente no arquivo/usr/include/elf.h:
O leitor mais paciente (MUUUUITO mais paciente), pode examinar o arquivo objeto para encontrar estas informações. Os demais leitores podem visualizar as informações contidas nos cabeçalhos das seções com o comandoreadelf -S(a opção-S indica que é para imprimir somente as informações que constam nos cabeçalhos de cada seção).
> readelf a.o --sections --wide
There are 13 section headers, starting at offset 0x148: Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .text PROGBITS 0000000000000000 000040 00002a 00 AX 0 0 1
[ 2] .rela.text RELA 0000000000000000 0005c0 000048 18 11 1 8
[ 3] .data PROGBITS 0000000000000000 00006c 000004 00 WA 0 0 4
1
typedef struct
2{
3
Elf64_Word sh_name ;
/* Section name (string tbl index)
*/
4
Elf64_Word sh_type ;
/* Section type
*/
5
Elf64_Xword sh_flags ;
/* Section flags
*/
6
Elf64_Addr sh_addr ;
/* Section virtual addr at execution
*/
7
Elf64_Off sh_offset ;
/* Section file offset
*/
8
Elf64_Xword sh_size ;
/* Section size in bytes
*/
9
Elf64_Word sh_link ;
/* Link to another section
*/
10
Elf64_Word sh_info ;
/* Additional section information
*/
11
Elf64_Xword sh_addralign ;
/* Section alignment
*/
12
Elf64_Xword sh_entsize ;
/* Entry size if section holds table
*/
13
} Elf64_Shdr;
Algorithm 53: EstruturaElf64_Shdr, cabeçalho das seções (de/usr/include/elf.h)
[ 5] .rodata PROGBITS 0000000000000000 000070 000008 00 A 0 0 1
[ 6] .comment PROGBITS 0000000000000000 000078 00002a 01 MS 0 0 1
[ 7] .note.GNU-stack PROGBITS 0000000000000000 0000a2 000000 00 0 0 1
[ 8] .eh_frame PROGBITS 0000000000000000 0000a8 000038 00 A 0 0 8
[ 9] .rela.eh_frame RELA 0000000000000000 000608 000018 18 11 8 8
[10] .shstrtab STRTAB 0000000000000000 0000e0 000061 00 0 0 1
[11] .symtab SYMTAB 0000000000000000 000488 000120 18 12 9 8
[12] .strtab STRTAB 0000000000000000 0005a8 000016 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), l (large) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific
As opçõessections widepedem para imprimir somente os cabeçalhos das seções sem quebrar a linha.
São ao todo 13 seções. As duas primeiras colunas contém o número e nome de cada uma das seções. A seção zero é só usada para referência. A seção 1 comporta a seção.text, a seção 2 comporta a seção.rela.texte assim por diante.
A terceira coluna (Type) indica o tipo da seção (osh_typeno cabeçalho da seção) indica a semântica e conteúdo daquela seção. A lista é extensa e pode ser encontrada na manpage do elf4. Destacamos as seguintes:
PROGBITS Indica que o conteúdo é definido pelo programa. NOBITS A seção não ocupa espaço no arquivo.
STRTAB A seção contém uma tabela de strings.
SYMTAB Contém uma tabela dos símbolos disponibilizados pelo arquivo objeto.
A coluna Flags (sh_flags) contém bits cujo mapeamento é mostrado ao final da impressão. Os flags da seção.textsãoAX. A flagA (Alloc)significa que a seção ocupa memória durante a execução do processo enquanto que a flagX (Execute)diz que esta seção contém código executável.
Para o escopo deste livro, esta nível de detalhamento é suficiente, pois permite explicar como o ligador trabalha. Para uma explicação mais detalhada, consulte as man pages, o arquivo/usr/include/elf.h, o livro de John Levine [16] e o manual do ELF [4].
Como será visto no capítulo 7, o uma das funções do ligador é ler um a vários arquivos objeto e juntá-los em um único arquivo executável. Um dos cuidados que ele deve ter é garantir que exista exatamente uma implementação de cada função referenciada pelo programa. Outro cuidado é juntar as seções equivalentes em vários arquivos objeto em um mesmo segmento do arquivo executável.
É evidente que para efetuar estas operações, os arquivos ELF objeto devem conter as informações necessárias. 4 > man elf