AMD64 -- nopw Montageanleitung?

AMD64 -- nopw Montageanleitung?


In dieser Compiler-Ausgabe versuche ich zu verstehen, wie die Maschinencode-Codierung von nopw Anleitung funktioniert:


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

Es gibt einige Diskussionen über "nopw" unter http://john.freml.in/amd64-nopl. Kann jemand die Bedeutung von 4004d2-4004e0 erklären? Wenn man sich die Opcode-Liste ansieht, scheint es, dass 66 .. Codes sind Multibyte-Erweiterungen. Ich glaube, ich könnte hier wahrscheinlich eine bessere Antwort darauf bekommen, als wenn ich nicht ein paar Stunden lang versucht hätte, die Opcode-Liste zu groken.



Diese asm-Ausgabe stammt aus dem folgenden (verrückten) Code in C, der sich zu einer einfachen Endlosschleife optimiert:


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

Beim Kompilieren mit gcc -O2 , der Compiler erkennt die unendliche Rekursion und verwandelt sie in eine Endlosschleife; es macht das sogar so gut, dass es tatsächlich die main() einschleift ohne den recurse() aufzurufen Funktion.



Anmerkung des Herausgebers:Das Auffüllen von Funktionen mit NOPs ist nicht spezifisch für Endlosschleifen. Hier ist eine Reihe von Funktionen mit unterschiedlich langen NOPs im Godbolt-Compiler-Explorer.


Antworten:


Die 0x66 Bytes sind ein "Operand-Size Override"-Präfix. Mehr als eines davon zu haben ist gleichbedeutend damit, eines zu haben.


Die 0x2e ist ein 'Null-Präfix' im 64-Bit-Modus (andernfalls ist es eine CS:-Segmentüberschreibung - weshalb es in der Assembly-Mnemonik angezeigt wird).


0x0f 0x1f ist ein 2-Byte-Opcode für ein NOP, das ein ModRM-Byte benötigt


0x84 ist das ModRM-Byte, das in diesem Fall für einen Adressierungsmodus codiert, der 5 weitere Bytes verwendet.


Einige CPUs sind langsam beim Decodieren von Anweisungen mit vielen Präfixen (z. B. mehr als drei), daher ist ein ModRM-Byte, das einen SIB + disp32 angibt, eine viel bessere Möglichkeit, zusätzliche 5 Bytes zu verbrauchen, als fünf weitere Präfix-Bytes.




Im Wesentlichen sind diese Bytes eine lange NOP-Anweisung, die sowieso niemals ausgeführt wird. Es soll sicherstellen, dass die nächste Funktion an einer 16-Byte-Grenze ausgerichtet ist, da der Compiler einen .p2align 4 ausgegeben hat Direktive, also füllte der Assembler mit einem NOP. gccs Standard für x86 ist

-falign-functions=16 . Für NOPs, die ausgeführt werden, hängt die optimale Wahl von Long-NOP von der Mikroarchitektur ab. Bei einer Mikroarchitektur, die an vielen Präfixen erstickt, wie Intel Silvermont oder AMD K8, hätten zwei NOPs mit jeweils 3 Präfixen möglicherweise schneller dekodiert.


Der Blog-Artikel, auf den die Frage verlinkt ist ( http://john.freml.in/amd64-nopl ), erklärt, warum der Compiler eine komplizierte einzelne NOP-Anweisung anstelle einer Reihe von Single-Byte-0x90-NOP-Anweisungen verwendet.


Einzelheiten zur Befehlscodierung finden Sie in den technischen Referenzdokumenten von AMD:



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


Hauptsächlich im "AMD64 Architecture Programmer's Manual Volume 3:General Purpose and System Instructions". Ich bin sicher, dass die technischen Referenzen von Intel für die x64-Architektur dieselben Informationen enthalten (und möglicherweise sogar verständlicher sind).