pbn/docs/opcodes.org

9.9 KiB
Raw Blame History

Códigos de operação

A passagem de operandos nas instruções pode envolver:

  • Registradores;
  • Endereços de memória;
  • Valores imediatos.

Quando consultamos o manual, dos códigos de operações, pode ser muito difícil entender o que utilizar e de que forma. Por isso, precisamos conhecer alguns conceitos básicos sobre a formação dos OpCodes no nível do código de máquina.

De modo simplificado, podemos dizer que os bytes dos códigos de operação da arquitetura Intel x86_64 seguem este formato:

[prefixos][operação][operandos]

Dentre as várias possibilidades, nós temos:

  • Prefixo REX, para habilitar operações de 64 bits.
  • Prefixo de tamanho de operandos (0x66), para alterar o tamanho padrão dos operandos.
  • Prefixo de tamanho de endereços (0x67), para possibilitar o uso de endereços de tamanhos diferentes do padrão.
  • Codificação de operandos com o byte ModR/M.
  • Codificação de endereços efetivos com o byte SIB.

Prefixo REX

Byte opcional da arquitetura x86_64 para:

  • Habilitar operandos de 64 bits;
  • Habilitar uso dos registradores r8 a r15;
  • Estender os campos da codificação de operandos.

Formato:

     ┌──────┬───┬───┬───┬───┐
Bits │ 7-4  │ 3 │ 2 │ 1 │ 0 │
     ├──────┼───┼───┼───┼───┤
Info │ 0100 │ W │ R │ X │ B │
     └──────┴───┴───┴───┴───┘
              │   │   │   └─── [B]ase extension
              │   │   └─────── Inde[x] extension
              │   └─────────── [R]egister extension
              └─────────────── [W]ide

Onde:

Bit Função
0100 Identifica o byte como prefixo REX
W Usa operandos de 64 bits
R Bit mais significativo para o campo reg do ModR/M
X Bit mais significativo para o campo index do byte SIB
B Bit mais significativo para o campo r/m (ModR/M) ou base (SIB)

Nota: os bits R, X e B acrescentam 1 bit (mais significativo) aos 3 bits dos campos relacionados, ampliando de 8 para 16 os valores possíveis.

Prefixo de tamanho de operando (operand-size override)

No conjunto de instruções x86, o tamanho dos operandos pode ser alterado por um prefixo especial com valor hexadecimal 0x66. Esse prefixo é utilizado para substituir o tamanho padrão dos operandos da instrução de acordo com o modo de operação (16, 32 ou 64 bits).

Efeito do prefixo 0x66:

Modo Sem prefixo Com prefixo
16 bits 16 bits 32 bits
32 bits 32 bits 16 bits
64 bits 32 bits 16 bits

Notas:

  • No modo 64 bits, o prefixo 0x66 não ativa operandos de 64 bits; para isso, utiliza-se o prefixo REX com o bit W=1.
  • O prefixo 0x66 é necessário para instruções que operam sobre registradores de 16 bits (ax, cx, r8w, etc.), pois o tamanho padrão no modo 64 é de 32 bits.

Prefixo de tamanho de endereço (address-size override)

O prefixo 0x67 altera o tamanho do cálculo de endereço, permitindo usar endereços de 16 ou 32 bits em modos onde estes não seriam o padrão.

  • No modo 64 bits, permite usar endereçamento de 32 bits;
  • No modo 32 bits, permite usar endereçamento de 16 bits;
  • No modo 16 bits, permite endereçamento de 32 bits.

Codificação de operandos

Em algumas situações, os operandos podem ser codificados como:

  • Byte ModR/M
  • Deslocamento (displacement)
  • Byte SIB

ModR/M

     ┌─────┬───────┬───────┐
Bits │ 7 6 │ 5 4 3 │ 2 1 0 │
     ├─────┼───────┼───────┤
Info │ mod │  reg  │  r/m  │
     └─────┴───────┴───────┘

Nota: a ordem de operandos de origem e destino varia com a instrução.

mod Significado
00 Endereçamento indireto sem deslocamento
01 Endereçamento indireto com deslocamento de 8 bits
10 Endereçamento indireto com deslocamento de 32 bits
11 Campo r/m é um registrador

Se campos reg e r/m forem registradores (mod = 11):

bits 8 bits 16 bits 32 bits 64 bits
000 al ax eax rax
001 cl cx ecx rcx
010 dl dx edx rdx
011 bl bx ebx rbx
100 ah / spl sp esp rsp
101 ch / bpl bp ebp rbp
110 dh / sil si esi rsi
111 bh / dil di edi rdi

Notas:

  • Os valores binários de 000 a 111 representam registradores quando mod=11.
  • No modo de 8 bits, os registradores spl, bpl, sil e dil só são válidos com prefixo REX. Sem REX, 100 a 111 correspondem a ah, ch, dh e bh.

Se o campo r/m for memória (mod != 11):

r/m mod 00 mod diferente de 00
000 [rax] [rax + disp8/disp32]
001 [rcx] [rcx + disp8/disp32]
010 [rdx] [rdx + disp8/disp32]
011 [rbx] [rbx + disp8/disp32]
100 SIB obrigatório SIB obrigatório
101 [disp32] [rbp + disp8/disp32]
110 [rsi] [rsi + disp8/disp32]
111 [rdi] [rdi + disp8/disp32]

Notas:

  • Quando r/m = 100, o byte seguinte é um byte SIB.
  • Com mod = 00 e r/m = 101, não há um registrador base e o dado seguinte é um deslocamento escrito em 4 bytes.

Deslocamento (displacement)

O deslocamento é um valor constante (signed) adicionado ao endereço base calculado a partir dos campos mod e r/m (e opcionalmente do byte SIB) para obter o endereço efetivo de memória.

O tipo e o tamanho do deslocamento são determinados pelo valor de mod:

mod Tipo Tamanho Observação
00 Nenhum 0 Com r/m = 101, há disp32 absoluto
01 disp8 1 byte (8 bits) Sempre somado ao endereço base
10 disp32 4 bytes (32 bits) Sempre somado ao endereço base

Notas:

  • O deslocamento é armazenado logo após o byte ModR/M ou após o byte SIB, se houver.
  • Ele é com sinal, ou seja:

    • Valores positivos aumentam o endereço base;
    • Valores negativos o reduzem.
  • O deslocamento é sempre somado ao endereço base calculado pelos campos r/m e SIB (se houver).
  • No caso especial de mod = 00 e r/m = 101, o deslocamento de 32 bits é o próprio endereço efetivo, sem registrador base.

Byte SIB

O byte SIB (Scale-Index-Base) é usado para formar endereços de memória mais complexos, da forma:

[base + index × scale + displacement]

Ele aparece obrigatoriamente após o byte ModR/M quando o campo mod != ~11 (o operando é memória) e o campo r/m = 100.

     ┌───────┬───────┬───────┐
Bits │  7 6  │ 5 4 3 │ 2 1 0 │
     ├───────┼───────┼───────┤
Info │ scale │ index │ base  │
     └───────┴───────┴───────┘

O campo scale (bits 76) representa o fator de multiplicação:

Bits Fator
00 1
01 2
10 4
11 8

O campo index (bits 53) determina o registrador que contém valor que será multiplicado pela escala:

Bits index
000 rax
001 rcx
010 rdx
011 rbx
100 (suprimido)
101 rbp
110 rsi
111 rdi

Notas:

  • O campo index também é codificado como registrador, como os campos reg e r/m.
  • Se index = 100 (rsp), o campo index é suprimido e o termo index × scale é ignorado.

O campo base (bits 20) representa o registrador que contém o endereço base para o cálculo do endereço efetivo:

Bits base
000 rax
001 rcx
010 rdx
011 rbx
100 rsp
101 rbp ou disp32
110 rsi
111 rdi

Nota: se base = 101 e mod = 00, não há registrador base e os 4 bytes seguintes são um disp32 (endereço absoluto).

Grupos de Instruções (Instruction Groups)

Alguns opcodes na arquitetura x86/x86_64 não definem uma única instrução, mas sim um grupo de instruções. Esses grupos utilizam um único byte de opcode base e o campo reg, do byte ModR/M (bits 53), como uma extensão para determinar a operação específica. Deste modo, é possível agrupar até 8 instruções diferentes com um mesmo opcode.

Formato geral:

[opcode] /n  [outros bytes...]

Onde:

  • opcode: byte base do grupo
  • /n: valor esperado no campo reg do byte ModR/M (de 000 a 111)

Se o valor do campo reg não for o esperado, a instrução será inválida ou interpretada incorretamente.