Este termo é utilizado para dizer ao programador que ao utilizar esta opção, estará exibindo o código assembly da função escrita em linguagem C gerada pelo compilador. Nesta hora você perceberá que aparecerá mesclado ao seu código C instruções assembly que é o que realmente será executado.
Quando criamos um programa, geralmente não nos importamos com o código assembly que esta sendo gerado pelo compilador. Porém em algumas situações bem específicas como criar um programa auto mutável, criação de vírus de computador, verificação de otimizações geradas pelo compilador, entre outras, este conhecimento se faz necessário.
Vou utilzar a "IDE" de desenvolvimento Visual C++ 6.0 para demostrações. Criei um programa simples em C, conforme segue abaixo:
int main( int argc, char ** argv ){
int x;
int y;
int k;
x = 0;
y = 1;
k = 2;
return 0;
}
Conforme observado nas linhas de código acima, este programa apenas cria 3 variaveis do tipo int, atribui valores a ela e encerra. Vou exemplificar o mesmo programa agora com as instruções assembly mescladas:
85: int main( int argc, char ** argv ){
0040104E push ebp
0040104F mov ebp,es
00401051 sub esp,0Ch
00401054 mov dword ptr [ebp-0Ch],0CCCCCCCCh
0040105B mov dword ptr [ebp-8],0CCCCCCCCh
00401062 mov dword ptr [ebp-4],0CCCCCCCCh
86:
87: int x;
88: int y;
89: int k;
90:
91: x = 0;
00401069 mov dword ptr [x],0
92: y = 1;
00401070 mov dword ptr [y],1
93: k = 2;
00401077 mov dword ptr [k],2
94:
95: return 0;
0040107E xor eax,eax
96:
97: }
00401080 mov esp,ebp
00401082 pop ebp
00401083 ret
A mesclagem do código fica da forma exibida acima, onde vem a instrução C e em seguida as instruções assembly necessárias. Cada linha de instrução C pode ser composta por diversas instruções assembly. As instruções C possuem sempre no início da linha o número da linha dentro do programa. A primeira linha tem o número 85 devido o fato que até o programa ser executado houve uma série de instruções executadas pelo sistema operacional com o objetivo de carregar seu programa na memória, porém não vou entrar neste detalhe pois não é o foco deste artigo. Quando a função main é chamada na linha 85, a primeira coisa que ocorre é a execução do prólogo da função que o compilador escreve de forma automática. O objetivo do prólogo é o seguinte:
- Armazenar o estado atual dos registradores do processador para após o término da função os mesmos serem recuperados. Estes valores são armazenados na pilha, que é uma região de memória do seu programa que armazena informações temporáriamente;
- O compilador identifica a quantidade de memória necessária na pilha de sua função, e já subtrai do tamanho de sua pilha o tamanho de todas as suas variáveis de escopo local;
- Inicializa as variáveis locais com um valor inválido;
Segue o trecho que se refere ao prólogo desta função:
0040104E push ebp -> Coloca o valor do registrador EBP na pilha;
0040104F mov ebp,esp -> Copia o valor do registrador ESP para o registrador EBP;
00401051 sub esp,0Ch -> Subtrai do valor do registrador ESP pelo valor 0c hexadecimal (12 em decimal) e grava no próprio ESP
00401054 mov dword ptr [ebp-0Ch],0CCCCCCCCh -> Copia o valor 0CCCCCC... para o endereço de memória especificado em "ebp-0ch"
0040105B mov dword ptr [ebp-8],0CCCCCCCCh -> Copia o valor 0CCCCCC... para o endereço de memória especificado em "ebp-8"
00401062 mov dword ptr [ebp-4],0CCCCCCCCh -> Copia o valor 0CCCCCC... para o endereço de memória especificado em "ebp-4"
Neste trecho somos apresentados a algumas instruções e operadores assembly:
- push -> Sua função é colocar um valor na pilha;
- mov -> Sua função é copiar um valor;
- EBP -> registrador de deslocamento do processador "BASE POINTER". Neste registrador fica armazenado o endereço de memória da base da pilha;
- ESP -> registrador de deslocamento do processador "STACK POINTER". Neste registrador fica armazenado o endereço do topo da pilha;
- dword ptr -> indica que o valor é um ponteiro para um DWORD, 32 bits;
- [] -> os valores que estão entre colchetes, indica que o conteúdo é um endereço e o valor que será retornado é o conteúdo deste endereço;
- h -> quando sucede um valor indica que o valor está expresso em notação hexadecimal.
Após o prólogo, vem as instruções colocadas dentro da função que no caso foram somente a atribuição de valores às variáveis declaradas, conforme segue:
00401069 mov dword ptr [x],0 -> atribui o valor 0 a variável x que é equivalente a [ebp-4]
00401070 mov dword ptr [y],1 -> atribui o valor 1 a variável y que é equivalente a [ebp-8]
00401077 mov dword ptr [k],2 -> atribui o valor 2 a variável k que é equivalente a [ebp-12]
0040107E xor eax,eax -> Grava 0 no registrador EAX
Neste trecho somos apresentados a mais algumas instruções e operadores assembly:
xor -> efetua um "ou exclusivo", ou seja, o bit somente será ligado se os dois bits envolvidos na operação estiverem com estados diferentes;
EAX -> registrador de uso geral. Por convenção em compiladores C ele é utilizado para armazenar o valor de retorno de uma função;
Agora vem o trecho do código chamado de epílogo, sua função é:
- Restaurar o estado anterior dos registradores;
- Retornar a função chamadora;
00401080 mov esp,ebp -> copia o valor de EBP para o ESP
00401082 pop ebp -> retira o valor da pilha e coloca em EBP
00401083 ret -> retorna a função chamadora
Neste trecho somos apresentados a mais algumas instruções e operadores assembly:
pop -> sua função é retirar um valor da pilha;
ret -> sua função é retornar ao endereço de memória da função chamadora;
*Informações adicionais:
- Do lado de cada instrução assembly existe um número semelhante a este "00401080". Estes números são os endereços de memória que indicam a localização da instrução assembly.
- Eu cito tanto a instrução pop, quanto a instrução push que retiram e colocam dados na pilha. Esta instrução leva em consideração o posicionamento atual da pilha que fica por convenção armazenada no registrador ESP.
Esta é uma pequena exemplicação do que realmente um computador está fazendo por trás do seu código C. Expliquei de maneira extremamente sintética vários conceitos envolvidos neste artigo. Continuarei posteriormente com outros exemplos.
Obrigado por sua atenção.
Nenhum comentário:
Postar um comentário