Juntando código C com Assembly
Como sempre ando vendo por aí duvidas de como juntar o código em C com Assembly, hoje resolvi fazer esse artigo para tirar todas as dúvidas referente a isto.
Primeiro devemos saber como o C processa os parâmetros e o retorno de dados, na verdade isso é bem simples. Quando passamos os parâmetros para o código o C joga os dados na pilha, então para um arquivo como:
teste.c:
int soma(int x, int y)
{
return x+y;
}
int main(void)
{
int r;
r = soma(5, 6);
return 0;
}
Teremos o seguinte código assembly:
teste.asm:
_soma: push ebp mov ebp, esp mov eax, DWORD [ebp + 12] add eax, DWORD [ebp + 8] pop ebp ret _main: push ebp mov ebp, esp sub esp, 24 and esp, -16 mov eax, 0 add eax, 15 add eax, 15 shr eax, 4 sal eax, 4 mov DWORD [ebp - 8], eax mov eax, DWORD [ebp -8] call __alloca call ___main mov [esp+4], 6 mov [esp], 5 call _soma mov [ebp - 4], eax mov eax, 0 leave ret
Parece ser muito complicado não? Mas vou explicar cada linha!
Como você deve ter percebido, o C adiciona um underscore antes do nome de cada função, a função _soma é de longe bem fácil de entender, como a função recebe os dados da pilha primeiro ela deverá manipula-los, então ela salva o EBP na pilha e move o valor de ESP para ele, a pilha agora deverá ser algo do tipo:
VALOR 6
VALOR 5
VALOR DE EIP
VALOR DE EBP
No código assembly gerado, podemos ver que o EAX é quem recebe o retorno, por isso todas as manipulação são feitas encima dele, então
mov eax, DWORD [ebp+12]
seria nosso Y e
add eax, DWORD [ebp + 8]
seria o mesmo que return x+y; depois disso o valor de EBP é retomado e a função volta de onde foi chamada.
Na função _main, vamos aquele amontoado de código gerado para alocar as variáveis, mas podemos ignora-lo, o que interessa mesmo é esta parte:
mov [esp+4], 6 mov [esp], 5 call _soma mov [ebp - 4], eax mov eax, 0 leave ret
As três primeiras linhas de código como podemos ver seria os valores passados a pilha e a chamada a função, seria o mesmo que fazer:
push 5 push 6 call _soma
E como o valor de retorno está em EAX, o [EBP - 4], representa nossa variável r e como podemos ver, todas as variáveis criadas dentro das funções ficam na pilha, por isso o uso de EBP. Como termino return 0, é representado como:
mov eax, 0 leave ; Libera o espaço usado por _main na pilha ret ; Retorna
Acredito que depois disso podemos entender, em geral, como o C gerá o código Assembly, mas pera ai! Não estamos esquecendo de algo?
Claro e como podemos juntar um arquivo assembly com um C? Se você estiver usando o DJGPP e o NASM, você pode fazer o seguinte:
nasm -fcoff assembly.asm -o assembly.o
gcc -c arquivo.c -o arquivo.o
ld -r assembly.o arquivo.o -o combinado.o
Para isso vou dar um exemplo final:
arqasm.asm:
[BITS 32] [SECTION .text] _soma: push ebp mov ebp, esp mov eax, DWORD [ebp + 12] add eax, DWORD [ebp + 8] pop ebp ret
arqc.c:
int soma(int x, int y);
int teste(void)
{
int r;
r = soma(5, 6);
return 0;
}
Se alguêm tiver interesse em ver como o GCC gerá os códigos para o assembly, você poderá tentar o seguinte comando:
gcc -c arquivo.c -S teste.s
Espero que tenham gostado, se tiverem dúvidas ou algo estiver errado, mandem um comentário.






31 dUTC outubro dUTC 2008 as 9:38
Cara, voce me tirou uma enooorrrme duvida.. Muito bom esse seu tutorial.
14 dUTC dezembro dUTC 2008 as 10:21
Prezados Srs/Sras Develop Brasil. Conicidencia minha e semelhante ao exposto , estou tentando entender as variaveis locais e parametros passado p/ funcao. O assembl. do main nao seria
push ebp
mov ebp,esp
sub esp, 44h int r menos 4 bits
push ebx
push esi
push edi
lea edi,[ebp - 44h]
mov ecx, 11h
mov eax, occccccch
rep.stos dword ptr[edi]
11 dUTC fevereiro dUTC 2009 as 10:50
Olha, eu uso DevC++, e vi que tem jeito de intercalar códigos assembly e C++, mas como eu poderia usar as variáveis geradas pelo C++ pelo assembly?
25 dUTC novembro dUTC 2009 as 16:31
Como faço para um programa c ler entrada em MIPS
30 dUTC novembro dUTC 2009 as 20:10
@MarcosFelipe: Você teria que usar
extern “C” int i;
Porque o C++ altera a nomenclatura das váriaveis que o programa assembly deveria ver, assim ele fica legivel ao assembly ou programa em C.
Abraços
3 dUTC fevereiro dUTC 2010 as 18:21
Ahe todos, muito bom, me ajudou muito, este site me ajudou muito, se alguem tiver interesse em me ajudar entre no meu msn:abnercordeiro@hotmail.com
valeu todos…
19 dUTC março dUTC 2011 as 20:49
Copiei o programa em C para o Dev-CPP (v. 4.9.9.2, com gcc v. 3.4.2 mingw-special, Windows Vista) e compilei-o.
Usando o gdb com o comando disassemble soma resultou nisto:
(gdb) disassemble soma
Dump of assembler code for function soma:
0×401290 : push %ebp
0×401291 : mov %esp,%ebp
0×401293 : mov 0xc(%ebp),%eax
0×401296 : add 0×8(%ebp),%eax
0×401299 : pop %ebp
0x40129a : ret
End of assembler dump.
E em seguida, disassemble main:
(gdb) disassemble main
Dump of assembler code for function main:
0x40129b : push %ebp
0x40129c : mov %esp,%ebp
0x40129e : sub $0×18,%esp
0x4012a1 : and $0xfffffff0,%esp
0x4012a4 : mov $0×0,%eax
0x4012a9 : add $0xf,%eax
0x4012ac : add $0xf,%eax
0x4012af : shr $0×4,%eax
0x4012b2 : shl $0×4,%eax
0x4012b5 : mov %eax,0xfffffff8(%ebp)
0x4012b8 : mov 0xfffffff8(%ebp),%eax
0x4012bb : call 0×401730
0x4012c0 : call 0x4013d0
0x4012c5 : movl $0×6,0×4(%esp,1)
0x4012cd : movl $0×5,(%esp,1)
0x4012d4 : call 0×401290
0x4012d9 : mov %eax,0xfffffffc(%ebp)
0x4012dc : mov $0×0,%eax
0x4012e1 : leave
0x4012e2 : ret
End of assembler dump.
Alguém poderia explicar as pequenas diferenças entre o código de Teste.c desmontado/depurado e o código logo abaixo de Teste.asm?
Desde já agradeço.
4 dUTC junho dUTC 2011 as 1:05
Gostaria de fazer o contrario. Dentro do assembly chamar uma função do C inclusive a main()
18 dUTC junho dUTC 2011 as 21:11
@Nelson: Isso vai depender muito do seu programa de assembly, no nasm usamos a declaração:
extern nome_da_funcao
call nome_da_funcao
No assembler do gcc (as), não precisamos usar tal declaração, basta somente fazer um call na função. Se a função tiver parâmetros, estes deverão ser colocados na pilha e isso depende muito do compilador que você usar.
22 dUTC julho dUTC 2011 as 23:21
De fato, você não precisa do stack-frame para integrar código em ASM com C… Um exemplo:
—-%<——%<———-
/* test.c */
extern int sum(int a, int b);
/* test2.asm */
section .text
global sum
sum:
mov eax,[esp-4]
add eax,[esp-8]
ret
—-%<——%<———-
Usar EBP quase sempre é supérfluo. O mesmo efeito pode ser obtido, em C, usando a opção de compilação -fomit-frame-pointer, no GCC.
[]s
Fred