AMD64 -- istruzioni per l'assemblaggio ora?

AMD64 -- istruzioni per l'assemblaggio ora?


In questo output del compilatore, sto cercando di capire come codificare il codice macchina di nopw l'istruzione funziona:


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

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



Quell'output di asm proviene dal seguente (folle) codice in C, che ottimizza fino a un semplice ciclo infinito:


long i = 0;
main() {
recurse();
}
recurse() {
i++;
recurse();
}

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



nota del redattore:le funzioni di riempimento con NOP non sono specifiche per i loop infiniti. Ecco un insieme di funzioni con una gamma di lunghezze di NOP, sull'esploratore del compilatore Godbolt.


Risposte:


Il 0x66 i byte sono un prefisso "Operand-Size Override". Avere più di uno di questi equivale ad averne uno.


Il 0x2e è un "prefisso nullo" in modalità a 64 bit (è un CS:segment override in caso contrario, motivo per cui viene visualizzato nel mnemonico dell'assembly).


0x0f 0x1f è un codice operativo a 2 byte per un NOP che accetta un byte ModRM


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


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




In sostanza, quei byte sono una lunga istruzione NOP che non verrà mai eseguita comunque. È lì per garantire che la funzione successiva sia allineata su un limite di 16 byte, perché il compilatore ha emesso un .p2align 4 direttiva, quindi l'assembler ha riempito con un NOP. L'impostazione predefinita di gcc per x86 è

-falign-functions=16 . Per i NOP che verranno eseguiti, la scelta ottimale del 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ù velocemente.


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


Puoi trovare i dettagli sulla codifica delle istruzioni nei documenti di riferimento tecnico di AMD:



  • http://developer.amd.com/documentation/guides/pages/default.aspx#manuals


Principalmente nel "Manuale del programmatore di architettura AMD64 Volume 3:Istruzioni generali e di sistema". Sono sicuro che i riferimenti tecnici di Intel per l'architettura x64 avranno le stesse informazioni (e potrebbero anche essere più comprensibili).