Seja bem vindo!

OSDev Brasil

Juntando código C com Assembly

Data: junho 10, 2008 || Postado por: Mounter   ||  Categoria: Intel X86, Sistemas Operacionais, Tutoriais

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.

10 Respostas para “Juntando código C com Assembly”

  1. anthony Diz:

    Cara, voce me tirou uma enooorrrme duvida.. Muito bom esse seu tutorial.

  2. hideo Diz:

    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]

  3. MarcosFelipe Diz:

    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?

  4. jose Diz:

    Como faço para um programa c ler entrada em MIPS

  5. Mounter Diz:

    @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

  6. Abner Cordeiro Diz:

    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…

  7. Alex K. Diz:

    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.

  8. Nelson Lima Diz:

    Gostaria de fazer o contrario. Dentro do assembly chamar uma função do C inclusive a main()

  9. Mounter Diz:

    @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.

  10. Frederico Lamberti Pissarra Diz:

    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

Deixe uma Resposta

Índice | Parceiros | Colaboradores | Sobre-nós | ©2007 OSDevBrasil.net | Desenvolvido por OptiSoft | Powered by WordPress | Hospedado por HostGator