2011-01-25 9 views
17

In questo output del compilatore, sto cercando di capire come la macchina-codice codifica dell'istruzione nopw funziona:AMD64 - istruzioni di montaggio nopw?

00000000004004d0 <main>: 
    4004d0:  eb fe     jmp 4004d0 <main> 
    4004d2:  66 66 66 66 66 2e 0f nopw %cs:0x0(%rax,%rax,1) 
    4004d9:  1f 84 00 00 00 00 00 

V'è una certa discussione su "nopw" presso http://john.freml.in/amd64-nopl. Qualcuno può spiegare il significato di 4004d2-4004e0? Dall'esame dell'elenco dei codici operativi, sembra che i codici 66 .. siano espansioni multibyte. Sento che probabilmente potrei ottenere una risposta migliore a questo qui rispetto a quanto farei a meno che non provassi a estrarre l'elenco dei codici di accesso per alcune ore.


Tale uscita ASM dalla seguente codice (folle) in C, che ottimizza giù ad un semplice ciclo infinito:

long i = 0; 

main() { 
    recurse(); 
} 

recurse() { 
    i++; 
    recurse(); 
} 

Quando compilato con gcc -O2, il compilatore riconosce la ricorsione e giri infinita in un ciclo infinito; lo fa così bene, infatti, che in realtà si aggira nel main() senza chiamare la funzione recurse().


Nota editoriale: le funzioni di riempimento con NOP non sono specifiche per i loop infiniti. Ecco una serie di funzioni con una serie di lunghezze di NOP, on the Godbolt compiler explorer.

+0

Stiamo solo guardando casualmente il junk padding? –

+1

Forse! Non lo so davvero! Questa è la bellezza di tutto! Wheee. In realtà, però, ottengo dal collegamento che il processore STAI caricando un blocco come una sola istruzione per l'ottimizzazione della velocità, sebbene grazie al 'jmp', non lo sia. Ho appena capito il significato. So cosa sia 0x90, ma non so cosa succede con '66 .. ..', o perché è lungo 72 bit. –

+1

Qui non è il motivo, ma potresti trovare [My, che strani NOP hai! - The Old New Thing] (http://blogs.msdn.com/b/oldnewthing/archive/2011/01/12/10114521.aspx) una lettura interessante. – ephemient

risposta

20

I byte 0x66 sono un prefisso "Operando di dimensioni standard". Avere più di uno di questi è equivalente ad averne uno.

Il 0x2e è un 'prefisso null' in modalità a 64 bit (è un CS: il segmento esegue l'override in caso contrario - ecco perché viene visualizzato nell'assembly mnemonic).

0x0f 0x1f è un codice operativo 2 byte di NOP che prende un byte ModRM

0x84 è ModRM byte che in questo caso codifica per una modalità di indirizzamento che utilizza più 5 byte.

Alcune CPU sono lente nel decodificare le istruzioni con molti prefissi (ad esempio più di tre), quindi un byte ModRM che specifica un SIB + disp32 è un modo molto migliore di utilizzare 5 byte in più rispetto a cinque prefissi in più.

AMD K8 decoders in Agner Fog's microarch pdf:

Ciascuno dei decodificatori di istruzione in grado di gestire tre prefissi per clock ciclo. Ciò significa che tre istruzioni con tre prefissi ciascuna possono essere decodificate nello stesso ciclo di clock. Un'istruzione con 4 - 6 prefissi richiede un ciclo di clock aggiuntivo per la decodifica.


In sostanza, questi byte sono una lunga istruzione NOP che non potrà mai ottenere eseguito comunque. È lì per assicurarsi che la prossima funzione sia allineata su un limite di 16 byte, perché il compilatore ha emesso una direttiva .p2align 4, quindi l'assemblatore è riempito con un NOP.gcc's default for x86 is
-falign-functions=16
. Per i NOP che verranno eseguiti, la scelta ottimale di NOP lungo dipende dalla microarchitettura. Per una microarchitettura che soffoca su molti prefissi, come Intel Silvermont o AMD K8, due NOP con 3 prefissi ciascuno potrebbero essere decodificati più rapidamente.

L'articolo del blog la domanda collegata a (http://john.freml.in/amd64-nopl) spiega perché il compilatore utilizza una singola istruzione NOP singola invece di un gruppo di istruzioni NOP 0x90 a byte singolo.

È possibile trovare i dettagli sulla codifica di istruzioni nei documenti rif tecnologia di AMD:

Principalmente nella "AMD64 Architecture Programmer Manuale Volume 3: General Purpose e istruzioni di sistema". Sono sicuro che i riferimenti tecnici di Intel per l'architettura x64 avranno le stesse informazioni (e potrebbero anche essere più comprensibili).

+0

Re il byte ModRM che significa ... http://ref.x86asm.net/coder64.html#x0F1F elenca il byte ModRM come utilizzato per gli NOP stampabili, con riferimenti a questo: 1. Vedere brevetto US 5,701,442 2. sandpile. org - IA-32 architecture - gruppi opcode. Non ho controllato quelli, ma nel caso ti interessi. – Bahbar

+0

È un NOP, quindi il byte mod/rm non * fa * nulla. Fa parte delle istruzioni come un modo per consentire una vasta gamma di lunghezze di istruzioni in un modo che i decoder possano decodificare rapidamente. La decodifica di molti prefissi è lenta su alcune CPU, quindi ripetere il prefisso "66" per operando di dimensioni 5 volte è molto peggio di un mod/rm che codifica per una modalità di indirizzamento che utilizza un disp32 SIB +. –

1

Direi che si tratta solo dell'istruzione di ritardo di ramo.

-3

Credo che il nopw sia junk - non viene mai letto nel tuo programma e quindi non è necessario incrementarlo.

+0

'i' mi ha dato un modo conveniente per controllare le dimensioni dello stack quando non è riuscito. Gdb, per quanto ne so io, non ha una chiave "print size of stack". È inoltre interessante osservare il compilatore rimuovere l'incremento di esso una volta che il livello di ottimizzazione è aumentato. Il programma è intenzionalmente "pazzo". –

+0

Il mio punto è che il compilatore lo ha ottimizzato, poiché non hai mai letto i. –

+0

La domanda non riguarda questo, però. Il punto della domanda è perché il 'nop' (' nopw' qui) viene fuori in questo modo. Lo standard 'nop' è 0x90 e viene semplicemente ripetuto. Mettere 'i' lì dentro come una variabile inutilizzata era intenzionale ed esternamente utile anche se non viene toccata nel codice. –

2

I pad assemblatore (non il compilatore) codificano fino al limite di allineamento successivo con l'istruzione NOP più lunga che può trovare adatta. Questo è ciò che stai vedendo.

Problemi correlati