INF1018 - Software Básico

Compilação e Ligação

  1. Considere o programa formado pelos arquivos abaixo:

    • arquivo temp1.c:

      #include <stdio.h>
      #include "temp1.h"
      #include "temp2.h"
      
      int a = 1024;
      
      int main (void) {
        foo();
        printf("%d\n", a);
        return 0;
      }
      
    • arquivo temp2.c:

      #include "temp1.h"
      #include "temp2.h"
      
      int b = 10;
      
      void foo (void) {
        a = -3;
      }
      
    • arquivo temp1.h:

      extern int a;
      
    • arquivo temp2.h:

      extern int b;
      void foo(void);
      

    Note que os arquivos temp1.h e temp2.h contém declarações dos simbolos globais exportados, respectivamente, por temp1.c e temp2.c A inclusão desses arquivos garante a consistência dessas declarações entre os módulos, tanto para os arquivos que definem os símbolos quanto para os que os importam.

    Compile, ligue e execute esse programa, seguindo os passos abaixo:
    > gcc -c -Wall temp1.c
    > gcc -c -Wall temp2.c
    > gcc -o prog  temp1.o temp2.o
    
    (Você poderia fazer tudo de uma só vez chamando gcc -Wall temp1.c temp2.c, mas, neste caso, o gcc não gera os arquivos .o.)
  2. Use o programa nm para inspecionar os símbolos usados por cada módulo (chame nm temp1.o e nm temp2.o). Você consegue inferir o significado das letras que aparecem na saída deste programa (U, T, etc.)? (Dica: "man nm".)

  3. Troque no 1o arquivo (temp1.c) a linha int a = 1024; por char a = 1; e recompile esse módulo. O que acontece? Em que passo foi detetado o erro? (compilação? linkedição?)

    Você percebe como a inclusão dos arquivos de header inclusive pelos módulos que definem os símbolos garante a consistência das declarações?

  4. Restabeleça no arquivo temp1.c declaração int a = 1024;

    1. Troque no 2o arquivo (temp2.c) a linha #include "temp1.h" por extern char a;

      Recompile os dois módulos, gere um novo executável e execute o programa. O que aconteceu? Você consegue explicar?

    2. Agora troque no arquivo temp2.c a declaração extern char a; por int a = 1; e recompile este módulo.

      Tente gerar um novo executável. O que acontece agora? Algum erro ocorreu? (se sim, em um passo?) Inspecione novamente os símbolos dos módulos objeto usando o nm.

    3. E se a troca for por static int a = 1;, o que acontece? Por que?

  5. Restaure a inclusão de "temp1.h" no 2o arquivo (temp2.c), e remova a declaração static int a = 1;.

    1. Agora remova no primeiro arquivo (temp1.c) a linha #include "temp2.h" (isto é, a declaração dos símbolos exportados por temp2).

      Troque em temp1.c a linha foo(); por b();

      Recompile os dois módulos e gere um novo executável com

      gcc -Wa,--execstack -Wall -c temp1.c
      gcc -Wa,--execstack -Wall -c temp2.c
      gcc -o prog -Wa,--execstack temp1.o temp2.o
      
      Verifique com o nm as tabelas de símbolo de cada módulo (em especial, o símbolo b).

      Execute o programa. O que aconteceu? Por que?

    2. Troque agora o valor inicial de b em temp2.c por 0xC3. Recompile este módulo, gere um novo executável, e execute-o. O que acontece agora? Por que?