Baixe o app para aproveitar ainda mais
Prévia do material em texto
Assembly intermediário: operações aritméticas e lógicas: Ênfase em memória Autor: Sergio Souza Novak Estudante de Ciência da Computação UNIOESTE data do lançamento:11/05/2016 Este E-BOOK não será vendido. AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com “Atenção, este livro se trata de anotações minhas, de um aluno, numa aula de Assembly. Faça bom uso dele e seja tolerante, assim como você, estou aprendendo essa linguagem, parceiro, RAAAAM, kkk. E as vezes aprendo por tentativa e erro. Lembre-se: você não pagou por esse livro. Estamos quites ? Então vamos lá: Prazer, meu nome é Sergio. O motivo de eu escrever isso é que não achei nada na internet a respeito disso que vos escrevo, então É MUITO BOM VOCÊ JÁ TER LIDO ARTIGOS BÁSICOS NA WEB. Você deve fazer essas “coisas” aqui descritas no LINUX. Eu não gosto de Linux, mas é o jeito. E mais uma coisa: onde estiver elf64 se você usa arquitetura _x86, isto é 32bits, mude para elf32 e também não use registradores de ordem r_ _ (RAX,EAX,RBX...chamo de “r” alguma coisa), pois os “r-alguma-coisa” são compatíveis apenas com máquinas _x64. Caso use _x64 é ótimo você usar esses r-alguma-coisa. O montador que eu utilizo é o NASM e para debuggar uso o GDB. Instale no LINUX o GDB e o NASM. Dos exemplos a seguir, salve no bloco de notas na extensão (*.asm), para mais fácil compreensão, aconselho que instale a sintaxe highlight que te ajuda a programar, sublinhando as palavras reservadas à linguagem. Também uso a SINTAXE INTEL para assembly hey-oh-lets-go” Sergio Souza Novak, autor. AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com Operações aritméticas e lógicas Operações ARITMÉTICAS Operações aritmética são operações do tipo adição e subtração em ponto flutuante. Em Assembly usamos as operações ADD e SUBB que correspondem a adição e subtração, respectivamente. Temos que a operação ADD e SUBB de sintaxe: ADD <registrador_de_destino> <valor_a_ser_adicionado> SUBB<<registrador_de_destino> <valor_a_ser_subtraído> Realiza a soma: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - registrador_de_destino+registrador_de_origem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Realiza a subtração: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - registrador_de_destino-registrador_de_origem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com destruíndo o valor inicial de registrador_de_destino. Algo bem parecido do que se tem em C: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - registrador_de_destino=registrador_de_destino + registrador_de_origem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - registrador_de_destino=registrador_de_destino - registrador_de_origem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Executando o algoritmo: section .text global _start _start: mov eax, 10 mov ebx, 20 mov ecx, 30 mov edx, 0xfffffffe add1: add ebx, eax add2: add edx, eax subb1: sub ecx, eax subb2: sub eax, ecx fim: mov rax, 1 mov rbx, 0 int 0x80 Montando e lincando depois iniciando no gdb, algo do tipo: alunos@CCSC-CC-LCT-32:~/Área de Trabalho$ nasm -f elf64 nome_do_arquivo.asm alunos@CCSC-CC-LCT-32:~/Área de Trabalho$ ld nome_do_arquivo.asm .o -o nome_do_arquivo.asm .x alunos@CCSC-CC-LCT-32:~/Área de Trabalho$ gdb nome_do_arquivo.asm .x Podemos iniciar os breakpoints declarados no corpo do código(obs: se a máquina for de 32bits substituir elf64 por elf32 e não usar registradores de ordem r_ _ , por exemplo RAX,RBX... típicos de arquitetura x_64): AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com (gdb) b add1 Ponto de parada 1 at 0x400094 (gdb) b add2 Ponto de parada 2 at 0x400096 (gdb) b subb1 Ponto de parada 3 at 0x400098 (gdb) b subb2 Ponto de parada 4 at 0x40009a (gdb) b fim Ponto de parada 5 at 0x40009c (gdb) r Breakpoint 1, 0x0000000000400096 in add1 () (gdb) p/d $eax $1 = 10 (gdb) p/d $ebx $2 = 20 (gdb) p/d $ecx $3 = 30 (gdb) p/d $edx $4 = -2 (gdb) p/x $edx $5 = 0xfffffffe (gdb) c Continuando. Breakpoint 2, 0x0000000000400096 in add2 () (gdb) p/d $ebx $8 = 30 (gdb) c Continuando. Breakpoint 3, 0x0000000000400098 in subb1 () (gdb) p/d $edx $9 = 8 (gdb) c Continuando. Breakpoint 4, 0x000000000040009a in subb2 () (gdb) p/d $ecx $10 = 20 (gdb) c Continuando. Breakpoint 5, 0x000000000040009c in fim () (gdb) p/d $eax $11 = -10 as somas e subtrações foram feitas corretamente. Vamos interpretar: Breakpoint 1, 0x0000000000400096 in add1 () O breakpoint 1 é feito antes do trecho de código que chamamos de add1, Depois através do comando posterior, podemos obter o resultado da série de comandos MOV. Que foi, realmente, bem sucedida. Observamos que todas as operações foram devidamente satisfeitas, nada foi além do esperado, as operações de soma e subtração foram satisfeitas. AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com Estudando overflow e outras operações no código exemplo 2: section .text global _start _start: mov ax, 0x7fff ; 32767 mov bx, 0xffff ; 65535 overflow: add ax, 1 ; -32768 carry: add bx, 1 ; 0 fim: mov rax, 1 mov rbx, 0 int 80h na linha que contém o MOV do valor 0x7fff em hexadecimal, temos que é um valor que quase ultrapassa o máximo escrito em 2 bytes. Já o número da linha seguinte é 0xffff é o maior representável em 2bytes. O que pode ser testado no gdb: (gdb) b overflow Ponto de parada 1 at 0x400088 (gdb) b carry Ponto de parada 2 at 0x40008c (gdb) b fim Ponto de parada 3 at 0x400090 (gdb) r (gdb) p/x $ax $1 = 0x7fff (gdb) p/x $bx $2 = 0xffff Os imediatos realmente foram bem movidos aos respectivos registradores. Continuando a execução: (gdb) c Continuando. Breakpoint 2, 0x000000000040008c in carry () Nesse breakpoint a instrução: add ax, 1 Foi executada. Logo, podemos estudar o que houve nessa execução. Para ter sido bem sucedida o registrador ax deve ter o conteúdo de 32768 que é 32767+1. Vamos assim verificar: (gdb) p/d $ax AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com $4 = -32768 (gdb) p/x $ax $3 = 0x8000 Observa-se que NÃO É O VALOR ESPERADO, não é de se surpreender, uma vez que o registrador AX é de 2bytes e o valor que se quer representar se utiliza de complemento de dois. Essa situação é típica de Overflow. Existe um comando para ver se ocorreu essa situação, se trata do registrador eflags, que guarda uma constante referente a última operação. (gdb) info registers eflags eflags 0xa96 [ PF AF SF IF OF ] A flag OF indica que ocorreu overflow na última operação. Estudo das flags no registrador eflags No terminal existe um comando quer mostra o que está acontecendo em todos os registradores, esse comando é: info registers E dá como resposta, por exemplo, isso: rax 0x8000 32768 rbx 0xffff 65535 rcx 0x0 0 rdx 0x0 0 rsi 0x0 0 rdi0x0 0 rbp 0x0 0x0 rsp 0x7fffffffe0600x7fffffffe060 r8 0x0 0 r9 0x0 0 r10 0x0 0 r11 0x0 0 r12 0x0 0 r13 0x0 0 r14 0x0 0 r15 0x0 0 rip 0x40008c 0x40008c <carry> eflags 0xa96 [ PF AF SF IF OF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 i r é a abreviação de info registers. Ou seja, podemos abreviar o comando fazendo: (gdb) i r que dá a mesma saída. Para selecionar apenas um registrador usa-se a sintaxe: i r <nome_do_registrador> O <nome_do_registrador> é um dos nomes citados no escopo info registers. AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com Para análise de overflow analisaremos primeiramente o registrador eflags que guarda constantes a respeito da última operação: (gdb) i r eflags eflags 0xa96 [ PF AF SF IF OF ] Este comando dá expressões chamadas de flags, que mostram características a respeito da última operação. Por exemplo, a flag IF. As flags mais conhecidas: IF(Flag de interrupção) : se esta flag estiver presente significa que o sistema teve que criticamente parar por algum motivo devido a privilágio de sistema. Essa flag não será estudada. OF (Flag de overflow): essa flag presente indica que a última operação ocorreu overflow. AF (Flag de ajuste de carry): essa flag presente indica que a última operação ocorreu um carry que “espirrou” fora dos 4 bits menos significativos. SF (Flag de sinal): essa flag indica que na última operação foi negativa e em complemento de 2. PF (Flag de Paridade): essa flag indica que a última operação o resultado retornado foi par. As flags só são acionadas por operações lógicas ou aritméticas, ou seja a operação MOV não é considerada pelo registrador eflags pois MOV é uma instrução de referência. Conclusão Concluímos que nas operações aritméticas deve-se cuidar com operações de soma e subtração, uma vez que estas são feitas bit-a-bit e o Assembly as usa indiscriminadamente. Resolvendo o problema Podemos resolver o problema, por exemplo, no código: section .text global _start _start: c1: mov eax, -1 add eax, 2 c2: adc ebx, eax b1: mov ecx, 2 sub ecx, 3 b2: sbb edx, ecx fim: AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com mov rax, 1 mov rbx, 0 int 0x80 que no terminal, montando e linkando depois iniciando no gdb, por exemplo, se o nome do arquivo for nome_do_arquivo: alunos@CCSC-CC-LCT-32:~/Área de Trabalho$ nasm -f elf64 nome_do_arquivo.asm alunos@CCSC-CC-LCT-32:~/Área de Trabalho$ ld nome_do_arquivo.asm .o -o nome_do_arquivo.asm .x alunos@CCSC-CC-LCT-32:~/Área de Trabalho$ gdb nome_do_arquivo.asm .x e no gdb, iniciando os breakpoints nos lugares especificados no corpo do código temos a saída (gdb) b c1 Ponto de parada 1 at 0x400080 (gdb) b c2 Ponto de parada 2 at 0x400088 (gdb) b b1 Ponto de parada 3 at 0x40008a (gdb) b b2 Ponto de parada 4 at 0x400092 (gdb) b fim Ponto de parada 5 at 0x400094 (gdb) r Starting program: /home/alunos/Área de Trabalho/a05e03.x warning: no loadable sections found in added symbol-file system-supplied DSO at 0x7ffff7ffd000 Breakpoint 1, 0x0000000000400080 in c1 () (gdb) c Continuando. Breakpoint 2, 0x0000000000400088 in c2 () (gdb) p/c $eax $1 = 1 '\001' (gdb) p/d $eax $2 = 1 (gdb) c Continuando. Breakpoint 3, 0x000000000040008a in b1 () (gdb) p/d $eax $3 = 1 (gdb) p/d $ebx $4 = 2 (gdb) c Continuando. Breakpoint 4, 0x0000000000400092 in b2 () (gdb) p/d $ecx $5 = -1 (gdb) p/x $ecx $6 = 0xffffffff (gdb) i r eflags eflags 0x297 [ CF PF AF SF IF ] (gdb) c Continuando. AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com Breakpoint 5, 0x0000000000400094 in fim () (gdb) p/x $edx $7 = 0x0 (gdb) p/d $edx $8 = 0 (gdb) i r eflags eflags 0x257 [ CF PF AF ZF IF ] As operações com carry foram devidamente satisfeitas, solucionando o problema e eliminando o overflow, resultando em apenas uma flag de carry. Adc pega a carry anterior e adiciona. Adc pega o dígito que “espirra”. Se adc não tiver carry é igual a add, caso contrário, atua no carry. Operações LÓGICAS Estudaremos a seguir, operações do tipo LÓGICAS, as conhecidas operações AND,OR,XOR,NOT,...etc. Para isso, usaremos o seguinte código: section .data v1: dw 0xfffb ; 1111.1111 1111.1011 v2: dw 0xaaaa ; 1010.1010 1010.1010 section .bss v1orv2: resw 1 v1andv2: resw 1 v1xorv2: resw 1 section .text global _start _start: mov ax, [v1] mov bx, [v2] mov cx, [v2] btesteum: ; or reg x reg or cx, ax btestedois: AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com ; or mem x reg mov [v1orv2], ax or [v1orv2], bx btestetres: ; or reg, imm or bx, 0x00ff btestequatro: ; and mov ax, [v1] mov bx, [v2] mov cx, [v2] btestecinco: ; and reg x reg and cx, ax btesteseis: ; and mem x reg mov [v1andv2], ax and [v1andv2], bx btestesete: ; and reg, imm and bx, 0x00ff btesteoito: xorlabel: ; xor mov ax, [v1] mov bx, [v2] mov cx, [v2] btestenove: ; xor reg x reg xor cx, ax ; xor mem x reg mov [v1xorv2], ax xor [v1xorv2], bx ; xor reg, imm xor bx, 0x00ff fim: mov rax, 1 mov rbx, 0 int 0x80 montando e linkando depois, no gdb, por exemplo, o nome do meu arquivo é “a05e04.asm”: AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com alunos@CCSC-CC-LCT-32:~/Área de Trabalho$ nasm -f elf64 a05e04.asm alunos@CCSC-CC-LCT-32:~/Área de Trabalho$ ld a05e03.o -o a05e03.x alunos@CCSC-CC-LCT-32:~/Área de Trabalho$ gdb a05e04.x No gdb, iniciando os breakpoints: (gdb) b btesteum Ponto de parada 1 at 0x4000c8 (gdb) b btestedois Ponto de parada 2 at 0x4000cb (gdb) b btestetres Ponto de parada 3 at 0x4000db (gdb) b btestequatro Ponto de parada 4 at 0x4000e0 (gdb) b btestecinco Ponto de parada 5 at 0x4000f8 (gdb) b btesteseis Ponto de parada 6 at 0x4000fb (gdb) b btestesete Ponto de parada 7 at 0x40010b (gdb) b btesteoito Ponto de parada 8 at 0x400110 (gdb) b btestenove Ponto de parada 9 at 0x400128 (gdb) b fim Ponto de parada 10 at 0x400140 A partir disso, você pode testar o conteúdo dos registradores com o comando: Se quiser o conteúdo em binário: p/t $<nome_do_registrador> Se quiser o conteúdo em hexadecimal: p/x $<nome_do_registrador> Se quiser o conteúdo em decimal: p/d $<nome_do_registrador> Se quiser o conteúdo em caracteres ASCII: p/c $<nome_do_registrador> para iniciar a execução use run: (gdb) r para saltar em cada breakpoint use continue: (gdb) c Exercício 1.Teste em binário as operações lógicas em cada registrador. 2.Qual instrução é unária e qual instrução é binaria neste código? AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com AUTOR: SERGIO SOUZA NOVAK e-mail:serginhosnovak@hotmail.com
Compartilhar