Introdução
Para quem está começando a programar em assembly no linux sabe como é difícil imprimir algo na tela usando somente a syscall write do sistema, principalmente quando se quer formatar a saída. Seria bom se pudéssemos usar funções como printf e scanf para fazermos IO em nossos programas.
No post de hoje iremos aprender como linkar a biblioteca libc em programas escritos em assembly.
Como compilar e linkar
O grande “mistério” está em como linkar o programa, pois é preciso indicar para o linker (o ld) qual será o linker dinâmico usado e também a inclusão da libc no aplicativo.
Supondo o código fonte programa.s, o processo de compilação e linkagem é feito assim:
$ as programa.s -o programa.o # cria o arquivo objeto $ ld -dynamic-linker /lib/ld-linux.so.2 -o programa programa.o -lc # cria o executável Se nenhum erro ocorreu basta executar o programa: $ ./programa
Exemplos de programas em assembly
Irei colocar alguns programas simples em assembly para demonstrar como é feito essas chamadas às funções da libc.
1 – Lendo um número inteiro e o mostrando na tela
# [lernumero.s] # Lê um número do teclado com scanf e em seguida o # mostra na tela usando printf. # Funções da libc utilizadas: # - printf # - scanf # # [Autor] # Marcos Paulo Ferreira (Daemonio) # undefinido gmail com # https://daemoniolabs.wordpress.com # # [Como usar] # $ as -o lernumero.o lernumero.s # $ ld -dynamic-linker /lib/ld-linux.so.2 -o lernumero lernumero.o -lc # $ ./lernumero # # Ter Nov 22 13:42:06 BRST 2011 # .equ EXIT_SYSCALL, 1 .section .data # Mensagem inicial. msg1_printf: .asciz "Digite um numero: " # Mensagem final. msg2_printf: .asciz "O numero digitado foi: %d\n" # Formato usado por scanf. str_scanf: .asciz "%d" # Variável que guarda o número digitado. variavel: .long 0 .section .text .globl _start _start: # Mostra a mensagem inicial. pushl $msg1_printf call printf addl $4, %esp # "limpa" a pilha # Lê o número com scanf. # Repare que a ordem da passagem # argumentos é invertida. pushl $variavel pushl $str_scanf call scanf addl $8, %esp # "limpa" a pilha pushl variavel pushl $msg2_printf call printf addl $8, %esp # "limpa" a pilha # Invoca o Linux para terminar. # o programa. exit: movl $EXIT_SYSCALL, %eax xorl %ebx, %ebx int $0x80 #EOF
2 – Converter string lida em maiúscula
# [maiscula.s] # Transforma uma string de entrada em maiúsculas. # Funções da libc utilizadas: # - printf # - memset # - toupper # # [Autor] # Marcos Paulo Ferreira (Daemonio) # undefinido gmail com # https://daemoniolabs.wordpress.com # # [Como usar] # $ as -o maiscula.o maiscula.s # $ ld -dynamic-linker /lib/ld-linux.so.2 -o maiscula maiscula.o # $ ./maiscula # # Ter Nov 22 14:44:07 BRST 2011 # # Constantes .equ BUFSIZE, 4096 .equ EXIT_SYSCALL, 1 .equ STDIN, 0 .section .data str: .asciz "Digite uma string: \n" # Declara nosso buffer de leitura # no bss, assim ele não aumentará # o tamanho do executável. .section .bss .lcomm mybuf, BUFSIZE .section .text .globl _start _start: # Zera o buffer que armazenara a string # digitada com a ajuda de memset. pushl $BUFSIZE pushl $0x0 pushl $mybuf call memset addl $12, %esp # Mostra a mensagem inicial. pushl $str call printf addl $4, %esp # Melhor chamar a syscall read # do que a função fgets, por exemplo. # fgets usa FILE* stream que é um tipo # do C. movl $3, %eax movl $STDIN, %ebx movl $mybuf, %ecx movl $BUFSIZE, %edx decl %edx # retira uma posição para o NULL char. int $0x80 # Chama a função de converter # uma string para maiúscula. pushl $mybuf call mystrupr addl $4, %esp # Mostra a string alterada na tela. pushl $mybuf call printf exit: movl $EXIT_SYSCALL, %eax xorl %ebx, %ebx int $0x80 .type mystrupr, @function mystrupr: pushl %ebp movl %esp, %ebp # Salva o endereço da string em %ebx. movl 8(%ebp), %ebx xorl %ecx, %ecx my_for: xorl %eax, %eax # Obtém um caractere da string. movb (%ebx, %ecx, 1), %al # Se for o NULL char, sai do for. testb %al, %al jz my_for_sair # Salva registradores utilizados. pushl %ebx pushl %ecx # Chama toupper da libc. pushl %eax call toupper # Restaura registradores. popl %ecx popl %ecx popl %ebx # Atualiza a string. movb %al, (%ebx, %ecx, 1) # Incrementa o contador. incl %ecx jmp my_for my_for_sair: popl %ebx ret #EOF
3 – Alocar memória no heap
# [heap.s] # Aloca memoria no heap usando malloc para armazenar # string digitada. # Funções da libc utilizadas: # - memset # - puts # - malloc # - free # # [Autor] # Marcos Paulo Ferreira (Daemonio) # undefinido gmail com # daemoniolabs.wordpress.com # # [Como usar] # $ as -o heap.o heap.s # $ ld -dynamic-linker /lib/ld-linux.so.2 -o heap heap.o -lc # $ ./heap # Ter Nov 22 14:43:51 BRST 2011 # # Constantes. .equ BUFSIZE, 4096 .equ STDIN, 0 .equ EXIT_SYSCALL, 1 .section .data bufpointer : .long 0 str: .asciz "** Digite uma string **" str2: .asciz "** String digitada **" erromalloc: .asciz "** Impossivel conseguir memoria. **" .section .text .globl _start _start: # Chama malloc pushl $BUFSIZE call malloc cmp $0, %eax jnz seguir # Mostra mensagem de erro caso # malloc retorne erro. pushl $erromalloc call puts jmp exit seguir: # Copia o ponteiro para bufpointer. movl %eax, bufpointer # Chama memset. pushl $BUFSIZE pushl $0 pushl bufpointer call memset addl $12, %esp # Mostra a mensagem inicial. pushl $str call puts addl $4, %esp # Leitura da string. movl $3, %eax movl $STDIN, %ebx movl bufpointer, %ecx movl $BUFSIZE, %edx decl %edx # retira uma posição para o NULL char. int $0x80 # Mostra a mensagem. pushl $str2 call puts addl $4, %esp # Mostra a string na tela. pushl bufpointer call puts addl $4, %esp # free. pushl bufpointer call free exit: movl $EXIT_SYSCALL, %eax xorl %ebx, %ebx int $0x80 #EOF
4 – Ler linhas de um arquivo
# [ccat.s] # Simula o aplicativo cat. # Uso: ccat # # [Autor] # Marcos Paulo Ferreira (Daemonio) # undefinido gmail com # https://daemoniolabs.wordpress.com # # Funções da libc utilizadas: # - memset # - malloc # - free # # [Como usar] # $ as -o ccat.o ccat.s # $ ld -dynamic-linker /lib/ld-linux.so.2 -o ccat ccat.o -lc # $ ./ccat # # Constantes .equ BUFSIZE , 4096 .equ EXIT_SYSCALL , 1 .equ READ_SYSCALL , 3 .equ WRITE_SYSCALL , 4 .equ OPEN_SYSCALL , 5 .equ CLOSE_SYSCALL , 6 .equ O_RDONLY , 0 .equ END_OF_FILE , 0 .equ STDOUT , 1 .section .data bufpointer : .long 0 filefd : .long 0 erromalloc: .asciz "** Impossivel conseguir memoria. **" erroopen: .asciz "** Impossivel abrir arquivo. **" use: .asciz "[uso] ccat " .section .text .globl _start _start: # Testa argc. cmp $2, (%esp) jge argvok # Uso incorreto. pushl $use call puts jmp exit argvok: # Abre arquivo. popl %ebx # argc popl %ebx # argv[0] popl %ebx # argv[1] movl $O_RDONLY, %ecx movl $0666, %edx movl $OPEN_SYSCALL, %eax int $0x80 cmp $0, %eax jg openok # Impossível abrir arquivo. pushl $erroopen call puts jmp exit openok: movl %eax, filefd # Chama malloc. pushl $BUFSIZE call malloc cmp $0, %eax jnz mallocok # Erro se malloc retornar NULL. pushl $erromalloc call puts jmp exit mallocok: # Copia o ponteiro para bufpointer. movl %eax, bufpointer nextline: # Chama memset. pushl $BUFSIZE pushl $0 pushl bufpointer call memset # Leitura da linha. movl $READ_SYSCALL, %eax movl filefd, %ebx movl bufpointer, %ecx movl $BUFSIZE, %edx decl %edx # retira uma posição para o NULL char int $0x80 # Verifica se chegou ao fim do arquivo. cmpl $END_OF_FILE, %eax jle end_loop # Mostra a linha na tela. movl %eax, %edx movl $WRITE_SYSCALL, %eax movl bufpointer, %ecx movl $STDOUT, %ebx int $0x80 jmp nextline # Fecha o fd com close e chama free. end_loop: movl $CLOSE_SYSCALL, %eax movl filefd, %ebx int $0x80 # free. pushl bufpointer call free exit: movl $EXIT_SYSCALL, %eax xorl %ebx, %ebx int $0x80 #EOF
Conclusão
Esse post foi para mostrar como usar as funções padrões do C, presentes na biblioteca dinâmica libc, em programas escritos em assembly. Embora isso tenha uma grande utilidade, eu acho que não faz sentido algum usar muitas funções prontas das bibliotecas do C, pois seria muito mais fácil programar em C diretamente.
Referências
[1] Programming from the Ground Up by Jonathan Bartlett (Acessado em: Novembro/2011)
http://florida.theorangegrove.org/og/file/6d9fe012-ffe1-1469-3a77-f65d56c0e41b/1/ProgrammingGroundUp-1-0-pdf.pdf
[2] IA-32 Assembly no Linux by Prof. Rossano Pablo Pinto, MSc. (Acessado em: Novembro/2011)
http://rossano.pro.br/fatec/cursos/sistcomp/apostilas/assembly-language-br.html
Post editado em: 01/08/2012