Share via


Arquitetura x86

O processador Intel x86 usa uma arquitetura cisc (computador conjunto de instruções) complexa, o que significa que há um número modesto de registros de finalidade especial em vez de grandes quantidades de registros de uso geral. Isso também significa que instruções complexas de finalidade especial serão predominados.

O processador x86 rastreia sua herança pelo menos até o processador Intel 8080 de 8 bits. Muitas peculiaridades no conjunto de instruções x86 são devido à compatibilidade com versões anteriores com esse processador (e com sua variante Zilog Z-80).

O Microsoft Win32 usa o processador x86 no modo simples de 32 bits. Esta documentação se concentrará apenas no modo simples.

Registros

A arquitetura x86 consiste nos seguintes registros inteiros sem privilégios.

Eax

Acumulador

Ebx

Registro base

ecx

Registro de contador

Edx

Registro de dados – pode ser usado para acesso à porta de E/S e funções aritméticas

Esi

Registro de índice de origem

Edi

Registro de índice de destino

Ebp

Registro de ponteiro base

Esp

Ponteiro de pilha

Todos os registros inteiros são de 32 bits. No entanto, muitos deles têm subregistros de 16 ou 8 bits.

ax

16 bits baixos de eax

Bx

16 bits baixos de ebx

cx

16 bits baixos de ecx

dx

16 bits baixos de edx

si

16 bits baixos de esi

di

16 bits baixos de edi

Bp

16 bits baixos de ebp

sp

Baixos 16 bits de esp

al

8 bits baixos de eax

ah

8 bits altos de ax

Bl

8 bits baixos de ebx

Bh

8 bits altos de bx

Cl

8 bits baixos de ecx

ch

8 bits altos de cx

Dl

8 bits baixos de edx

dh

8 bits altos de dx

Operar em um subregistro afeta apenas o subregistro e nenhuma das partes fora do subregistro. Por exemplo, armazenar no registro de ax deixa os 16 bits altos do registro eax inalterados.

Ao usar o ? (Avaliar expressão) comando , os registros devem ser prefixados com um sinal "at" ( @ ). Por exemplo, você deve usar ? @ax em vez de ? ax. Isso garante que o depurador reconheça ax como um registro em vez de um símbolo.

No entanto, o (@) não é necessário no comando r (Registros). Por exemplo, r ax=5 sempre será interpretado corretamente.

Dois outros registros são importantes para o estado atual do processador.

Eip

ponteiro de instrução

sinalizadores

sinalizadores

O ponteiro de instrução é o endereço da instrução que está sendo executada.

O registro de sinalizadores é uma coleção de sinalizadores de bit único. Muitas instruções alteram os sinalizadores para descrever o resultado da instrução. Esses sinalizadores podem ser testados por instruções de salto condicional. Consulte Sinalizadores x86 para obter detalhes.

Convenções de chamada

A arquitetura x86 tem várias convenções de chamada diferentes. Felizmente, todos seguem as mesmas regras de preservação de registro e retorno de função:

  • As funções devem preservar todos os registros, exceto eax, ecx e edx, que podem ser alterados em uma chamada de função e esp, que devem ser atualizados de acordo com a convenção de chamada.

  • O registro eax receberá valores retornados da função se o resultado for 32 bits ou menor. Se o resultado for 64 bits, o resultado será armazenado no par edx:eax .

Veja a seguir uma lista de convenções de chamada usadas na arquitetura x86:

  • Win32 (__stdcall)

    Os parâmetros de função são passados na pilha, enviados por push da direita para a esquerda e o computador chamado limpa a pilha.

  • Chamada de método C++ nativo (também conhecida como thiscall)

    Os parâmetros de função são passados na pilha, enviados por push da direita para a esquerda, o ponteiro "this" é passado no registro ecx e o computador chamado limpa a pilha.

  • COM (__stdcall para chamadas de método C++)

    Os parâmetros de função são passados na pilha, enviados por push da direita para a esquerda, o ponteiro "this" é enviado por push na pilha e, em seguida, a função é chamada. O computador chamado limpa a pilha.

  • __fastcall

    Os dois primeiros argumentos DWORD ou menores são passados nos registros ecx e edx . Os parâmetros restantes são passados na pilha, enviados da direita para a esquerda. O computador chamado limpa a pilha.

  • __cdecl

    Os parâmetros de função são passados na pilha, enviados por push da direita para a esquerda e o chamador limpa a pilha. A convenção de chamada __cdecl é usada para todas as funções com parâmetros de comprimento variável.

Exibição do depurador de registros e sinalizadores

Aqui está uma exibição de registro de depurador de exemplo:

eax=00000000 ebx=008b6f00 ecx=01010101 edx=ffffffff esi=00000000 edi=00465000
eip=77f9d022 esp=05cffc48 ebp=05cffc54 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000286

Na depuração no modo de usuário, você pode ignorar o iopl e toda a última linha da exibição do depurador.

Sinalizadores x86

No exemplo anterior, os códigos de duas letras no final da segunda linha são sinalizadores. Esses são registros de bit único e têm uma variedade de usos.

A tabela a seguir lista os sinalizadores x86:

Código de sinalizador Nome do Sinalizador Valor Status do Sinalizador Descrição
de Sinalizador de estouro 0 1 nvov Sem estouro – Estouro
df Sinalizador de direção 0 1 updn Direção para cima - Direção para baixo
if Sinalizador de interrupção 0 1 diei Interrupções desabilitadas – Interrupções habilitadas
sf Sinalizador de Sinal 0 1 plng Positivo (ou zero) – Negativo
Zf Sinalizador Zero 0 1 nzzr Diferente de zero - Zero
af Sinalizador auxiliar de transporte 0 1 naac Sem transporte auxiliar - Transporte auxiliar
pf Sinalizador de paridade 0 1 Pepo Paridade estranha - Paridade mesmo
cf Sinalizador de Transporte 0 1 nccy Sem transporte - Transporte
Tf Sinalizador de interceptação Se tf for igual a 1, o processador gerará uma exceção STATUS_SINGLE_STEP após a execução de uma instrução. Esse sinalizador é usado por um depurador para implementar o rastreamento em etapa única. Ele não deve ser usado por outros aplicativos.
iopl Nível de privilégio de E/S Nível de privilégio de E/S Esse é um inteiro de dois bits, com valores entre zero e 3. Ele é usado pelo sistema operacional para controlar o acesso ao hardware. Ele não deve ser usado por aplicativos.

Quando os registros são exibidos como resultado de algum comando na janela Comando do Depurador, é o sinalizador status exibido. No entanto, se você quiser alterar um sinalizador usando o comando r (Registros), consulte-o pelo código de sinalizador.

Na janela Registros do WinDbg, o código de sinalizador é usado para exibir ou alterar sinalizadores. Não há suporte para o sinalizador status.

Veja um exemplo. Na exibição de registro anterior, o sinalizador status ng é exibido. Isso significa que o sinalizador de sinal está atualmente definido como 1. Para alterar isso, use o seguinte comando:

r sf=0

Isso define o sinalizador de sinal como zero. Se você fizer outra exibição de registro, o código ng status não será exibido. Em vez disso, o código pl status será exibido.

O Sinalizador de Sinal, o Sinalizador Zero e o Sinalizador de Transporte são os sinalizadores mais comumente usados.

Condições

Uma condição descreve o estado de um ou mais sinalizadores. Todas as operações condicionais no x86 são expressas em termos de condições.

O assembler usa uma abreviação de uma ou duas letras para representar uma condição. Uma condição pode ser representada por várias abreviações. Por exemplo, AE ("acima ou igual") é a mesma condição que NB ("não abaixo"). A tabela a seguir lista algumas condições comuns e seu significado.

Nome da condição Flags Significado

Z

ZF=1

O resultado da última operação foi zero.

NZ

ZF=0

O resultado da última operação não foi zero.

C

CF=1

A última operação exigiu um transporte ou empréstimo. (Para inteiros sem sinal, isso indica estouro.)

NC

CF=0

A última operação não exigiu um transporte ou empréstimo. (Para inteiros sem sinal, isso indica estouro.)

S

SF=1

O resultado da última operação tem seu conjunto de bits alto.

NS

SF=0

O resultado da última operação tem seu bit alto claro.

O

OF=1

Quando tratada como uma operação de inteiro com sinal, a última operação causou um estouro ou um subfluxo.

Não

OF=0

Quando tratada como operação de inteiro com sinal, a última operação não causou um estouro ou um fluxo inferior.

As condições também podem ser usadas para comparar dois valores. A instrução cmp compara seus dois operandos e define sinalizadores como se um operando subtraído do outro. As condições a seguir podem ser usadas para marcar o resultado de cmpvalue1, value2.

Nome da condição Flags Significado após uma operação CMP.

E

ZF=1

value1 == value2.

NE

ZF=0

value1 != value2.

GE NL

SF=OF

value1>= value2. Os valores são tratados como inteiros com sinal.

LE NG

ZF=1 ou SF!=OF

value1<= value2. Os valores são tratados como inteiros com sinal.

G NLE

ZF=0 e SF=OF

value1>value2. Os valores são tratados como inteiros com sinal.

L NGE

SF!=OF

value1<value2. Os valores são tratados como inteiros com sinal.

AE NB

CF=0

value1>= value2. Os valores são tratados como inteiros sem sinal.

BE NA

CF=1 ou ZF=1

value1<= value2. Os valores são tratados como inteiros sem sinal.

Um NBE

CF=0 e ZF=0

value1>value2. Os valores são tratados como inteiros sem sinal.

B NAE

CF=1

value1<value2. Os valores são tratados como inteiros sem sinal.

Normalmente, as condições são usadas para agir no resultado de um cmp ou instrução de teste . Por exemplo,

cmp eax, 5
jz equal

compara o registro eax com o número 5 calculando a expressão (eax - 5) e definindo sinalizadores de acordo com o resultado. Se o resultado da subtração for zero, o sinalizador zr será definido e a condição jz será verdadeira para que o salto seja obtido.

Tipos de dados

  • byte: 8 bits

  • word: 16 bits

  • dword: 32 bits

  • qword: 64 bits (inclui duplos de ponto flutuante)

  • tword: 80 bits (inclui duplos estendidos de ponto flutuante)

  • oword: 128 bits

Notação

A tabela a seguir indica a notação usada para descrever as instruções de linguagem do assembly.

Notation Significado

r, r1, r2...

Registros

m

Endereço de memória (consulte a seção Modos de Endereçamento bem-sucedidos para obter mais informações.)

#n

Constante imediata

r/m

Registrar ou memória

r/#n

Registrar ou constante imediata

r/m/#n

Registrar, memória ou constante imediata

Cc

Um código de condição listado na seção Condições anterior.

T

"B", "W" ou "D" (byte, word ou dword)

accT

Acumulador de tamanho T : al se T = "B", ax if T = "W" ou eax if T = "D"

Modos de endereçamento

Há vários modos de endereçamento diferentes, mas todos eles assumem o formato T ptr [expr], em que T é algum tipo de dados (consulte a seção Tipos de Dados anterior) e expr é uma expressão que envolve constantes e registros.

A notação para a maioria dos modos pode ser deduzida sem muita dificuldade. Por exemplo, BYTE PTR [esi+edx*8+3] significa "pegue o valor do registro esi , adicione a ele oito vezes o valor do registro edx , adicione três e acesse o byte no endereço resultante".

Pipelining

O Pentium é um problema duplo, o que significa que ele pode executar até duas ações em um tique de relógio. No entanto, as regras sobre quando ele é capaz de executar duas ações ao mesmo tempo (conhecidas como emparelhamento) são muito complicadas.

Como o x86 é um processador CISC, você não precisa se preocupar com slots de atraso de salto.

Acesso sincronizado à memória

As instruções carregar, modificar e armazenar podem receber um prefixo de bloqueio , que modifica a instrução da seguinte maneira:

  1. Antes de emitir a instrução, a CPU liberará todas as operações de memória pendentes para garantir a coerência. Todas as pré-buscas de dados são abandonadas.

  2. Ao emitir a instrução, a CPU terá acesso exclusivo ao barramento. Isso garante a atomicidade da operação de carregamento/modificação/armazenamento.

A instrução xchg obedece automaticamente às regras anteriores sempre que troca um valor com memória.

Todas as outras instruções padrão para não bloqueio.

Previsão de Salto

Prevê-se que saltos incondicionals sejam tomados.

Prevê-se que os saltos condicionais sejam tomados ou não, dependendo se foram feitos na última vez em que foram executados. O cache para registrar o histórico de saltos é limitado em tamanho.

Se a CPU não tiver um registro de se o salto condicional foi feito ou não na última vez em que foi executado, ele prevê saltos condicionais para trás conforme feito e avanço de saltos condicionais como não realizados.

Alinhamento

O processador x86 corrigirá automaticamente o acesso de memória não atribuído, em uma penalidade de desempenho. Nenhuma exceção é gerada.

Um acesso à memória será considerado alinhado se o endereço for um múltiplo inteiro do tamanho do objeto. Por exemplo, todos os acessos BYTE são alinhados (tudo é um múltiplo inteiro de 1), os acessos do WORD a endereços par são alinhados e os endereços DWORD devem ser um múltiplo de 4 para serem alinhados.

O prefixo de bloqueio não deve ser usado para acessos de memória não atribuídos.