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).
unsigned char codigo[]
)
preenchido com o código de máquina visto no item anterior.
Lembre-se que a saída do objdump mostra os códigos das instruções
em hexadecimal. Use esses valores para preencher o array.
A seguir, o programa deve converter o endereço do array para um
endereço de função.
Para isso, declare o tipo
"ponteiro para uma função que recebe um
int
e retorna um int
",
conforme abaixo:
typedef int (*funcp) (int);
Atribua então o endereço do array a uma variável desse tipo:
funcp f = (funcp)codigo;
O ponteiro f
armazena agora o endereço da função, ou seja,
o endereço inicial do código da função, armazenado na memória.
Você pode então usar f
para chamar essa função
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 (sem essa opção, o sistema operacional abortará o seu programa, por tentar executar um código armazenado na área de dados). Execute o programa resultante e verifique a sua saída.
typedef int (*funcp) (int); int foo (funcp f, int x) { return (*f)(x); }
foo
recebe agora um ponteiro
para uma função (isto é, um endereço), e chama essa função passando para ela o seu segundo
parâmetro.
Você pode obter, em C, o endereço de uma função usando o seu nome:
typedef int (*funcp) (int); int add (int x) { return x+1; } int foo(funcp f, int x) { return (*f) (x); } int main() { ... i = foo(add, 10); ... }
Para traduzir a nova foo
você vai precisar usar uma instrução assembly que permita chamar essa função
f
. Uma opção é usar uma variação da instrução
call
, onde o endereço da função a ser chamada está em
um registrador. Por exemplo:
call *%eax
Observe o código de máquina da nova função foo
com
objdump -d
.
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
.
Você vai precisar de um tipo diferente para armazenar o endereço
da nova foo
:
"ponteiro para uma função que recebe um ponteiro para função e um int,
e retorna um int". Esse tipo pode ser declarado como abaixo:
typedef int (*funcp2) (funcp, int);
Execute agora o seu programa, e verifique se tudo funciona corretamente!