Wie viele bytes hat der push-Befehl push auf den stack, wenn ich nicht geben Sie den operand-Größe?
Kann ich push 4 bytes auf den stack zu tun:
push DWORD 123
Aber ich habe heraus gefunden, dass ich verwenden können push
ohne Angabe der Operanden-Größe:
push 123
In diesem Fall, wie viele bytes hat die push
Anweisung push auf den stack? Auch die Anzahl von bytes geschoben, hängt von den Operanden-Größe (also in meinem Beispiel wird es push 1 byte)?
- Die native register Größe, um die Stapel ausgerichtet werden. In 32-bit-Modus befindet, drücken 4 bytes. In 64-bit-Modus befindet, drücken Sie 8 Byte.
- BTW, könnte man getestet haben dies mit einem debugger. Es ist einfach nur Schritt die Anleitung und sehen, wie
esp
/rsp
änderungen. Sie könnte auch die Demontage Ausgang und merken, dass Sie beide versammeln, um den gleichen Maschinencode.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Es hängt nicht von dem Wert der Zahl. Die technische x86 Begriff für wie viele bytes
push
schiebt ist "operand-size", aber das ist eine separate Sache aus, ob die Anzahl passt in eine imm8 oder nicht.Siehe auch Nicht jeder PUSH-Befehl push ein Vielfaches von 8 bytes auf 64?
Nein, die Größe des unmittelbaren ist nicht der operand-Größe. Es drückt immer 4 bytes in 32-bit-oder 64 im 64-bit-code, es sei denn, Sie tun etwas seltsam.
Empfehlung: immer nur schreiben
push 123
oderpush 0x12345
Verwendung der Standard -push
Größe für den Modus, den Sie in und und lassen Sie die assembler-wählen Sie die Kodierung. Das ist fast immer, was Sie wollen. Wenn es das ist alles, was Sie wissen wollten, können Sie jetzt zu Lesen aufhören.Zuerst von allen, ist es nützlich zu wissen welche Größen von
push
sind sogar in x86 Maschinencode:EIN REX.W=0 Präfix wird nicht lassen Sie Kodieren eine 32-bit-drücken.1
Gibt es keine anderen Optionen. Der stack-pointer wird immer um eins verringert, die durch die Operanden-Größe der push2. (So ist es möglich, "misalign" den Stapel schieben 16 bit).
pop
hat die gleichen Möglichkeiten der Größe: 16, 32, oder 64, außer, dass keine 32-bit-pop im 64-bit Modus.Dies gilt unabhängig davon, ob Sie schieben ein register oder ein unmittelbarer, und unabhängig davon, ob die unmittelbaren passt in eine sign-extended
imm8
oder es muss einimm32
(oderimm16
für 16-bit-drückt). (Ein 64-bit -push imm32
- Zeichen-erstreckt sich auf 64-bit. Es gibt keinepush imm64
nurmov reg, imm64
)In NASM-source-code,
push 123
versammelt, um den operand-Größe ist, dass Spiele den Modus Sie arbeiten. In Ihrem Fall denke ich, Sie schreiben von 32-bit-code, sopush 123
ist ein 32-bit schieben, obwohl es kann (und tut), dann verwenden Sie diepush imm8
Codierung.Ihrem assembler immer weiß, welche Art von code es ist die Montage, da Sie, um zu wissen, Wann zu verwenden oder nicht verwenden operand-size-Präfixe, wenn Sie Kraft der operand-Größe.
MASM ist der gleiche, das einzige, was vielleicht anders ist die syntax zum erzwingen einer anderen Operanden-Größe.
Alles, was Sie schreiben in assembler versammeln sich zu einer der gültigen Maschinencode-Optionen (weil die Leute, schrieb der assembler wissen, was ist und ist nicht encodeable), also Nein, Sie können nicht schieben Sie ein einzelnes byte mit einer
push
Unterricht. Wenn man wollte, könnte man emulieren, es mitdec esp
/mov byte [esp], 123
NASM Beispiele:
Ausgabe von
nasm -l /dev/stdout
dump einer Liste an das terminal, zusammen mit der original-Quellcode-Zeile.Leicht bearbeitet werden, um separate opcode und Präfix-bytes der Operanden. (Im Gegensatz zu
objdump -drwC -Mintel
, NASM die Demontage format nicht lassen Sie Leerzeichen zwischen den bytes im Maschinencode-hexdump).dword
ist in der Regel eine operand-size-Sache, währendstrict dword
ist, wie Sie verlangen, dass der assembler nicht optimieren, es zu einem kleineren Codierung.Alle vorhergehenden Anweisungen sind 32-bit schiebt (oder 64-bit in 64-bit-Modus, mit den gleichen Maschinencode). Alle folgenden Anweisungen sind 16-bit-drückt, unabhängig davon, in welchem Modus Sie setzen Sie Sie in. (Wenn montiert in 16-bit-Modus, werden Sie nicht haben eine
0x66
operand-size prefix)NASM scheint zur Behandlung der
byte
unddword
Außerkraftsetzungen anwenden, um die Größe des unmittelbaren, aberword
gilt für die operand-size der Anweisung. Eigentlich mito32-push-12
im 64-bit Modus nicht eine Warnung erhalten, entweder.push eax
tut, aber: "Fehler: Befehl nicht unterstützt 64-bit-Modus".Beachten Sie, dass
push imm8
codiert als6A ib
in allen Modi. Ohne operand-size prefix, wird der operand-Größe ist der Modus Größe. (z.B.6A FF
dekodiert im long-Modus als 64-bit-operand-Größe push operand-1
, Dekrementieren RSP von 8 und ein 8-byte speichern.)Das address-size prefix wirkt sich nur auf die explizite Adressierung verwendet für push mit einem Speicher-Quelle, wie z.B. in 64-bit-Modus:
push qword [rsi]
(keine Präfixe) vs.push qword [esi]
(address-size prefix für 32-bit-addressing mode).push dword [rsi]
ist nicht encodeable, da kann auch nichts machen, das operand-size-32-bit in 64-bit-code1.push qword [esi]
nicht abgeschnittenrsp
zu 32-bit. Anscheinend "Stack-Adresse Breite" ist eine andere Sache, wahrscheinlich in ein segment-Deskriptor. (Es ist immer 64 im 64-bit-code auf eine "normale" OS, ich denke, auch für Linux x32-ABI: ILP32 im long-Modus).Würde, wenn Sie jemals wollen, schieben 16 bit? Wenn Sie schreiben in asm aus performance-Gründen, dann wohl nie. In mein code-golf adler32, ein eng push -> große pop dauerte weniger bytes code als shift/ODER kombinieren von zwei 16b Ganzzahlen in einen 32b Wert.
Oder vielleicht ein exploit für das 64-bit-code, möchten Sie vielleicht schieben einige Daten auf den stack, ohne Lücken. Können Sie nicht einfach
push imm32
, weil das Zeichen oder null ist, erstreckt sich auf 64-bit. Sie könnte es tun, in 16-bit-Blöcken mit mehreren 16-bit-push-Anweisungen. Aber wahrscheinlich noch effizienter zumov rax, imm64
/push rax
(10B+1B = 11B für eine 8B imm payload). Oderpush 0xDEADBEEF
/mov dword [rsp+4], 0xDEADC0DE
(5B + 8B = 13B und braucht nicht zu registrieren). vier 16-bit schiebt dauern würde, 16B.Fußnoten:
In der Tat REX.W=0 ist, werden ignoriert und nicht ändern Sie den Operanden-Größe Weg von den Standard-64-bit. NASM, YASM und GAS versammeln sich alle
push r12
zu41 54
, nicht49 54
. GNUobjdjump
denkt49 54
ungewöhnlich ist, und dekodiert Sie als49 54 rex.WB push r12
. (Beide führen die gleichen). Microsoft verpflichtet sich auch, mit einem- 40h
REX als Polsterung aufpush rbx
in einigen Windows-DLLs.Intel sagt nur, dass die 32-bit-drückt "nicht encodeable" (N. E. in der Tabelle) im lang-Modus. Ich verstehe nicht, warum W=1 ist nicht die standard-Kodierung für
push
/pop
wenn ein REX-Präfix benötigt wird, aber offenbar die Wahl ist willkürlich.Fun-fact: nur die stack-Anweisungen und ein paar andere Standard-64-bit-operand-Größe in 64-bit-Modus. In Maschinen-code,
add rax, rdx
braucht ein REX-Präfix (mit dem W-bit gesetzt). Sonst würde es zu decodieren, wieadd eax, edx
. Aber Sie können nicht, verringert den Operanden-Größe mit einerREX.W=0
wenn es standardmäßig auf 64-bit, nur erhöhen, wenn es standardmäßig 32.http://wiki.osdev.org/X86-64_Instruction_Encoding#REX_prefix Listen Sie die Anweisungen, die standardmäßig auf 64-bit in 64-bit-Modus. Beachten Sie, dass
jrcxz
nicht streng gehören in diese Liste, weil die register überprüft (cx/ecx/rcx) ist bestimmt durch Adresse-Größe, nicht operand-Größe ist, also, es kann überschrieben werden, um 32-bit (aber nicht 16-bit) 64-bit-Modus.loop
ist die gleiche.Es ist merkwürdig, dass Intel instruction reference manual-Eintrag für
push
(HTML-Auszug: http://felixcloutier.com/x86/PUSH.html)zeigt, was passieren würde, für ein 32-bit-operand-Größe push-in 64-bit-Modus (der einzige Fall, wo stack-Adresse Breite 64, so nutzt
rsp
). Vielleicht ist es erreichbar, irgendwie mit einigen nicht-standard-Einstellungen in das code-segment-descriptor, so können Sie es nicht in der normalen 64-bit-code läuft auch unter einem normalen OS. Oder, wahrscheinlicher, es ist ein versehen, und das ist, was passieren würde, wenn es war encodeable, aber es ist nicht.Außer segment-Register sind 16-bit -, sondern eine normale
push fs
noch dekrementiert den stack-pointer durch stack-Breite (operand-Größe). Intel-Dokumente, aktuelle Intel CPUs nur ein 16b-store in diesem Fall wird, während der rest der 32 oder 64b unverändert.x86 offiziell nicht haben stack Breite das ist durchgesetzt in der hardware. Es ist ein software /calling convention Begriff, z.B.
char
undshort
Argumente auf den stack gelegt, in jedem Aufrufkonventionen sind gepolstert, nach 4B oder 8B, so daß der Stapel ausgerichtet bleibt. (Moderne 32-und 64-bit-Aufrufkonventionen wie die x86-32-System V psABI verwendet, die von Linux halten die stack-16B ausgerichtet ist, bevor Sie die Funktion Anrufe, obwohl Sie eine arg "slot" auf dem stack ist nur noch 4B). Trotzdem, "stack " Breite" ist nur eine Programmier-Konvention auf jeder Architektur.Die nächste Sache, die in der x86-ISA zu einem "stack " Breite" ist die Vorgabe-Operanden-Größe
push
/pop
. Aber Sie manipulieren können, der stack-pointer immer Sie wollen, z.B.sub esp,1
. Sie können, aber nicht aus Gründen der Leistung 😛mov
unmittelbarer in der sich zuerst registrieren und dannpush
. 2 Anweisungen anstelle von 1...push
verrückt ist, außer in den 16-bit-Modus. Es misaligns dem Stapel, so würden Sie ihn nur für verrückt hacks wie das Zusammenführen von zwei 16-bit-Werte in einen 32-bit-Wert mit minimale code-Größe, auf Kosten der Leistung.imm64
istmov
. Andere, wiepush
,add
,imul
alle nehmen nur eine 32-bit (oder 8 bit) sofort, da die meisten Konstanten sind klein. Sie wollen nicht eine 8-byte immediate nur so können Sie hinzufügen oder drücken 1024. Ich bin mir nicht sicher ob es eine bestehende Q&Eine darüber, warum x86-64 ist so konstruiert, meist sign-extended-imm32
(und sign-extended-disp32
im Adressierungsarten); ich hatte einen kurzen Blick, aber nicht finden, wenn auf der Suche nach mehr Duplikate für stackoverflow.com/questions/48705762/pushing-imm32-ends-up-inDen "stack " Breite" in einem computer, die die kleinste Menge an Daten, die auf dem Stapel abgelegt, definiert ist, werden die register Größe des Prozessors. Dies bedeutet, dass, wenn Sie es mit einem Prozessor mit 16 bit-Register, der stack-Breite werden 2 bytes. Wenn der Prozessor verfügt über 32-bit Register, der stack-Breite ist 4 bytes. Wenn der Prozessor 64-bit-Register, der stack Breite ist 8 bytes.
Nicht verwechselt werden, wenn mit modernen x86/x86_64-Systeme; wenn das system läuft im 32 bit-Modus, Stapel-Breite und registrieren Größe ist 32 bits oder 4 bytes. Wenn der Wechsel zu 64-bit-Modus, dann und nur dann wird die register-und stack-Größe ändern.
push r16
ändert RSP nur 2, anstatt Polsterung an der "stack-Breite".push
ohne ein operand-size prefix noch drückt 16 bits.PUSH
. Ich glaube nicht, dass ein assembler ist zu tun, wenn Sie ausdrücklich verlangen es mit einerWORD
oderWORD PTR
Richtlinie. Eine genauere Formulierung im ersten Absatz wäre die "operand-size-Attribut des aktuellen code-Segments", aber das ist zugegebenermaßen umständlich. Es ist nur das register der Breite, das entspricht der aktuellen Betriebssysteme - Modus, so offensichtlich, wenn Sie im 16-bit Modus, es werden 2 bytes, auch wenn 32-bit-Register zur Verfügung.push
unterscheidet sich von nicht-stack-Anweisungen: seine Vorgabe-Operanden-Größe in 64-bit ist, 64-bit, auch ohne REX.W=1. Dapush
operand-size-Zeug ist so seltsam, beschloss ich, dies wäre ein guter Ort, um zu versuchen, eine kanonische Antwort.PUSH
ist nicht die einzige Anweisung, die eine default operand size von 64-bit im long-Modus auch ohne dieREX
Präfix. In der Nähe vonCALL
,RET
,JMP
, undJcc
arbeiten auch so, wie einige der eher obskuren Anweisungen wieENTER
,LEAVE
,LOOPcc
. Oh, und natürlichPOP
. Ich denke, Sie würde sagen, alle von denen sind "stack" - Anweisungen, aber ich glaube nicht, das ist fast so verwirrend, komisch oder ein Problem, du machst es!LOOP
: das Handbuch dokumentiert, dass es ignoriertREX.W
(wie ich fand, dasspush
tut), und seine Adresse-Größe steuert, ob es nutzt cx/ecx/rcx. Seltsame Dinge mitpush
: NASM Größe Planern nicht tun, was Sie erwarten würde:push dword 123
nicht warnen, im 64-bit Modus. Intel 's manual scheint zu zeigen (in Abschnitt "Bedienung"), dass es möglich ist, fürpush
zu verringern bzw um 4. Aber es ist nicht nur 64 oder 16, das scheint seltsam.push
dem normalen Weg ist nicht schwierig, das ist, warum ich fügte hinzu, im ersten Abschnitt ziemlich bald nach der Veröffentlichung meiner Antwort. Aber da viele Dinge, über diepush
verwirrt haben mich zu verschiedenen Zeiten, ich dachte, dass andere Leute sich vielleicht gewundert haben, über Sie, auch. Als Davids Antwort zeigt, es ist einfach zu mis-die Tatsachen, die bei dem Versuch eine einfache Antwort überpush
, vor allem, wenn Sie wollen, der für alle Modi (ich nehme an, ignorieren von 32-bit-regs in 16-bit-Modus wurde absichtlich).PUSHF
/PUSHD
- und pop-äquivalente, die nicht Arbeit im 64-bit Modus. Also das ist wahrscheinlich, warum die Formulierung wurde abgesetzt in den aktuellen Versionen, da es nicht genau, für long-Modus. Aber du hast Recht, es ist sehr seltsam, dass das Handbuch fürPUSH
ist ein Punkt, der beginnt mit "Operand size", wo es heißt, dass ein REX.W Anleitung, kann das Präfix verwendet werden, das überschreiben der Größe. Es ist also nicht nur die Beschreibung pseudo-code, ist irreführend.