INF1018 - Software Básico

Aulas de Laboratório

Código de Máquina

  1. Traduza a função abaixo para assembler, criando um arquivo foo.s:

    int foo (int x) {
      return x+1;
    }
    

  2. Use gcc -m32 -c foo.s para traduzir seu programa para linguagem de máquina (o gcc vai gerar um arquivo foo.o).

    Veja qual o código de máquina que seu programa gera, usando o comando objdump -d foo.o (a opção -d do objdump faz um "disassembly" do arquivo objeto). Você pode também usar a opção -Wa,-al do gcc para listar o código de máquina gerado.

  3. Escreva agora um programa em C como descrito a seguir. Esse programa deve criar um array de bytes (unsigned char codigo[]) preenchido com o código de máquina visto no item anterior. A seguir, ele deve converter o endereço do array para um endereço de função. Para isso, declare o tipo ponteiro para função recebendo int e retornando int, conforme abaixo:

    typedef int (*funcp) (int x);
    
    A seguir, atribua o endereço do array a uma variável do tipo acima:
    funcp f = (funcp)codigo;
    
    Agora, você pode chamar f como se fosse uma função C, fazendo, por exemplo:
    i = (*f)(10);
    
    Faça isso no seu programa. Deve ser necessário compilar seu programa com

    gcc -m32 -Wall -Wa,--execstack -o seuprograma seuprograma.c

    para permitir a execução do seu código de máquina. Execute o programa resultante.

  4. Traduza agora a função abaixo para assembler,

    int foo (int x) {
      return add(x);
    }
    

  5. Observe o código de máquina da nova função foo com gcc -c -Wa,-al ou objdump -d . Note que o código gerado contém a instrução
    e8 fc ff ff ff 
    
    Nessa instrução, o byte e8 representa o código de call, e os quatro bytes seguintes, o deslocamento (em número de bytes) da função chamada em relação à instrução seguinte ao call (em little endian!). Esse deslocamento pode ser negativo ou positivo, dependendo do endereço da função chamada ser "menor" ou "maior" que o da instrução seguinte ao call.

    Note que o deslocamento NÃO está correto no arquivo .o, pois o endereço da função chamada somente será conhecido no passo de linkedição.

  6. Declare agora no seu arquivo C a função:

    int add (int x) {
      return x+1;
    }
    
    e modifique o programa para ele criar no array o equivalente à função abaixo (para fazer isso, novamente escreva o assembly equivalente em foo.s e execute objdump -d).
    int foo (int x) {
      return add(x);
    }
    
    Como você pode obter o endereço de add, você pode modificar seu programa C para calcular qual deve ser o deslocamento para a chamada de foo a add e completar o código gerado para o call corretamente.