Hier geht es um das BP / EBP / RBP-Register auf Intel-Plattformen. Dieses Register ist standardmäßig auf das Stapelsegment eingestellt (für den Zugriff auf das Stapelsegment ist kein spezielles Präfix erforderlich).
Das EBP ist die beste Wahl für den Zugriff auf Datenstrukturen, Variablen und dynamisch zugewiesenen Arbeitsbereich innerhalb des Stapels. EBP wird häufig verwendet, um auf Elemente auf dem Stapel relativ zu einem festen Punkt auf dem Stapel und nicht relativ zu den aktuellen Nutzungsbedingungen zuzugreifen. Es identifiziert typischerweise die Basisadresse des aktuellen Stapelrahmens, der für die aktuelle Prozedur eingerichtet wurde. Wenn EBP als Basisregister in einer Versatzberechnung verwendet wird, wird der Versatz automatisch im aktuellen Stapelsegment (dh dem aktuell von SS ausgewählten Segment) berechnet. Da SS nicht explizit angegeben werden muss, ist die Befehlskodierung in solchen Fällen effizienter. EBP kann auch verwendet werden, um in Segmente zu indizieren, die über andere Segmentregister adressierbar sind.
(Quelle - http://css.csail.mit.edu/6.858/2017/readings/i386/s02_03.htm )
Da auf den meisten 32-Bit-Plattformen Datensegment und Stapelsegment identisch sind, ist diese Zuordnung von EBP / RBP zum Stapel kein Problem mehr. Dies gilt auch für 64-Bit-Plattformen: Die von AMD 2003 eingeführte x86-64-Architektur hat die Unterstützung für die Segmentierung im 64-Bit-Modus weitgehend eingestellt: Vier der Segmentregister: CS, SS, DS und ES werden auf 0 gesetzt Diese Umstände von x86 32-Bit- und 64-Bit-Plattformen bedeuten im Wesentlichen, dass das EBP / RBP-Register ohne Präfix in den Prozessoranweisungen verwendet werden kann, die auf den Speicher zugreifen.
Die Compiler-Option, über die Sie geschrieben haben, ermöglicht es also, BP / EBP / RBP für andere Zwecke zu verwenden, z. B. um eine lokale Variable zu speichern.
Mit "Dies vermeidet die Anweisungen zum Speichern, Einrichten und Wiederherstellen von Frame-Zeigern" ist das Vermeiden des folgenden Codes bei der Eingabe jeder Funktion gemeint:
push ebp
mov ebp, esp
oder die enter
Anweisung, die auf Intel 80286- und 80386-Prozessoren sehr nützlich war.
Vor der Rückkehr der Funktion wird außerdem der folgende Code verwendet:
mov esp, ebp
pop ebp
oder die leave
Anweisung.
Debugging-Tools können die Stack-Daten scannen und diese Push-EBP-Registerdaten beim Auffinden verwenden call sites
, dh um die Namen der Funktion und die Argumente in der Reihenfolge anzuzeigen, in der sie hierarchisch aufgerufen wurden.
Programmierer haben möglicherweise Fragen zu Stapelrahmen nicht in einem weiten Sinne (dass es sich um eine einzelne Entität im Stapel handelt, die nur einen Funktionsaufruf bedient und die Rücksprungadresse, Argumente und lokalen Variablen beibehält), sondern im engeren Sinne - wenn der Begriff stack frames
in erwähnt wird den Kontext der Compileroptionen. Aus Sicht des Compilers ist ein Stapelrahmen nur der Eingangs- und Ausgangscode für die Routine , der einen Anker auf den Stapel schiebt - der auch zum Debuggen und zur Ausnahmebehandlung verwendet werden kann. Debugging-Tools können die Stapeldaten scannen und diese Anker für die Rückverfolgung verwenden, während sie sich call sites
im Stapel befinden, dh um die Namen der Funktion in der Reihenfolge anzuzeigen, in der sie hierarchisch aufgerufen wurden.
Aus diesem Grund ist es für einen Programmierer sehr wichtig zu verstehen, was ein Stapelrahmen in Bezug auf Compileroptionen ist - da der Compiler steuern kann, ob dieser Code generiert werden soll oder nicht.
In einigen Fällen kann der Compiler auf den Stapelrahmen (Eingangs- und Ausgangscode für die Routine) verzichten, und auf die Variablen wird direkt über den Stapelzeiger (SP / ESP / RSP) und nicht über den praktischen Basiszeiger (BP /) zugegriffen. ESP / RSP). Die Bedingungen, unter denen ein Compiler die Stapelrahmen für einige Funktionen weglässt, können unterschiedlich sein, zum Beispiel: (1) Die Funktion ist eine Blattfunktion (dh eine Endeinheit, die keine anderen Funktionen aufruft). (2) es werden keine Ausnahmen verwendet; (3) Es werden keine Routinen mit ausgehenden Parametern auf dem Stapel aufgerufen. (4) Die Funktion hat keine Parameter.
Das Weglassen von Stapelrahmen (Eingabe- und Ausstiegscode für die Routine) kann den Code kleiner und schneller machen, kann jedoch auch die Fähigkeit der Debugger beeinträchtigen, die Daten im Stapel zurückzuverfolgen und dem Programmierer anzuzeigen. Dies sind die Compileroptionen, die bestimmen, unter welchen Bedingungen eine Funktion erfüllt sein soll, damit der Compiler ihr den Stack-Frame-Ein- und Ausstiegscode zuweist. Beispielsweise kann ein Compiler in den folgenden Fällen Optionen zum Hinzufügen eines solchen Eingangs- und Ausgangscodes zu Funktionen haben: (a) immer, (b) nie, (c) bei Bedarf (Angabe der Bedingungen).
Zurück von den Allgemeinheiten zu den Besonderheiten: Wenn Sie die -fomit-frame-pointer
GCC-Compileroption verwenden, können Sie sowohl beim Eingabe- als auch beim Ausstiegscode für die Routine und beim Vorhandensein eines zusätzlichen Registers gewinnen (es sei denn, es ist standardmäßig entweder selbst oder implizit von anderen aktiviert Optionen, in diesem Fall profitieren Sie bereits vom Gewinn der Verwendung des EBP / RBP-Registers, und durch explizite Angabe dieser Option wird kein zusätzlicher Gewinn erzielt, wenn sie bereits implizit aktiviert ist. Beachten Sie jedoch, dass das BP-Register im 16-Bit- und 32-Bit-Modus nicht wie AX (AL und AH) auf 8-Bit-Teile zugreifen kann.
Da diese Option nicht nur dem Compiler ermöglicht, EBP als Allzweckregister für Optimierungen zu verwenden, sondern auch das Generieren von Exit- und Entry-Code für den Stack-Frame verhindert, was das Debuggen erschwert, wird in der GCC-Dokumentation ausdrücklich angegeben (ungewöhnlich fett hervorgehoben) Stil), dass das Aktivieren dieser Option das Debuggen auf einigen Computern unmöglich macht
Beachten Sie auch, dass andere Compileroptionen, die sich auf das Debuggen oder Optimieren beziehen, die -fomit-frame-pointer
Option möglicherweise implizit ein- oder ausschalten.
Ich habe auf gcc.gnu.org keine offiziellen Informationen darüber gefunden, wie sich andere Optionen -fomit-frame-pointer
auf x86-Plattformen auswirken , https://gcc.gnu.org/onlinedocs/gcc-3.4.4/gcc/Optimize-Options.html gibt nur folgendes an:
-O aktiviert auch -fomit-frame-pointer auf Computern, auf denen dies das Debuggen nicht beeinträchtigt.
Aus der Dokumentation an sich geht also nicht hervor, ob sie aktiviert-fomit-frame-pointer
wird, wenn Sie nur mit einer einzigen -O
Option auf der x86-Plattform kompilieren . Es kann empirisch getestet werden, aber in diesem Fall sind die GCC-Entwickler nicht verpflichtet, das Verhalten dieser Option in Zukunft nicht ohne vorherige Ankündigung zu ändern.
Doch Peter Cordes hat in den Kommentaren darauf hingewiesen , dass es einen Unterschied für die Standardeinstellungen der -fomit-frame-pointer
zwischen x86-16 - Plattformen und x86-32 / 64 - Plattformen.
Diese Option - -fomit-frame-pointer
- ist auch für den Intel C ++ Compiler 15.0 relevant , nicht nur für den GCC:
Für den Intel Compiler hat diese Option einen Alias /Oy
.
Folgendes hat Intel darüber geschrieben:
Diese Optionen bestimmen, ob EBP bei Optimierungen als Allzweckregister verwendet wird. Die Optionen -fomit-frame-pointer und / Oy ermöglichen diese Verwendung. Optionen -fno-omit-frame-pointer und / Oy- verbieten es.
Einige Debugger erwarten, dass EBP als Stapelrahmenzeiger verwendet wird, und können keine Stapelrückverfolgung erzeugen, wenn dies nicht der Fall ist. Die Optionen -fno-omit-frame-pointer und / Oy- weisen den Compiler an, Code zu generieren, der EBP als Stack-Frame-Zeiger für alle Funktionen verwaltet und verwendet, sodass ein Debugger weiterhin einen Stack-Backtrace erstellen kann, ohne Folgendes zu tun:
Für -fno-omit-frame-pointer: Deaktivieren von Optimierungen mit -O0 Für / Oy-: Deaktivieren von / O1-, / O2- oder / O3-Optimierungen Die Option -fno-omit-frame-pointer wird festgelegt, wenn Sie die Option - angeben. O0 oder die Option -g. Die Option -fomit-frame-pointer wird festgelegt, wenn Sie die Option -O1, -O2 oder -O3 angeben.
Die Option / Oy wird festgelegt, wenn Sie die Option / O1, / O2 oder / O3 angeben. Option / Oy- wird festgelegt, wenn Sie die Option / Od angeben.
Die Verwendung des Option -fno-omit-frame-pointer oder der Option / Oy- reduziert die Anzahl der verfügbaren Universalregister um 1 und kann zu etwas weniger effizientem Code führen.
HINWEIS Für Linux * -Systeme: Derzeit liegt ein Problem mit der Behandlung von GCC 3.2-Ausnahmen vor. Daher ignoriert der Intel-Compiler diese Option, wenn GCC 3.2 für C ++ installiert und die Ausnahmebehandlung aktiviert ist (Standardeinstellung).
Bitte beachten Sie, dass das obige Zitat nur für den Intel C ++ 15-Compiler relevant ist, nicht für GCC.