Wenn man über das Lese Assembler ich über die Menschen kommt oft zu schreiben , dass sie schieben ein bestimmtes Register des Prozessors und Pop es später wieder wiederherstellen vorheriger Zustand.
- Wie kann man ein Register schieben? Wo wird es weitergeschoben? Warum wird das benötigt?
- Kommt es auf einen einzelnen Prozessorbefehl an oder ist es komplexer?
assembly
x86
stack
terminology
Ars emble
quelle
quelle
b
,w
,l
oderq
die Größe des Speichers manipuliert zu bezeichnen. Beispiel:pushl %eax
undpopl %eax
%eax
immer 32 Bit groß sind.Antworten:
Wenn Sie einen Wert verschieben (der nicht unbedingt in einem Register gespeichert ist), müssen Sie ihn in den Stapel schreiben.
Poppen bedeutet, alles, was sich oben auf dem Stapel befindet, in einem Register wiederherzustellen . Das sind grundlegende Anweisungen:
quelle
r/m
nicht nur "Registrieren", sondern auch "Registrieren"push dword [esi]
. Oder sogarpop dword [esp]
, um denselben Wert zu laden und dann wieder an dieselbe Adresse zu speichern. ( github.com/HJLebbink/asm-dude/wiki/POP ). Ich erwähne dies nur, weil Sie "nicht unbedingt ein Register" sagen.pop
in einen Bereich der Erinnerung:pop [0xdeadbeef]
So schieben Sie ein Register. Ich nehme an, wir sprechen über x86.
Es wird auf Stapel geschoben. Der Wert von
ESP
Registers wird auf die Größe des Push-Werts dekrementiert, wenn der Stapel in x86-Systemen nach unten wächst.Es wird benötigt, um die Werte zu erhalten. Die allgemeine Verwendung ist
A
push
ist eine einzelne Anweisung in x86, die intern zwei Dinge ausführt.ESP
Registers.ESP
Register auf die Größe des Push-Werts.quelle
Wo wird es weitergeschoben?
esp - 4
. Etwas präziser:esp
wird von 4 abgezogenesp
pop
kehrt dies um.Das System V ABI weist Linux an,
rsp
auf einen sinnvollen Stapelspeicherort hinzuweisen, wenn das Programm gestartet wird: Wie lautet der Standardregisterstatus beim Programmstart (asm, linux)? Welches ist, was Sie normalerweise verwenden sollten.Wie kann man ein Register schieben?
Beispiel für ein minimales GNU-GAS:
Das obige auf GitHub mit ausführbaren Assertions .
Warum wird das benötigt?
Es ist wahr, dass diese Anweisungen leicht über und implementiert werden
mov
können .add
sub
Der Grund dafür ist, dass diese Kombinationen von Anweisungen so häufig sind, dass Intel beschlossen hat, sie für uns bereitzustellen.
Der Grund, warum diese Kombinationen so häufig sind, besteht darin, dass sie das Speichern und Wiederherstellen der Werte von Registern im Speicher erleichtern, damit sie nicht überschrieben werden.
Versuchen Sie, C-Code von Hand zu kompilieren, um das Problem zu verstehen.
Eine große Schwierigkeit besteht darin, zu entscheiden, wo jede Variable gespeichert wird.
Im Idealfall passen alle Variablen in Register, auf die am schnellsten zugegriffen werden kann (derzeit etwa 100-mal schneller als RAM).
Aber natürlich können wir leicht mehr Variablen als Register haben, insbesondere für die Argumente verschachtelter Funktionen. Die einzige Lösung besteht darin, in den Speicher zu schreiben.
Wir könnten in jede Speicheradresse schreiben, aber da die lokalen Variablen und Argumente von Funktionsaufrufen und -rückgaben in ein schönes Stapelmuster passen, wird eine Speicherfragmentierung verhindert , ist dies der beste Weg, damit umzugehen. Vergleichen Sie das mit dem Wahnsinn, einen Heap-Allokator zu schreiben.
Dann lassen wir Compiler die Registerzuordnung für uns optimieren, da dies NP vollständig ist und einer der schwierigsten Teile beim Schreiben eines Compilers ist. Dieses Problem wird als Registerzuordnung bezeichnet und ist isomorph zur Diagrammfärbung .
Wenn der Allokator des Compilers gezwungen ist, Dinge im Speicher zu speichern, anstatt nur Register, wird dies als Überlauf bezeichnet .
Kommt es auf einen einzelnen Prozessorbefehl an oder ist es komplexer?
Wir wissen nur, dass Intel a
push
und a dokumentiertpop
Anweisung , also sind sie eine Anweisung in diesem Sinne.Intern könnte es auf mehrere Mikrocodes erweitert werden, einen zum Ändern
esp
und einen zum Ausführen der Speicher-E / A, und mehrere Zyklen dauern.Es ist aber auch möglich, dass eine einzelne
push
schneller ist als eine äquivalente Kombination anderer Anweisungen, da sie spezifischer ist.Dies ist meist un (der) dokumentiert:
push
undpop
eine einzige Mikrooperation durchführen.quelle
push
/pop
in Uops dekodieren. Dank Leistungsindikatoren sind experimentelle Tests möglich, und Agner Fog hat dies getan und Anweisungstabellen veröffentlicht . Pentium-M- und spätere CPUs verfügen dank der Stack-Engine über Single-Uoppush
/pop
(siehe Agners Microarch- PDF). Dies schließt aktuelle AMD-CPUs dank der Intel / AMD-Patentfreigabevereinbarung ein.mov
Ladevorgängen). Für verschüttete nicht konstante Variablen sind die Roundtrips für die Weiterleitung des Geschäfts mit einer hohen zusätzlichen Latenz verbunden (zusätzliche ~ 5c im Vergleich zur direkten Weiterleitung, und die Anweisungen für das Geschäft sind nicht billig).ocperf.py
Wrapper-Skript verwenden, um einfache symbolische Namen für die Zähler zu erhalten.Pushing- und Popping-Register sind hinter den Kulissen gleichbedeutend mit:
Beachten Sie, dass dies die x86-64 At & t-Syntax ist.
Wenn Sie es als Paar verwenden, können Sie ein Register auf dem Stapel speichern und später wiederherstellen. Es gibt auch andere Verwendungszwecke.
quelle
lea rsp, [rsp±8]
anstelle vonadd
/ verwendensub
, um die Wirkung vonpush
/pop
auf Flags besser zu emulieren .Fast alle CPUs verwenden Stack. Der Programmstapel ist eine LIFO- Technik mit hardwareunterstützter Verwaltung.
Der Stapel ist die Größe des Programmspeichers (RAM), der normalerweise oben auf dem CPU-Speicherhaufen zugewiesen ist und in entgegengesetzter Richtung wächst (bei PUSH-Anweisung wird der Stapelzeiger verringert). Ein Standardbegriff für das Einfügen in einen Stapel ist PUSH und für das Entfernen vom Stapel ist POP .
Der Stapel wird über das vom Stapel vorgesehene CPU-Register verwaltet, das auch als Stapelzeiger bezeichnet wird. Wenn die CPU also POP oder PUSH ausführt, lädt / speichert der Stapelzeiger ein Register oder eine Konstante in den Stapelspeicher und der Stapelzeiger wird automatisch verringert oder entsprechend der Anzahl der geschobenen Wörter erhöht oder in (von) Stapel eingeblendet.
Über Assembler-Anweisungen können wir speichern, um zu stapeln:
quelle