pbn/docs/opcodes.org

277 lines
9.9 KiB
Org Mode
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

* 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:
#+begin_example
[prefixos][operação][operandos]
#+end_example
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:
#+begin_example
┌──────┬───┬───┬───┬───┐
Bits │ 7-4 │ 3 │ 2 │ 1 │ 0 │
├──────┼───┼───┼───┼───┤
Info │ 0100 │ W │ R │ X │ B │
└──────┴───┴───┴───┴───┘
│ │ │ └─── [B]ase extension
│ │ └─────── Inde[x] extension
│ └─────────── [R]egister extension
└─────────────── [W]ide
#+end_example
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
#+begin_example
┌─────┬───────┬───────┐
Bits │ 7 6 │ 5 4 3 │ 2 1 0 │
├─────┼───────┼───────┤
Info │ mod │ reg │ r/m │
└─────┴───────┴───────┘
#+end_example
*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:
#+begin_example
[base + index × scale + displacement]
#+end_example
Ele aparece obrigatoriamente após o byte ModR/M quando o campo ~mod != ~11~
(o operando é memória) e o campo ~r/m = 100~.
#+begin_example
┌───────┬───────┬───────┐
Bits │ 7 6 │ 5 4 3 │ 2 1 0 │
├───────┼───────┼───────┤
Info │ scale │ index │ base │
└───────┴───────┴───────┘
#+end_example
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:
#+begin_example
[opcode] /n [outros bytes...]
#+end_example
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.