Architettura x86
Il processore Intel x86 usa un'architettura CISC (Instruction Set Computer) complessa, il che significa che esiste un numero modesto di registri speciali anziché grandi quantità di registri per utilizzo generico. Significa anche che le istruzioni speciali complesse predominieranno.
Il processore x86 traccia il suo patrimonio almeno per quanto riguarda il processore Intel 8080 a 8 bit. Molte peculiarità nel set di istruzioni x86 sono dovute alla compatibilità con le versioni precedenti con quel processore (e con la sua variante Zilog Z-80).
Microsoft Win32 usa il processore x86 in modalità flat a 32 bit. Questa documentazione si concentrerà solo sulla modalità flat.
Registri
L'architettura x86 è costituita dai registri interi senza privilegi seguenti.
eax |
Accumulatore |
ebx |
Registro di base |
ecx |
Registro contatori |
edx |
Registro dati: può essere usato per l'accesso alle porte di I/O e funzioni aritmetiche |
Esi |
Registro dell'indice di origine |
Edi |
Registro degli indici di destinazione |
ebp |
Registro del puntatore di base |
Spagnolo |
Puntatore dello stack |
Tutti i registri integer sono a 32 bit. Tuttavia, molti di essi hanno sottoregistratori a 16 bit o a 8 bit.
ascia |
Basso 16 bit di eax |
Bx |
Basso 16 bit di ebx |
cx |
Basso 16 bit di ecx |
Dx |
Basso 16 bit di edx |
si |
Basso 16 bit di esi |
di |
Basso 16 bit di edi |
Bp |
Basso 16 bit di ebp |
Sp |
Basso 16 bit di esp |
ale |
Basso 8 bit di eax |
ah |
Alto 8 bit di ax |
laureato in legge |
Basso 8 bit di ebx |
Bh |
Alto 8 bit di bx |
Cl |
Basso 8 bit di ecx |
Ch |
Alto 8 bit di cx |
dl |
Basso 8 bit di edx |
Dh |
Alto 8 bit di dx |
L'uso di una sottoregister influisce solo sulla sottoregister e nessuna delle parti esterne alla sottoregister. Ad esempio, l'archiviazione nel registro ax lascia invariati i 16 bit alti del registro eax .
Quando si usa ? (Evaluate Expression) comando, i registri devono essere preceduti da un segno "at" ( @ ). Ad esempio, è consigliabile usare ? @ax anziché ? ax. In questo modo il debugger riconosce ax come registro anziché come simbolo.
Tuttavia, il parametro (@) non è necessario nel comando r (Registri). Ad esempio, r ax=5 verrà sempre interpretato correttamente.
Due altri registri sono importanti per lo stato corrente del processore.
eip |
puntatore all'istruzione |
flags |
flags |
Il puntatore all'istruzione è l'indirizzo dell'istruzione in esecuzione.
Il registro dei flag è una raccolta di flag a bit singolo. Molte istruzioni modificano i flag per descrivere il risultato dell'istruzione. Questi flag possono quindi essere testati tramite istruzioni di salto condizionale. Per informazioni dettagliate, vedere Flag x86.
Convenzioni di chiamata
L'architettura x86 ha diverse convenzioni di chiamata. Fortunatamente, tutti seguono le stesse regole di conservazione dei registri e restituzione delle funzioni:
Le funzioni devono mantenere tutti i registri, ad eccezione di eax, ecx edx, che possono essere modificati in una chiamata di funzione ed esp, che devono essere aggiornati in base alla convenzione di chiamata.
Il registro eax riceve i valori restituiti dalla funzione se il risultato è di 32 bit o minore. Se il risultato è a 64 bit, il risultato viene archiviato nella coppia edx:eax .
Di seguito è riportato un elenco di convenzioni di chiamata usate nell'architettura x86:
Win32 (__stdcall)
I parametri della funzione vengono passati nello stack, inseriti da destra a sinistra e il chiamato pulisce lo stack.
Chiamata al metodo C++ nativa (nota anche come thiscall)
I parametri della funzione vengono passati nello stack, inseriti da destra a sinistra, il puntatore "this" viene passato nel registro ecx e il chiamato pulisce lo stack.
COM (__stdcall per le chiamate al metodo C++)
I parametri della funzione vengono passati nello stack, inseriti da destra a sinistra, quindi viene eseguito il push del puntatore "this" nello stack e quindi viene chiamata la funzione . Il chiamato pulisce lo stack.
__fastcall
I primi due argomenti DWORD o più piccoli vengono passati nei registri ecx edx. I parametri rimanenti vengono passati nello stack, inseriti da destra a sinistra. Il chiamato pulisce lo stack.
__cdecl
I parametri della funzione vengono passati nello stack, inseriti da destra a sinistra e il chiamante pulisce lo stack. La convenzione di chiamata __cdecl viene usata per tutte le funzioni con parametri a lunghezza variabile.
Visualizzazione del debugger di registri e flag
Di seguito è riportato un esempio di visualizzazione del registro del debugger:
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
Nel debug in modalità utente è possibile ignorare iopl e l'intera ultima riga della visualizzazione del debugger.
Flag x86
Nell'esempio precedente i codici a due lettere alla fine della seconda riga sono flag. Si tratta di registri a bit singolo e con un'ampia gamma di usi.
La tabella seguente elenca i flag x86:
Contrassegno codice | Nome contrassegno | Valore | Stato flag | Descrizione |
---|---|---|---|---|
di | Overflow Flag | 0 1 | nvov | Nessun overflow - Overflow |
df | Flag di direzione | 0 1 | updn | Direzione verso l'alto - Direzione verso il basso |
if | Interrupt Flag | 0 1 | diei | Interruzioni disabilitate - Interruzioni abilitate |
Sf | Contrassegno di firma | 0 1 | plng | Positivo (o zero) - Negativo |
zf | Contrassegno zero | 0 1 | nzzr | Diverso da zero - Zero |
af | Bandiera porta ausiliaria | 0 1 | naac | Nessun trasporto ausiliario - Trasporto ausiliario |
pf | Contrassegno parità | 0 1 | Pepo | Parità dispari - Parità pari |
cfr | Contrassegno di trasporto | 0 1 | nccy | Nessun trasporto - Trasporto |
tf | Trap Flag | Se tf è uguale a 1, il processore genererà un'eccezione STATUS_SINGLE_STEP dopo l'esecuzione di un'istruzione. Questo flag viene usato da un debugger per implementare la traccia in un unico passaggio. Non deve essere usato da altre applicazioni. | ||
iopl | Livello di privilegi di I/O | Livello di privilegio I/O Questo è un numero intero a due bit, con valori compresi tra zero e 3. Viene usato dal sistema operativo per controllare l'accesso all'hardware. Non deve essere usato dalle applicazioni. |
Quando i registri vengono visualizzati come risultato di un comando nella finestra Di comando del debugger, si tratta dello stato del flag visualizzato. Tuttavia, se si vuole modificare un flag usando il comando r (Registri), è necessario farvi riferimento dal codice del flag.
Nella finestra Registri di WinDbg il codice del flag viene usato per visualizzare o modificare i flag. Lo stato del flag non è supportato.
Ecco un esempio. Nella visualizzazione del registro precedente viene visualizzato lo stato del flag ng . Ciò significa che il flag del segno è attualmente impostato su 1. Per modificare questa operazione, usare il comando seguente:
r sf=0
In questo modo il flag del segno viene impostato su zero. Se si esegue un'altra visualizzazione del registro, il codice di stato ng non verrà visualizzato. Verrà invece visualizzato il codice di stato pl .
La bandiera del segno, la bandiera zero e la bandiera di trasporto sono i flag più usati.
Condizioni
Una condizione descrive lo stato di uno o più flag. Tutte le operazioni condizionali su x86 sono espresse in termini di condizioni.
L'assembler usa un'abbreviazione di una o due lettere per rappresentare una condizione. Una condizione può essere rappresentata da più abbreviazioni. Ad esempio, AE ("sopra o uguale") è la stessa condizione di NB ("not below"). Nella tabella seguente sono elencate alcune condizioni comuni e il relativo significato.
Nome condizione | Flag | significato |
---|---|---|
Z |
ZF=1 |
Risultato dell'ultima operazione pari a zero. |
NZ |
ZF=0 |
Risultato dell'ultima operazione non pari a zero. |
A |
CF=1 |
L'ultima operazione richiedeva un trasporto o un prestito. Per i numeri interi senza segno, indica l'overflow. |
NC |
CF=0 |
L'ultima operazione non richiedeva un trasporto o un prestito. Per i numeri interi senza segno, indica l'overflow. |
S |
SF=1 |
Il risultato dell'ultima operazione ha un set di bit elevato. |
NS |
SF=0 |
Il risultato dell'ultima operazione è chiaro a bit elevato. |
O |
OF=1 |
Se considerato come operazione con segno integer, l'ultima operazione ha causato un overflow o un underflow. |
NO |
OF=0 |
Se considerato come operazione con segno integer, l'ultima operazione non ha causato un overflow o un underflow. |
Le condizioni possono essere usate anche per confrontare due valori. L'istruzione cmp confronta i due operandi e quindi imposta i flag come se sottraesse un operando dall'altro. È possibile usare le condizioni seguenti per controllare il risultato di cmp value1, value2.
Nome condizione | Flag | Significato dopo un'operazione CMP. |
---|---|---|
E |
ZF=1 |
value1 == value2. |
NE |
ZF=0 |
value1 != value2. |
GE NL | SF=OF |
value1>= value2. I valori vengono considerati numeri interi con segno. |
LE NG | ZF=1 o SF!=OF |
value1<= value2. I valori vengono considerati numeri interi con segno. |
G NLE | ZF=0 e SF=OF |
value1>value2. I valori vengono considerati numeri interi con segno. |
L NGE | SF!=OF |
value1<value2. I valori vengono considerati numeri interi con segno. |
AE NB | CF=0 |
value1>= value2. I valori vengono considerati numeri interi senza segno. |
BE NA | CF=1 o ZF=1 |
value1<= value2. I valori vengono considerati numeri interi senza segno. |
Un'appliance virtuale di rete | CF=0 e ZF=0 |
value1>value2. I valori vengono considerati numeri interi senza segno. |
B NAE | CF=1 |
value1<value2. I valori vengono considerati numeri interi senza segno. |
Le condizioni vengono in genere usate per agire sul risultato di un'istruzione cmp o test . ad esempio:
cmp eax, 5
jz equal
confronta il registro eax con il numero 5 calcolando l'espressione (eax - 5) e impostando i flag in base al risultato. Se il risultato della sottrazione è zero, verrà impostato il flag zr e la condizione jz sarà true in modo che venga eseguito il salto.
Tipi di dati
byte: 8 bit
parola: 16 bit
dword: 32 bit
qword: 64 bit (include valori double a virgola mobile)
tword: 80 bit (include doppi estesi a virgola mobile)
oword: 128 bit
Notazione
La tabella seguente indica la notazione usata per descrivere le istruzioni del linguaggio assembly.
Notazione | Significato |
---|---|
r, r1, r2... |
Registri |
m |
Indirizzo di memoria (vedere la sezione Modalità di indirizzamento riuscito per altre informazioni). |
#n |
Costante immediata |
r/m |
Registrare o memoria |
r/#n |
Registra o costante immediata |
r/m/#n |
Registra, memoria o costante immediata |
Cc |
Codice di condizione elencato nella sezione Condizioni precedente. |
T |
"B", "W" o "D" (byte, parola o dword) |
accT |
Dimensione T): al se T = "B", ax se T = "W" o eax se T = "D" |
Modalità di indirizzamento
Esistono diverse modalità di indirizzamento, ma tutte prendono il formato T ptr [expr], dove T è un tipo di dati (vedere la sezione Tipi di dati precedente) ed expr è un'espressione che coinvolge costanti e registri.
La notazione per la maggior parte delle modalità può essere dedotta senza molta difficoltà. Ad esempio, BYTE PTR [esi+edx*8+3] significa "prendere il valore del registro esi , aggiungerlo otto volte il valore del registro edx , aggiungere tre, quindi accedere al byte all'indirizzo risultante".
Pipelining
Il Pentium è a doppio problema, il che significa che può eseguire fino a due azioni in un tick di clock. Tuttavia, le regole su quando è in grado di eseguire due azioni contemporaneamente (note come associazione) sono molto complicate.
Poiché x86 è un processore CISC, non è necessario preoccuparsi degli slot di ritardo del salto.
Accesso alla memoria sincronizzato
Le istruzioni di caricamento, modifica e archiviazione possono ricevere un prefisso di blocco , che modifica l'istruzione nel modo seguente:
Prima di eseguire l'istruzione, la CPU scarica tutte le operazioni di memoria in sospeso per garantire la coerenza. Tutti i prelettura dei dati vengono abbandonati.
Durante l'emissione dell'istruzione, la CPU avrà accesso esclusivo al bus. In questo modo si garantisce l'atomicità dell'operazione di caricamento/modifica/archiviazione.
L'istruzione xchg rispetta automaticamente le regole precedenti ogni volta che scambia un valore con memoria.
Per impostazione predefinita, tutte le altre istruzioni non vengono bloccate.
Jump Prediction
I salti incondizionato vengono stimati per essere presi.
I salti condizionali vengono stimati per essere eseguiti o meno, a seconda che siano stati eseguiti l'ultima volta che sono stati eseguiti. La cache per la registrazione della cronologia dei salti è limitata.
Se la CPU non ha un record se il passaggio condizionale è stato eseguito o meno l'ultima volta che è stato eseguito, prevede salti condizionali indietro come eseguiti e salti condizionali in avanti come non eseguiti.
Allineamento
Il processore x86 correggerà automaticamente l'accesso alla memoria non idonea, con una riduzione delle prestazioni. Non viene generata alcuna eccezione.
Un accesso alla memoria viene considerato allineato se l'indirizzo è un multiplo intero delle dimensioni dell'oggetto. Ad esempio, tutti gli accessi BYTE sono allineati (tutto è un multiplo intero di 1), gli accessi di WORD agli indirizzi pari sono allineati e gli indirizzi DWORD devono essere un multiplo di 4 per essere allineati.
Il prefisso di blocco non deve essere usato per gli accessi alla memoria non idonei.