Beim Erstellen meines Assemblers für die x86-Plattform sind beim Codieren der Anweisung JMP
einige Probleme aufgetreten:
( von meiner Lieblings-x86-Instruktionswebsite, Ссылка )
Alle sind relative Sprünge, wobei die Größe jeder Codierung (Operation + Operand) in der dritten Spalte liegt.
Jetzt hat mein ursprüngliches (und deshalb Fehler) Design den maximalen (5 Bytes) Platz für jeden Befehl reserviert. Der Operand ist noch nicht bekannt, weil es ein Sprung zu einem noch unbekannten Ort ist. Also habe ich einen "rewrite" -Mechanismus implementiert, der die Operanden an der richtigen Stelle im Speicher neu schreibt, wenn der Sprung bekannt ist, und den Rest mit NOP
s füllt. Dies ist ein ernsthaftes Problem in engen Schleifen.
Jetzt ist mein Problem mit der folgenden Situation:
%Vor% Das Problem ist, dass ich die kleinstmögliche Kodierung für eine JMP
Anweisung (und keine NOP
Füllung) möchte.
Ich muss die Größe der Anweisung bei c
kennen, bevor ich den relativen Abstand zwischen a
und b
für den Operanden bei d
berechnen kann. Dasselbe gilt für die JMP
at c
: Sie müssen die Größe von d
kennen, bevor sie den relativen Abstand zwischen e
und a
berechnen kann.
Wie lösen bestehende Assemblierer dieses Problem oder wie würden Sie dies tun?
Das ist, was ich denke, das das Problem löst:
Zuerst kodiere alle Befehle, um zwischen dem
JMP
und seinem Ziel zu operieren, wenn diese Region einen Opcode mit variabler Größe enthält, verwende die maximale Größe, z.5
für einJMP
. Dann kodiere das relativeJMP
zu seinem Ziel, indem du die kleinste mögliche Kodierungsgröße wählst (3, 4 oder 5) und den Abstand errechnest. Wenn ein Opcode mit variabler Größe codiert ist, ändern Sie alle absoluten Operanden vor und alle relativen Befehle, die diese codierte Anweisung überspringen: Sie werden neu codiert, wenn sich ihr Operand ändert, um die kleinstmögliche Größe zu wählen. Diese Methode endet garantiert, da Opcodes mit variabler Größe nur schrumpfen können (weil sie die maximale Größe verwenden).
Ich frage mich, vielleicht ist das eine überentwickelte Lösung , deshalb stelle ich diese Frage.
Hier ist ein Ansatz, den ich verwendet habe, der vielleicht ineffizient erscheint, aber sich als nicht für den meisten echten Code (Pseudocode) erweist:
%Vor% Im ersten Durchlauf haben Sie eine sehr gute Annäherung an den zu verwendenden jmp
code, indem Sie eine pessimistische Bytezählung für alle Sprungbefehle verwenden.
Im zweiten Durchgang können Sie die Sprünge mit dem gewählten pessimistischen Opcode ausfüllen. Sehr wenige Sprünge könnten dann umgeschrieben werden, um ein Byte oder zwei weniger zu verwenden, nur solche, die ursprünglich sehr nahe an dem Sprungschwellwert von 8/16 Bit oder 16/32 Byte waren. Da die Kandidaten alle Sprünge von vielen Bytes sind, sind sie weniger wahrscheinlich in kritischen Schleifensituationen, so dass Sie wahrscheinlich feststellen werden, dass weitere Durchgänge wenig oder keinen Vorteil gegenüber einer Lösung mit zwei Durchgängen bieten.
Tags und Links x86 encoding instruction-set