Traduza a função abaixo para assembly, criando um arquivo foo.s
:
int foo (int x) {
return x+1;
}
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 (o próprio gcc imprime este código).
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.
Traduza agora a função abaixo para assembler,
int foo (int x) {
return add(x);
}
foo
com
gcc -c -Wa,-al
ou objdump -d
.
Note que o código gerado contém a instrução
e8 fc ff ff ffNessa instrução, o byte
e8
representa o código
de call, e os quatro bytes seguintes, o
deslocamento
da função chamada em relação à instrução seguinte ao call
(isto é, a diferença entre os dois endereços).
Esse deslocamento é armazenado em little endian, e pode ser um valor 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 (a função não está definida no módulo). O montador então preenche a posição do deslocamento com um valor "default", que será depois "corrigido" pelo linkeditor.
Declare agora no seu arquivo C a função:
int add (int x) {
return x+1;
}
e modifique o programa para ele preencher no array codigo
o
código de máquina da nova função foo
.
Como você pode obter o endereço de add
"programaticamente"
(usando o operador &),
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 a instrução call,
preenchendo os quatro bytes que se seguem ao "opcode" e8 com esse deslocamento
(lembre-se de que esse valor deve estar em little-endian!).
Execute agora o seu programa, e verifique se tudo funciona corretamente!