resume sobre codificação de operações
This commit is contained in:
parent
a853524d46
commit
3f077d6972
1 changed files with 277 additions and 0 deletions
277
docs/opcodes.org
Normal file
277
docs/opcodes.org
Normal file
|
@ -0,0 +1,277 @@
|
||||||
|
* 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 7–6) representa o fator de multiplicação:
|
||||||
|
|
||||||
|
| Bits | Fator |
|
||||||
|
|------+-------|
|
||||||
|
| ~00~ | ~1~ |
|
||||||
|
| ~01~ | ~2~ |
|
||||||
|
| ~10~ | ~4~ |
|
||||||
|
| ~11~ | ~8~ |
|
||||||
|
|
||||||
|
O campo *index* (bits 5–3) 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 2–0) 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 5–3), 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.
|
Loading…
Add table
Reference in a new issue