Interface Hardware-Software
Camadas de Software
Camada de SW
de baixo nível
Camada de SW de Baixo Nível
Mais dependente do HW
– Embora haja muitas partes que sejam independentes
Permite acesso controlado e gerenciamento de dispositivos
de HW
– E/S
– Escalonamento de Processo
– Inicialização de dispositivos
Código mais complexo de escrever e depurar
– Deve levar em conta detalhes de HW
– Se linguagem de programação utilizada for de baixo nível,
mais linhas de código são necessárias
Linguagens de Programação Utilizadas
Mesmo sendo código de baixo nível, não implica que o
código deve ser escrito todo em assembly
Maior parte do código é escrita em linguagens de alto nível
- Pequenas partes específicas escritas em assembly
Existem muitas bibliotecas ou rotinas que permitem
acesso baixo nível a linguagens de alto nível
– POSIX – Rotinas referentes a processos
Quando Usar Assembly?
Controle total de que partes do hardware devem ser
acessadas
– Registradores específicos
Garantir que uma rotina execute de forma mais
determinística
- Compiladores de linguagem de alto nível podem modificar a
forma de execução pretendida pelo programador
- Importante para sistemas de tempo real
Rotinas específicas que linguagens de alto nível não dão
suporte
– Ex: Partes do Boot loader
Por Que Não Usar Assembly em Todo Código ?
Baixa produtividade
– Muitas linhas de código para fazer o que poucas linhas de
código em alto nível fazem
– Depuração mais lenta e complexa
Alto custo de manutenção
Baixa portabilidade
– Códigos específicos a uma ISA
– Nem todo código de baixo nível é dependente do HW
Alternativa:
Uso conjunto de assembly e
linguagem de alto nível
Integração C-Assembly
Podemos combinar C e Assembly de diferentes formas
Módulos C e Assembly separados
– Integração através de chamadas de rotinas
– Durante a link-edição é feita a integração
Código assembly inline
Integrando Módulos C e Assembly Separados
extern imprimir SECTION .data var dq 3.5 SECTION .text global main main: push dword [var+4]push dword [var] call imprimir add esp, 8
void imprimir(double valor){ printf(“%lf”,valor); }
Montador
Compilador
Link-editor
Teste1.asm
Teste2.c
Teste1.exe
Teste1.o
Teste2.o
Duas Formas de Integrar Módulos C e Assembly
extern imprimir SECTION .data var dq 3.5 SECTION .text global main main: push dword [var+4]push dword [var] call imprimir add esp, 8
void imprimir(double valor){ printf(“%lf”,valor); } SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>
extern int soma(int,int,int); int main() int a,b,c,value; value = soma(a,b,c); }
Assembly
chamando C
C chamando
Assembly
Chamando Rotinas Assembly em C
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int, int); int main() int a,b,c,value; value = soma(a,b,c); }
C chamando
Assembly
Existem convenções
para a passagem de
parâmetros do código C
para assembly e valores
de retorno
Algumas regras devem
ser seguidas no código
assembly quanto ao uso
de registradores
ESP
ESP
Passagem de Parâmetros em C
End - 16 End - 12 End - 8 End -4 Endsoma(a,b,c)
ESP
ESP
ESP
a
b
c
EIP
Em C os parâmetros são
inseridos na pilha da
direita para a esquerda
(Right-pusher)
Stack Frame
Stack frame contém as
variáveis locais da
função chamada
– Stack frame cresce de
endereços maiores para
menores
– Guarda também o
começo do stack frame
da rotina que chamou a
função
Por convenção, o
registrador
EBP
guarda o
endereço do começo do
stack frame
– Conhecido como o Frame Pointer (FP)
O Que Acontece na Chamada de Rotina Assembly
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); }
C chamando
Assembly
mov eax,1mov [ebp-4],eax; a=1 mov eax,4 mov [ebp-8],eax; b=4 mov eax,5 mov [ebp-12],eax; c=5 push [ebp-12] push [ebp-8] push [ebp-4] call soma add esp,12 mov [ebp-16],eax
Equivalentes
Convenção de Retorno de Funções
Funções em C utilizam determinados registradores para
retorno, portanto o código C sempre assume que as rotinas
em assembly também utilizem estes registradores
– Valores de 8,16 e 32 bits são devolvidos em
EAX
– Valores de 64 bits são devolvidos em
EDX:EAX
– Valores de ponto flutuante são devolvidos em
ST(0)
Regras de Escrita do Código Assembly
O código assembly chamado por C pode utilizar qualquer
registrador, porém deve preservar EBP,EBX,ESI e EDI
– Preservar significa colocar na pilha (salvar em memória),
antes da utilização
O assembly deve utilizar para retorno os registradores de
retorno padrão
É aconselhável começar a rotina com a instrução enter e
antes do retorno colocar a instrução leave
Instrução ENTER
Instrução para preparar um novo stack frame
Empilha EBP (ponteiro do stack frame anterior), move o valor de
ESP(topo da pilha) para EBP e aloca
bytes
para armazenar
variáveis locais (subtrai
bytes
de ESP e atualiza ESP)
nivel
informa o aninhamento da rotina, sendo 0 o maior nível– Informa a quantidade de stack frames pointers (FPs) devem
ser copiados para o novo stack frame
– Permite o acesso de rotinas de menor nível a variáveis de
maior nível
Forma Geral
Exemplo: ENTER
enter
16
,
0
push ebp;
(1)
mov ebp,esp;
(2)
sub
esp
,16;
(3)
Equivalentes
End End - 12 End - 8 End - 4 End - 16ESP
EBPa
ESP
EBP
ESP
EBPa = EBP antigo
(1)
(2)
Instrução LEAVE
Instrução para reverter as ações do
enter
Copia EBP para ESP para liberar espaço alocado, e então
restaura valor antigo do EBP
– Como consequencia, restaura valor antigo de ESP antes de
entrar na rotina
Forma Geral
leave
Exemplo: LEAVE
leave
mov esp,ebp;
(1)
pop
ebp
;
(2)
Equivalentes
End End - 4 End - 8 End - 12 End - 16ESP
EBPa
ESP
EBP
ESP
EBPa = EBP antigo
(2)
(2)
EBPa
EBP
ESP
(1)
End + 4Compilando e Linkando os Módulos
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int,int); int main()
int a=1,b=4,c=5,value; value = soma(a,b,c); }
Para toda rotina em um
módulo externo deve-se
utilizar a palavra extern
Para a rotina externa chamada
deve-se utilizar a palavra
global
main.c
soma.asm
Para compilar e linkar:
nasm –f elf soma.asm
Execução do Código
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); } End End - 24 End - 20 End - 16 End - 12
5
ESP
EBPa
EBP
End - 8 End - 4Execução do Código
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); }
5
4
ESP
EBPa
EBP
End End - 24 End - 20 End - 16 End - 12 End - 8 End - 4Execução do Código
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); }
4
1
ESP
EBPa
EBP
End End - 24 End - 20 End - 16 End - 12 End - 8 End - 45
Execução do Código
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); }
4
1
EIP
ESP
EBPa
EBP
End End - 24 End - 20 End - 16 End - 12 End - 8 End - 45
Execução do Código
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); }
4
1
EIP
EBPa
ESP,EBP
-
EAX
End End - 24 End - 20 End - 16 End - 12 End - 8 End - 45
Execução do Código
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); }
4
1
EIP
EBPa
ESP,EBP
1
EAX
End End - 24 End - 20 End - 16 End - 12 End - 8 End - 45
Execução do Código
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); }
4
1
EIP
EBPa
ESP,EBP
5
EAX
End End - 24 End - 20 End - 16 End - 12 End - 8 End - 45
Execução do Código
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); }
4
1
EIP
EBPa
ESP,EBP
10
EAX
End End - 24 End - 20 End - 16 End - 12 End - 8 End - 45
Execução do Código
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); }
4
1
EIP
EBPa
ESP
End End - 24 End - 20 End - 16 End - 12 End - 8 End - 45
10
EBP
EBPa
EAX
Execução do Código
SECTION .text global soma soma: enter 0,0 mov eax,[ebp+8] add eax,[ebp+12] add eax,[ebp+16] leave ret #include <stdio.h>extern int soma(int,int,int); int main() int a=1,b=4,c=5,value; value = soma(a,b,c); }
4
1
EIP
EBPa
ESP
End End - 24 End - 20 End - 16 End - 12 End - 8 End - 45
10
EBP
EBPa
EAX
Outro Exemplo: Soma de Vetor
SECTION .text
global somaVetor
somaVetor: enter 0,0
mov edx,[ebp+8]; vetor mov ecx,[ebp+12]; SIZE push ebx; preservar mov ebx,0 mov eax,0 add_loop: add eax,[edx+ebx*4] inc ebx loop add_loop pop ebx leave ret #include <stdio.h> #define SIZE 4
extern int somaVetor(int*,int); int main() int vetor[]= {4,8,10,12}; int soma; soma = somaVetor(vetor,SIZE); printf(“Soma = %d”,soma); }
Chamando Funções C em Assembly
SECTION .data vetor dd 5,10,2 SIZE dd 3 SECTION .text extern min global main main: push dword [SIZE]push dword vetor call min
add esp,8
int min(int* vetor, int size){ int i, menor = vetor[0];
for (i = 0; i<size; i++){ if (menor > vetor[i]) menor = vetor[i]; } return menor; }
Assembly
chamando C
Utilizam-se as mesmas
convenções para a
passagem de
parâmetros do código C
para assembly e valores
de retorno
ESP
ESP
Passagem de Parâmetros em C
End End - 4 End - 8 End - 12 End - 16int min(vetor,size)
ESP
ESP
size
vetor
EIP
No código assembly
deve-se empilhar os
parâmetros da direita
para a esquerda
call min
Exemplo: Chamando a Função min e printf
SECTION .data vetor dd 5,10,2 SIZE dd 3 Msg db “min = %d",0x0a,0x00 SECTION .text extern min, printfglobal main
main: push dword [SIZE]
push dword vetor call min add esp,8 push eax push dword Msg call printf add esp,8
int min(int* vetor, int size){ int i, menor = vetor[0];
for (i = 0; i<size; i++){ if (menor > vetor[i]) menor = vetor[i]; }
return menor; }
Código Assembly Inline
Podemos também inserir código assembly diretamente no
código C
Aconselhável apenas para pequenos trechos de código
– Senão o código fica bastante ilegível
Contudo, a sintaxe aceita pela maioria dos compiladores C
é a sintaxe AT&T
Algumas Diferenças da Sintaxe AT&T (1)
Para utilizar registradores, deve-se preceder o nome do
registrador com %
Os lugares dos operandos destino e fonte na instrução são
invertidos
Deve-se especificar na instrução, o tamanho dos
operandos
NASM AT&T
mov eax,ebx movl %ebx,%eax
mov dx, ax movw %ax, %dx
inc cl incb %cl
Algumas Diferenças da Sintaxe AT&T (2)
Constantes e imediatos devem ser precedidos $
Para especificar endereçamento indireto deve-se utilizar (),
em vez de []
Para utilizar endereçamento de base indexado escalar,
deve-se colocar (base,indice,escala)
NASM AT&T
mov eax,5 movl $5,%eax
add bx, word[bp] addw (%bp),%bx
Exemplo: Calculando Soma Vetor Inline
#include <stdio.h> int vetor[] = {4,8,10,12}; int soma = 0; int main() { asm("movl $vetor,%edx;" "movl $0,%ebx;" "movl $4,%ecx;" "movl $0,%eax;""add_loop: addl (%edx,%ebx,4),%eax;" "incl %ebx;"
"loop add_loop;" "movl %eax,soma"
);
printf("\nA soma do vetor eh = %d\n",soma); return 0;
}