Ich habe einen langen Katalog sehr guter Artikel über die Windows x64 ABI gelesen. Ein sehr kleiner Aspekt dieser Artikel ist die Beschreibung des Rahmenzeigers. Der Grundgedanke ist, dass, weil die Windows x64-Call-Stack-Regeln so starr sind, ein dedizierter Frame-Pointer normalerweise nicht benötigt wird, obwohl er optional ist.
Die einzige Ausnahme, die ich regelmäßig gesehen habe, ist, wenn alloca()
verwendet wird um Speicher auf dem Stapel dynamisch zuzuweisen. Funktionen, die dies scheinbar tun, erfordern einen Bildzeiger. Zum Beispiel, um aus der Microsoft-Dokumentation auf "Stack Allocation" zu zitieren (kursiv und fett von mir hinzugefügt) ):
Wenn Speicherplatz in einer Funktion dynamisch zugewiesen wird (alloca), muss ein nichtflüchtiges Register als Rahmenzeiger verwendet werden, um die Basis des festen Teils des Stacks und dieses Registers müssen im Prolog gespeichert und initialisiert werden. Beachten Sie, dass bei Verwendung von Alloca Anrufe von demselben Anrufer an denselben Angerufenen möglicherweise unterschiedliche Heimatadressen für ihre Registerparameter haben.
Dazu fügt die x64 ABI alloca()
-Dokumentation von Microsoft kryptisch Folgendes hinzu:
_alloca muss 16-Byte-ausgerichtet sein und zusätzlich muss einen Rahmenzeiger verwenden
.
Zuerst, warum muss es verwendet werden? Ich gehe davon aus, dass sich der Call-Stack auf eine Ausnahme zurückzieht, aber ich habe noch keine befriedigende Erklärung gefunden.
Nächste Frage: wo muss es zeigen? Im ersten der beiden obigen Zitate heißt es, dass "es" verwendet werden muss, um die Basis des " festen Teils des Stapels" zu markieren. Was ist der "feste Teil des Stapels"? Ich habe den Eindruck, dass dieser Begriff in einem gegebenen Rahmen den Bereich von Adressen bezeichnet, die (höhere Adressen zu niedrigeren) umfasst:
Auch hier habe ich keine befriedigende Definition für diesen "festen Teil" gefunden. Die Seite "Stack Allocation" , mit der ich oben verlinkt bin, enthält das folgende Diagramm mit den Worten "if verwendet wird, wird der Stapelzeiger allgemein hierhin zeigen ":
Dieser sehr nette Blog-Beitrag ist ebenso vage, einschließlich ein Diagramm, das besagt, dass der Rahmenzeiger "irgendwo hierhin zeigt", wobei "hier" die Adressen für die gespeicherten nichtflüchtigen Register und die Ortsnamen sind.
Ein letztes kryptisches Detail aus dem Microsoft-MSDN-Artikel "Dynamic Parameter Stack Area Construction" >, die nur das enthält:
Wenn ein Rahmenzeiger verwendet wird, besteht die Option, den Parameterstapelbereich dynamisch zu erstellen. Dies wird derzeit nicht im x64-Compiler durchgeführt.
Was bedeutet "allgemein"? Wo ist "irgendwo hier"? Welche Option gibt es? Gibt es eine Regel? Wen interessiert das?
Oder, tl; dr: Was der Titel fragt. Jede Antwort mit einer kommentierten Assembly wurde dankbar angenommen.
Das Diagramm macht deutlich, dass der Frame-Zeiger auf den unteren Teil des festen Teils des lokalen Stack-Frames zeigt. Der "feste Teil" ist der Teil, dessen Größe sich nicht ändert und dessen Position relativ zu dem anfänglichen Stapelzeiger fixiert ist. Im Diagramm ist es mit "Lokale Variablen und gespeicherte nichtflüchtige Register" gekennzeichnet. [1]
Die genaue Position des Frame-Zeigers spielt für das Betriebssystem keine Rolle, da von einem informationstheoretischen Standpunkt aus lokale Variablen nicht von dem durch alloca
zugewiesenen Speicher unmittelbar nach dem Eintritt in eine Funktion unterschieden werden können.
Das Betriebssystem hat keine Möglichkeit, zwischen diesen beiden Funktionen zu unterscheiden. Beide speichern a
auf dem Stapel direkt unter den nichtflüchtigen Registern.
Diese Äquivalenz ist der Grund, warum das Diagramm "allgemein" sagt. In der Praxis verweisen Compiler darauf, wo angegeben, aber in der Theorie könnten sie irgendwo innerhalb des lokalen Rahmens zeigen, solange der Abstand vom Rahmenzeiger zur Rückkehradresse eine Konstante ist.
Die Funktion muss das Betriebssystem informieren, wo sich der Rahmenzeiger befindet, damit der Stapel während der Ausnahmebehandlung abgewickelt werden kann. Ohne diese Informationen wäre es nicht möglich, den Stapel zu durchlaufen, da der Rahmen eine variable Größe hat.
[1] Sie können dies aus der Tatsache ableiten, dass der Text sagt, dass der Rahmenzeiger auf "die Basis des fixierten Teils des Stapels" zeigt und das Diagramm sagt "Der Rahmenzeiger wird hier generell zeigen", und es ist zeigt auf die Basis der lokalen Variablen und gespeicherte nichtflüchtige Register. Unter der Annahme, dass der Text und das Diagramm übereinstimmen, impliziert dies, dass der feste Teil des Stapels mit den lokalen Variablen und den gespeicherten nichtflüchtigen Registern übereinstimmt. Das ist die gleiche Schlussfolgerung, die Sie jeden Tag machen, ohne es zu merken. Zum Beispiel, wenn eine Geschichte sagt
rief Sally ihrem Bruder zu. "Billy, wo bist du?"
Sie können daraus schließen, dass Billy Sallys Bruder ist.
alloca
soll mit einer Größe verwendet werden, die nur zur Laufzeit verfügbar ist. Daher wird der Stapelzeiger um einen Betrag geändert, der zum Zeitpunkt der Kompilierung nicht bekannt ist. Normalerweise können Sie Ihre lokalen Variablen und Argumente auf dem Stack relativ zum Stack-Pointer aufgrund des festen Layouts adressieren, aber alloca
führt dazu, dass Sie ein weiteres stabiles Register benötigen. Dieser Rahmenzeiger kann beliebig zeigen, solange Sie die Beziehung zum festen Bereich kennen.
Der Rahmenzeiger ist auch nützlich, wenn die Zeit gekommen ist, den alloca
Speicher freizugeben, weil Sie einfach den Stapelzeiger an einen bekannten Ort wiederherstellen können, ohne sich darum kümmern zu müssen, wie stark sich der Stapelzeiger geändert hat.
Ich glaube nicht, dass die ABI den Rahmenzeiger als solchen benötigt oder dass er rbp
sein muss oder dass er auf einen bestimmten Ort zeigen muss (Haftungsausschluss: Ich benutze keine Fenster).
Tags und Links c assembly 64bit windows calling-convention