2012-06-26 4 views
5

Ich verstehe, was ein Emulator tut, eine Maschinensprache in eine andere zu ändern, oft "Just-in-time". Könnte ein solches Programm darin bestehen, dass es eine Binärdatei einliest, die für eine Architektur geschrieben ist, und eine neue Binärdatei für eine andere Architektur speichert. Nach Abschluss des Prozesses würde der Benutzer mit Binärdateien für die native Ausführung in der angegebenen Architektur bereit sein. Dies wäre besonders nützlich für diejenigen, die teure proprietäre Anwendungen für eine Legacy-Architektur haben.Warum muss die Emulation in Echtzeit durchgeführt werden?

Ist es möglich, eine solche Anwendung zu machen? Binäre Rekompilierung ist kein neues Konzept, aber ich habe noch keine brauchbaren Implementierungen gefunden.

Mit Hilfe von einigen anderen, würde ich Implementierung eines solchen Programms Codierung auf einer Open-Source zu starten begeistert sein, wenn die Programmierung einer solchen Möglichkeit.

+1

Es ist ein paar Mal getan. Zum Beispiel kompilierte DEC FX! 32 X86-Binärdateien auf einem DEC-Alpha, oft schneller als jedes x86 der Zeit könnte. Es war jedoch nicht genug, um das (falsche) Management von DEC auszugleichen, und Compaq/HP kümmerte sich nicht darum. –

+0

Warum existieren Emulatoren überhaupt, wenn das der Fall ist? Ist das wirklich * viel * schwieriger als einen Emulator zu schreiben? – user1483857

Antwort

0

Sie müssten, indem sie sicher, dass alle referenzierten Bibliotheken sind neu kompiliert mit diesem als auch beginnen.

Es ist möglich, aber ein großes Unternehmen.

Sehen Sie sich auch könnte es Lizenzprobleme werden durch diese Weise; Sie erstellen eine Derivat-Arbeit auf der Grundlage der ursprünglichen Software. Die meisten Lizenzen, mit denen Sie dies tun können, werden Ihnen auch die Quelle zur Verfügung stellen, sodass Sie die Quelle einfach neu kompilieren oder portieren können, was viel einfacher ist.

+0

Also, das würde nicht genau als Reverse Engineering gezählt werden, oder? Ich würde gerne so ein Biest machen, nur damit es einen Präzedenzfall in den Gerichten gibt. Eine nützliche Anwendung würde Closed-Source-PPC-Binärdateien auf einem OS X Mountain Lion laufen, wo viele der erforderlichen lib in der OS sind oder könnten von Snow Leopard oder Lion portiert werden. Eine andere wäre die Konsolenemulation, bei der die meisten Libs im Spiel sind. – user1483857

+1

Nicht wirklich, Ihr Re-Compiler würde: "lesen-> übersetzen-> neue Version schreiben". Das klingt mehr nach Derivat-Arbeit als Reverse Engineering. Reverse Engineering versteht **, was es tut und schreibt neuen Code, der das Gleiche tut. – WhyNotHugo

+0

Eine nützliche Anwendung würde Closed-Source-PPC-Binärdateien auf einem OS X Mountain Lion laufen, wo viele der erforderlichen lib in der OS sind oder könnten von Snow Leopard oder Lion portiert werden. Eine andere wäre die Konsolenemulation, bei der die meisten Libs im Spiel sind. Würde ich in rechtliche Schwierigkeiten geraten? – user1483857

3

Ich glaube, Sie suchen nach statischen vs dynamischen Neukompilierung. Dynamische Neukompilierung beschreiben Sie als "Echtzeit" -Emulation oder Neukompilierung. Der Code wird in Blöcken neu kompiliert, wodurch der Emulator die Laufzeitumgebung des ursprünglichen Codes genau wiedergeben kann.

Static Neuübersetzung ist, was Sie fragen, ob es möglich ist. Es ist in vielen verschiedenen Situationen möglich, wie einige darauf hingewiesen haben, jedoch kann Code, der sehr spezifische Laufzeitbeschränkungen erwartet, nach einer statischen Neukompilierung möglicherweise nicht erfolgreich ausgeführt werden. Aus diesem Grund kann Corn, ein N64-Emulator, der statische Neukompilierung verwendet, nur sehr wenige hoch handoptimierte Spiele ausführen, während andere N64-Emulatoren, die dynamische Neukompilierung verwenden, eine viel größere Auswahl an Spielen ausführen.

Statische Neukompilierung ist in der Tat möglich für noch komplexeren und traditionellen Code (zB x86 zu PowerPC), aber ein solches Unterfangen würde sich als extrem mühsam erweisen, da der Compiler viele Tricks verwenden müsste, um den erzeugten statischen Code laufen zu lassen zuverlässig auf der Zielmaschine. Dynamische Rekompiler können dies während der Laufzeit zu einem Bruchteil des Entwicklungsaufwands und zu vernachlässigbaren Kosten in der Leistung durchführen.

+0

Auch ich glaube, es gibt Unterschiede zwischen Fließkomma-Implementierung in verschiedenen Architekturen, aber ich bin mir nicht sicher. –

4

Statische Neukompilierung ist eine vielversprechende Möglichkeit, Binärdateien von einer fremden Architektur in eine andere Zielarchitektur zu übersetzen. Es wäre schneller als Just-In-Time (JIT), weil es den Code nicht direkt vor dem Ausführen kompilieren muss, und weil die zusätzliche Kompilierungszeit nützlich ist, um den Generierungscode zu optimieren.

jedoch JIT-Kompilierung verwendet dynamische Programmanalyse, während statische Neukompilierung auf statische Programmanalyse beruht (daher der Name).

In der statischen Analyse haben Sie keine Laufzeitinformationen zu einer Ausführung.

Ein großes Problem mit diesem durch indirekte Sprünge gestellt. Der Begriff umfasst Code, der aus bestimmten switch-Anweisungen, aus der Verwendung von Funktionszeigern oder aus Laufzeitpolymorphien (think virtual table) generiert werden kann. Es läuft alles auf eine Anweisung der Form nach unten:

JMP reg_A 

Angenommen, Sie haben die Startadresse des Programms kennen, und Sie sich entschieden Anweisungen neu kompiliert werden von diesem Punkt zu beginnen. Wenn Sie auf einen direkten Sprung stoßen, gehen Sie zu seiner Zieladresse und Sie setzen die Neukompilierung von dort fort. Wenn Sie jedoch auf einen indirekten Sprung stoßen, stecken Sie fest. In dieser Bauanleitung ist der Inhalt von reg_A statisch nicht bekannt. Daher kennen wir die Adresse der nächsten Anweisung nicht. Beachten Sie, dass dieses Problem bei der dynamischen Neukompilierung nicht auftritt, da wir den virtuellen Zustand der Register emulieren und den aktuellen Inhalt von kennen. Außerdem sind Sie bei der statischen Neukompilierung daran interessiert, zu diesem Zeitpunkt alle möglichen Werte für reg_A zu finden, da Sie alle möglichen Pfade kompilieren möchten. In der dynamischen Analyse benötigen Sie nur den aktuellen Wert, um den gerade ausgeführten Pfad zu generieren. Wenn der Wert geändert wird, können Sie weiterhin die anderen Pfade generieren. In einigen Fällen kann die statische Analyse eine Liste von Kandidaten finden (wenn es eine switch ist, muss es irgendwo eine Tabelle des möglichen Offset geben), aber im allgemeinen Fall wissen wir einfach nicht.

Feine, sagen Sie, sich alle Anweisungen dann in den binären neu kompilieren!

Das Problem hier ist, dass in den meisten Binärdateien sowohl Code und Daten enthalten. Abhängig von der Architektur können Sie möglicherweise nicht feststellen, welche welche ist.

Schlimmer noch, in einigen Architekturen gibt es keine Ausrichtungsbeschränkungen und Befehle mit variabler Breite, und Sie können an einem bestimmten Punkt beginnen, sich zu zerlegen, nur um festzustellen, dass Sie mit einem Offset neu kompiliert haben.

ist eine vereinfachte Befehlssatz, der zwei Befehle und ein einzelnes Register A Lassen Sie nehmen:

41 xx (size 2): Add xx to `A`. 
42 (size 1): Increment `A` by one. 

Nehmen wir folgendes Binärprogrammordnung:

41 42 

Lassen Sie uns sagen, dass der Startpunkt ist das erste Byte 41 . Sie tun:

41 42 (size 2): Add 42 to `A`. 

Aber was, wenn 41 ist ein Stück von Daten? Dann wird Ihr Programm wird:

42 (size 1): Increment `A` by one. 

Dieses Problem wird in alten Spielen vergrößert wird, die oft direkt in der Montage optimiert wurde, und wo der Programmierer könnte absichtlich expect some byte to be interpreted as both code and data, depending on the context!

Noch schlimmer könnte das neu kompilierte Programm zu erzeugen Code selbst! Stellen Sie sich vor, Sie würden einen JIT-Compiler neu kompilieren. Das Ergebnis würde immer noch Code für die Quellarchitektur ausgeben und versuchen, dorthin zu springen, was höchstwahrscheinlich dazu führen würde, dass das Programm sehr bald stirbt. Statisches Neukompilieren von Code, der nur zur Laufzeit verfügbar ist, erfordert unendliche Tricks!

Statische binäre Analyse ist ein sehr lebender Bereich der Forschung (hauptsächlich im Bereich der Sicherheit, nach Schwachstellen in Systemen zu suchen, deren Quellen nicht verfügbar sind), und tatsächlich kenne ich einen Versuch, eine NES emulator that tries to statically recompile programs zu produzieren. Der Artikel ist sehr interessant.

Ein Kompromiss zwischen JIT und statischen Neukompilierung wäre statisch so viel Code wie möglich neu zu kompilieren, nur die Bits zu halten, die nicht statisch übersetzt werden kann.

+0

Große Erklärung. Ich möchte einen weiteren möglichen Kompromiss zwischen der statischen/dynamischen Neukompilierung erwähnen - Caching (zu einer Datei) von dynamisch neu kompilierten Code-Chunks. Vielleicht würde dieser Ansatz viel von dem Vorteil der statischen Neukompilierung profitieren, die durch die Fähigkeit zur "Code-Pfad-Erkennung" der Laufzeitanalyse verbessert wird. – Cauterite

Verwandte Themen