Ich habe mich über Folgendes gewundert. Ich habe bemerkt, dass IA32 beim Schreiben einer Assemblersprache derart entworfen ist, dass die Verwendung von relativen Sprüngen gefördert wird, d. H. Ein Verschiebungsbetrag von Bytes gegenüber der Verwendung von absoluten Sprüngen springt, d.h. eip an eine spezifische Adresse im Speicher ändert. Was ist die Logik dahinter?
Die meisten Sprünge sind auf Ziele, die nicht weit von der Sprunganweisung entfernt sind. Da Sprunganweisungen bereitgestellt werden, die einen vorzeichenbehafteten 16-Bit-Wert annehmen, können sie weniger Bytes enthalten als für einen absoluten Sprung benötigt werden (normalerweise 4 Byte plus die Anweisung selbst).
Ein kleiner zusätzlicher Vorteil von relativen Verzweigungen ist, dass sie nicht im Linker repariert werden müssen, oder, in diesem Fall, durch die zusätzliche Indirektion gehen, die in PIC (positionsunabhängiger Code) benötigt wird.
Relative Sprünge ermöglichen es Compilern, verschiebbaren Code zu generieren, was bedeutet, dass der Code irgendwo im Speicher ausgeführt wird; Es ist nicht an einen festen Standort gebunden. Dies ist das entscheidende Konzept hinter Bibliotheken: Sie können den Code einmal schreiben und in eine verschiebbare -Objektdatei kompilieren, die unverändert in ein beliebiges Programm eingebunden werden kann. Der Linker muss absolute Adressen nur für die Funktionen zuweisen, die extern zugänglich sind (so dass Ihr eigener Code sie finden kann); Alle "internen" Sprünge sind relativ und müssen sich nicht von einer ausführbaren zur nächsten ändern.
Es lohnt sich, Code in einer höheren Programmiersprache wie C zu schreiben, den Compiler Assembler-Code generieren zu lassen (prüfen Sie die Option -S
auf gcc) und dann die Assembly-Ausgabe zu lesen. Achten Sie besonders auf Bedingungen und Schleifen, wie if
, for
und while
, und Sie werden sehen, dass sie alle relative Sprünge erzeugen.
Hier ist ein künstliches Beispiel mit fiktiven Montageanweisungen:
%Vor% Hier generiert der Compiler relative Sprünge ( JGE
und JMP
), um den nicht ausgeführten Zweig des if-else
-Blocks zu überspringen. Diese Sprünge funktionieren, egal wo im Speicher der Linker den Code ablegt. Wenn sie absolute Sprünge wären, müsste der Linker die Adressen jedes Mal neu berechnen, wenn er den Code verknüpft.
Sie werden sogar feststellen, dass viele Funktionsaufrufe relative Sprünge erzeugen, insbesondere wenn die Funktionen innerhalb einer einzigen Datei liegen. Dies beschleunigt nicht nur den Verknüpfungsprozess, sondern macht den laufenden Code auch kleiner und effizienter. Das liegt daran, dass relative Adressen typischerweise auf einen viel kleineren Bereich als absolute Adressen beschränkt sind, was bedeutet, dass sie in weniger Bytes dargestellt werden können.
Hoffe das hilft!