Gibt es intelligente Fälle von Laufzeitcode-Änderungen?


119

Können Sie sich legitime (intelligente) Verwendungen für die Änderung des Laufzeitcodes vorstellen (Programm, das zur Laufzeit seinen eigenen Code ändert)?

Moderne Betriebssysteme scheinen Programme, die dies tun, zu missbilligen, da diese Technik von Viren verwendet wurde, um eine Erkennung zu vermeiden.

Ich kann mir nur eine Art Laufzeitoptimierung vorstellen, bei der Code entfernt oder hinzugefügt wird, indem zur Laufzeit etwas bekannt wird, das zur Kompilierungszeit nicht bekannt ist.


8
In modernen Architekturen beeinträchtigt dies das Caching und die Anweisungspipeline erheblich: Selbstmodifizierender Code würde den Cache nicht ändern, sodass Sie Barrieren benötigen würden, was Ihren Code wahrscheinlich verlangsamen würde. Und Sie können keinen Code ändern, der sich bereits in der Anweisungspipeline befindet. Daher muss jede Optimierung, die auf selbstmodifizierendem Code basiert, lange vor dem Ausführen des Codes durchgeführt werden, um eine Leistungseffektivität zu erzielen, die beispielsweise einer Laufzeitprüfung überlegen ist.
Alexandre C.

7
@Alexandre: Es ist üblich, dass selbstmodifizierender Code Änderungen selten (z. B. einmal, zweimal) vornimmt, obwohl er beliebig oft ausgeführt wird, sodass die einmaligen Kosten unbedeutend sein können.
Tony Delroy

7
Ich bin mir nicht sicher, warum dies mit C oder C ++ gekennzeichnet ist, da keiner einen Mechanismus dafür hat.
MSalters

4
@Alexandre: Microsoft Office ist dafür bekannt, genau das zu tun. Infolgedessen (?) Bieten alle x86-Prozessoren eine hervorragende Unterstützung für selbstmodifizierenden Code. Auf anderen Prozessoren ist eine kostspielige Synchronisation erforderlich, was das Ganze weniger attraktiv macht.
Mackie Messer

3
@Cawas: Normalerweise lädt die Software zur automatischen Aktualisierung neue Assemblys und / oder ausführbare Dateien herunter und überschreibt die vorhandenen. Dann wird die Software neu gestartet. Dies ist, was Firefox, Adobe usw. tun. Selbstmodifizierend bedeutet normalerweise, dass Code zur Laufzeit von der Anwendung aufgrund einiger Parameter in den Speicher neu geschrieben wird und nicht unbedingt auf der Festplatte gespeichert bleibt. Beispielsweise kann es ganze Codepfade optimieren, wenn es intelligent erkennen kann, dass diese Pfade während dieses bestimmten Laufs nicht ausgeführt werden, um die Ausführung zu beschleunigen.
NotMe

Antworten:


117

Es gibt viele gültige Fälle für Codeänderungen. Das Generieren von Code zur Laufzeit kann nützlich sein für:

  • Einige virtuelle Maschinen verwenden die JIT-Kompilierung , um die Leistung zu verbessern.
  • Das Generieren von Spezialfunktionen im laufenden Betrieb ist in der Computergrafik seit langem üblich. Siehe z. B. Rob Pike und Bart Locanthi und John Reiser Hardware-Software-Kompromisse für Bitmap-Grafiken auf dem Blit (1984) oder diesen Beitrag (2006) von Chris Lattner über Apples Verwendung von LLVM zur Laufzeitcode-Spezialisierung in ihrem OpenGL-Stack.
  • In einigen Fällen greift die Software auf eine als Trampolin bekannte Technik zurück, bei der Code dynamisch auf dem Stapel (oder an einem anderen Ort) erstellt wird. Beispiele sind die verschachtelten Funktionen von GCC und der Signalmechanismus einiger Unices.

Manchmal wird Code zur Laufzeit in Code übersetzt (dies wird als dynamische binäre Übersetzung bezeichnet ):

  • Emulatoren wie Rosetta von Apple verwenden diese Technik, um die Emulation zu beschleunigen. Ein weiteres Beispiel ist die Code-Morphing-Software von Transmeta .
  • Anspruchsvolle Debugger und Profiler wie Valgrind oder Pin verwenden es, um Ihren Code zu instrumentieren, während er ausgeführt wird.
  • Bevor der x86-Befehlssatz erweitert wurde, konnte Virtualisierungssoftware wie VMWare privilegierten x86-Code nicht direkt in virtuellen Maschinen ausführen. Stattdessen musste es problematische Anweisungen im laufenden Betrieb in geeigneteren benutzerdefinierten Code übersetzen.

Die Codeänderung kann verwendet werden, um Einschränkungen des Befehlssatzes zu umgehen:

  • Es gab eine Zeit (vor langer Zeit, ich weiß), in der Computer keine Anweisungen hatten, von einem Unterprogramm zurückzukehren oder den Speicher indirekt zu adressieren. Selbstmodifizierender Code war die einzige Möglichkeit, Unterprogramme, Zeiger und Arrays zu implementieren .

Weitere Fälle von Codeänderungen:

  • Viele Debugger ersetzen Anweisungen zum Implementieren von Haltepunkten .
  • Einige dynamische Linker ändern den Code zur Laufzeit. Dieser Artikel bietet Hintergrundinformationen zur Laufzeitverschiebung von Windows-DLLs, bei denen es sich effektiv um eine Form der Codeänderung handelt.

10
Diese Liste scheint Beispiele für Code zu vermischen, der sich selbst ändert, und Code, der anderen Code wie Linker ändert.
AShelly

6
@AShelly: Nun, wenn Sie den dynamischen Linker / Loader als Teil des Codes betrachten, ändert er sich selbst. Sie leben im selben Adressraum, daher denke ich, dass dies eine gültige Sichtweise ist.
Mackie Messer

1
Ok, die Liste unterscheidet jetzt zwischen Programmen und Systemsoftware. Ich hoffe das macht Sinn. Am Ende ist jede Klassifizierung umstritten. Es kommt darauf an, was genau Sie in die Definition des Programms (oder Codes) einbeziehen.
Mackie Messer

35

Dies wurde in Computergrafiken durchgeführt, insbesondere in Software-Renderern zu Optimierungszwecken. Zur Laufzeit wird der Status vieler Parameter untersucht und eine optimierte Version des Rasterizer-Codes generiert (wodurch möglicherweise viele Bedingungen beseitigt werden), mit der Grafikprimitive, z. B. Dreiecke, viel schneller gerendert werden können.


5
Eine interessante Lektüre sind Michael Abrashs dreiteilige Pixomatic-Artikel über DDJ: drdobbs.com/architecture-and-design/184405765 , drdobbs.com/184405807 , drdobbs.com/184405848 . Der zweite Link (Teil 2) befasst sich mit dem Pixomatic-Code-Schweißer für die Pixel-Pipeline.
typo.pl

1
Ein sehr schöner Artikel zum Thema. Ab 1984, aber immer noch eine gute Lektüre: Rob Pike und Bart Locanthi und John Reiser. Hardware-Software-Kompromisse für Bitmap-Grafiken auf dem Blit .
Mackie Messer

5
Charles Petzold erklärt ein Beispiel dieser Art in einem Buch mit dem Titel "Beautiful Code": amazon.com/Beautiful-Code-Leading-Programmers-Practice/dp/…
Nawaz

3
Diese Antwort spricht über das Generieren von Code, aber die Frage ist über das Ändern von Code ...
Timwi

3
@ Timwi - es hat Code geändert. Anstatt eine große Kette von Ifs zu verarbeiten, wurde die Form einmal analysiert und der Renderer neu geschrieben, sodass er für den richtigen Formtyp eingerichtet wurde, ohne jedes Mal überprüfen zu müssen. Interessanterweise ist dies jetzt bei opencl-Code üblich - da er im laufenden Betrieb kompiliert wird, können Sie ihn zur Laufzeit für den speziellen Fall umschreiben
Martin Beckett

23

Ein triftiger Grund ist, dass dem asm-Befehlssatz einige notwendige Befehle fehlen, die Sie selbst erstellen können. Beispiel: Auf x86 gibt es keine Möglichkeit, einen Interrupt für eine Variable in einem Register zu erstellen (z. B. Interrupt mit Interrupt-Nummer in ax). Es waren nur im Opcode codierte Konstantennummern zulässig. Mit selbstmodifizierendem Code könnte man dieses Verhalten emulieren.


Meinetwegen. Gibt es eine Verwendung dieser Technik? Es scheint gefährlich.
Alexandre C.

4
@Alexandre C.: Wenn ich mich recht erinnere, mussten viele Laufzeitbibliotheken (C, Pascal, ...) eine Funktion DOS-mal ausführen, um Interrupt-Aufrufe auszuführen. Da eine solche Funktion die Interrupt-Nummer als Parameter erhält, mussten Sie eine solche Funktion angeben (natürlich hätten Sie bei konstanter Nummer den richtigen Code generieren können, aber das war nicht garantiert). Und alle Bibliotheken haben es mit selbstmodifizierendem Code implementiert.
Flolo

Sie können einen Schalter verwenden, um dies ohne Codeänderung zu tun. Die Verkleinerung ist, dass der
Ausgabecode

17

Einige Compiler verwendeten es für die Initialisierung statischer Variablen, um die Kosten einer Bedingung für nachfolgende Zugriffe zu vermeiden. Mit anderen Worten, sie implementieren "Diesen Code nur einmal ausführen", indem sie diesen Code bei der ersten Ausführung mit No-Ops überschreiben.


1
Sehr schön, besonders wenn es darum geht, Mutex-Sperren / Entsperrungen zu vermeiden.
Tony Delroy

2
"Ja wirklich?" Wie funktioniert dies für ROM-basierten Code oder für Code, der im schreibgeschützten Codesegment ausgeführt wird?
Ira Baxter

1
@Ira Baxter: Jeder Compiler, der verschiebbaren Code ausgibt, weiß, dass das Codesegment zumindest während des Startvorgangs beschreibbar ist. Die Aussage "Einige Compiler haben es verwendet" ist also weiterhin möglich.
MSalters

17

Es gibt viele Fälle:

  • Viren verwendeten häufig selbstmodifizierenden Code, um ihren Code vor der Ausführung zu "deobfuscieren". Diese Technik kann jedoch auch nützlich sein, um Reverse Engineering, Cracking und unerwünschtes Hacken zu vereiteln
  • In einigen Fällen kann es während der Laufzeit einen bestimmten Punkt geben (z. B. unmittelbar nach dem Lesen der Konfigurationsdatei), an dem bekannt ist, dass für den Rest der Lebensdauer des Prozesses immer oder nie ein bestimmter Zweig verwendet wird: und nicht unnötig Wenn Sie eine Variable überprüfen, um festzustellen, in welche Richtung verzweigt werden soll, kann der Verzweigungsbefehl selbst entsprechend geändert werden
    • z. B. kann bekannt werden, dass nur einer der möglichen abgeleiteten Typen behandelt wird, so dass der virtuelle Versand durch einen bestimmten Anruf ersetzt werden kann
    • Nachdem festgestellt wurde, welche Hardware verfügbar ist, kann die Verwendung eines passenden Codes fest codiert werden
  • Unnötiger Code kann durch No-Op-Anweisungen oder einen Sprung darüber ersetzt werden oder das nächste Codebit kann direkt an seinen Platz verschoben werden (einfacher, wenn positionsunabhängige Opcodes verwendet werden).
  • Code, der geschrieben wurde, um das eigene Debuggen zu erleichtern, kann eine vom Debugger an einer strategischen Stelle erwartete Trap- / Signal- / Interrupt-Anweisung einfügen.
  • Einige Prädikatausdrücke, die auf Benutzereingaben basieren, werden möglicherweise von einer Bibliothek in nativen Code kompiliert
  • Inlining einiger einfacher Operationen, die erst zur Laufzeit sichtbar sind (z. B. aus einer dynamisch geladenen Bibliothek) ...
  • Bedingendes Hinzufügen von Selbstinstrumentierungs- / Profilierungsschritten
  • Risse können als Bibliotheken implementiert werden, die den Code ändern, der sie lädt (nicht "selbst" modifiziert, sondern dieselben Techniken und Berechtigungen benötigt).
  • ...

Die Sicherheitsmodelle einiger Betriebssysteme bedeuten, dass selbstmodifizierender Code nicht ohne Root- / Administratorrechte ausgeführt werden kann, was ihn für den allgemeinen Gebrauch unpraktisch macht.

Aus Wikipedia:

Anwendungssoftware, die unter einem Betriebssystem mit strenger W ^ X-Sicherheit ausgeführt wird, kann keine Anweisungen auf Seiten ausführen, auf die geschrieben werden darf. Nur das Betriebssystem selbst darf Anweisungen sowohl in den Speicher schreiben als auch später ausführen.

Unter solchen Betriebssystemen benötigen sogar Programme wie die Java-VM Root- / Administratorrechte, um ihren JIT-Code auszuführen. ( Weitere Informationen finden Sie unter http://en.wikipedia.org/wiki/W%5EX. )


2
Sie benötigen keine Root-Rechte für selbstmodifizierenden Code. Die Java-VM auch nicht.
Mackie Messer

Ich wusste nicht, dass einige Betriebssysteme so streng sind. In einigen Anwendungen ist dies jedoch durchaus sinnvoll. Ich frage mich jedoch, ob die Ausführung von Java mit Root-Rechten tatsächlich die Sicherheit erhöht ...
Mackie Messer

@ Mackie: Ich denke, es muss verringert werden, aber vielleicht kann es einige Speicherberechtigungen festlegen und dann die effektive UID wieder in ein Benutzerkonto ändern ...?
Tony Delroy

Ja, ich würde erwarten, dass sie über einen fein abgestimmten Mechanismus verfügen, um Berechtigungen für das strenge Sicherheitsmodell zu erteilen.
Mackie Messer

15

Das Synthesis-Betriebssystem hat Ihr Programm im Wesentlichen teilweise in Bezug auf API-Aufrufe ausgewertet und den Betriebssystemcode durch die Ergebnisse ersetzt. Der Hauptvorteil ist, dass viele Fehlerprüfungen weg waren (denn wenn Ihr Programm das Betriebssystem nicht auffordert, etwas Dummes zu tun, muss es nicht überprüft werden).

Ja, das ist ein Beispiel für die Laufzeitoptimierung.


Ich verstehe den Punkt nicht. Wenn Sie sagen, dass ein Systemaufruf vom Betriebssystem verboten wird, erhalten Sie wahrscheinlich eine Fehlermeldung zurück, dass Sie den Code einchecken müssen, nicht wahr? Es scheint mir, dass das Ändern der ausführbaren Datei anstelle der Rückgabe eines Fehlercodes eine Art Überentwicklung darstellt.
Alexandre C.

@Alexandre C .: Auf diese Weise können Sie möglicherweise Nullzeigerprüfungen eliminieren. Oft ist es für den Anrufer trivial offensichtlich, dass ein Argument gültig ist.
MSalters

@Alexandre: Sie können die Forschung unter dem Link lesen. Ich denke, sie haben ziemlich beeindruckende Beschleunigungen bekommen, und das wäre der Punkt: -}
Ira Baxter

2
Bei relativ trivialen und nicht E / A-gebundenen Systemaufrufen sind die Einsparungen erheblich. Wenn Sie beispielsweise ein Deamon für Unix schreiben, gibt es eine Reihe von Boiler-Plate-Systemaufrufen, mit denen Sie stdio trennen, verschiedene Signalhandler einrichten usw. Wenn Sie wissen, dass die Parameter eines Aufrufs Konstanten sind und dass die Die Ergebnisse sind immer die gleichen (z. B. das Schließen von stdin). Ein Großteil des Codes, den Sie im allgemeinen Fall ausführen, ist nicht erforderlich.
Mark Bessey

1
Wenn Sie die Arbeit lesen, enthält Kapitel 8 einige wirklich beeindruckende Zahlen zu nicht trivialen Echtzeit-E / A für die Datenerfassung. Erinnern Sie sich daran, dass dies eine These aus der Mitte der 1980er Jahre ist und die Maschine, auf der er lief, 10 war? Mhz 68000, er war in der Lage, Audiodaten in CD-Qualität (44.000 Samples pro Sekunde) mit einfacher alter Software zu erfassen . Er behauptete, dass Sun Workstations (klassisches Unix) nur etwa 1/5 dieser Rate erreichen könnten. Ich bin ein alter Assembler-Programmierer aus dieser Zeit, und das ist ziemlich spektakulär.
Ira Baxter

9

Vor vielen Jahren habe ich einen Morgen damit verbracht, selbstmodifizierenden Code zu debuggen. Eine Anweisung hat die Zieladresse der folgenden Anweisung geändert, dh ich habe eine Zweigadresse berechnet. Es wurde in Assemblersprache geschrieben und funktionierte perfekt, als ich das Programm einzeln durchging. Aber als ich das Programm ausführte, schlug es fehl. Schließlich stellte ich fest, dass die Maschine 2 Anweisungen aus dem Speicher abrief und (da die Anweisungen im Speicher angeordnet waren) die Anweisung, die ich änderte, bereits abgerufen worden war und die Maschine daher die unveränderte (falsche) Version der Anweisung ausführte. Natürlich habe ich beim Debuggen immer nur eine Anweisung gleichzeitig ausgeführt.

Mein Punkt, selbstmodifizierender Code kann beim Testen / Debuggen extrem unangenehm sein und hat oft versteckte Annahmen über das Verhalten der Maschine (sei es Hardware oder virtuell). Darüber hinaus könnte das System niemals Codepages zwischen den verschiedenen Threads / Prozessen teilen, die auf den (jetzt) ​​Multi-Core-Maschinen ausgeführt werden. Dies beeinträchtigt viele der Vorteile des virtuellen Speichers usw. Es würde auch Zweigoptimierungen auf Hardwareebene ungültig machen.

(Hinweis - Ich habe JIT nicht in die Kategorie des selbstmodifizierenden Codes aufgenommen. JIT übersetzt von einer Darstellung des Codes in eine alternative Darstellung, es ändert den Code nicht.)

Alles in allem ist es nur eine schlechte Idee - wirklich ordentlich, wirklich dunkel, aber wirklich schlecht.

Natürlich - wenn Sie nur 8080 und ~ 512 Byte Speicher haben, müssen Sie möglicherweise auf solche Praktiken zurückgreifen.


1
Ich weiß nicht, gut und schlecht scheinen nicht die richtigen Kategorien zu sein, um darüber nachzudenken. Natürlich sollten Sie wirklich wissen, was Sie tun und warum Sie es tun. Aber der Programmierer, der diesen Code geschrieben hat, wollte wahrscheinlich nicht, dass Sie sehen, was das Programm tat. Natürlich ist es böse, wenn Sie Code so debuggen müssen. Aber dieser Code sollte sehr wahrscheinlich so sein.
Mackie Messer

Moderne x86-CPUs verfügen über eine stärkere SMC-Erkennung als auf dem Papier erforderlich: Beobachten des Abrufs veralteter Anweisungen auf x86 mit selbstmodifizierendem Code . Bei den meisten Nicht-x86-CPUs (wie ARM) stimmt der Anweisungscache nicht mit den Datencaches überein, sodass eine manuelle Leerung / Synchronisierung erforderlich ist, bevor neu gespeicherte Bytes zuverlässig als Anweisungen ausgeführt werden können. community.arm.com/processors/b/blog/posts/… . In beiden Fällen ist die SMC-Leistung auf modernen CPUs schrecklich , es sei denn, Sie ändern sie einmal und führen sie mehrmals aus.
Peter Cordes

7

Aus Sicht eines Betriebssystemkerns führt jede Just In Time Compiler- und Linker-Laufzeit eine Selbständerung des Programmtextes durch. Prominentes Beispiel wäre Googles V8 ECMA Script Interpreter.


5

Ein weiterer Grund für selbstmodifizierenden Code (eigentlich ein "selbstgenerierender" Code) ist die Implementierung eines Just-In-Time-Kompilierungsmechanismus für die Leistung. Beispielsweise kann ein Programm, das einen algebrischen Ausdruck liest und ihn anhand einer Reihe von Eingabeparametern berechnet, den Ausdruck in Maschinencode konvertieren, bevor die Berechnung angegeben wird.


5

Sie kennen die alte Kastanie, dass es keinen logischen Unterschied zwischen Hardware und Software gibt ... man kann auch sagen, dass es keinen logischen Unterschied zwischen Code und Daten gibt.

Was ist selbstmodifizierender Code? Code, der Werte in den Ausführungsstrom einfügt, damit er nicht als Daten, sondern als Befehl interpretiert werden kann. Sicher gibt es den theoretischen Standpunkt in funktionalen Sprachen, dass es wirklich keinen Unterschied gibt. Ich sage zu e kann dies auf einfache Weise in imperativen Sprachen und Compilern / Interpreten tun, ohne die Annahme eines gleichen Status.

Ich beziehe mich auf den praktischen Sinne, dass Daten Programmausführungspfade verändern können (in gewissem Sinne ist dies äußerst offensichtlich). Ich denke an so etwas wie einen Compiler-Compiler, der eine Tabelle (ein Array von Daten) erstellt, die man beim Parsen durchläuft, von Status zu Status wechselt (und auch andere Variablen ändert), genau wie ein Programm von Befehl zu Befehl wechselt , Variablen im Prozess ändern.

Selbst in dem üblichen Fall, in dem ein Compiler Codebereich erstellt und auf einen vollständig separaten Datenbereich (den Heap) verweist, können die Daten dennoch geändert werden, um den Ausführungspfad explizit zu ändern.


4
Kein logischer Unterschied, stimmt. Ich habe jedoch nicht zu viele selbstmodifizierende integrierte Schaltkreise gesehen.
Ira Baxter

@Mitch, IMO, das den Exec-Pfad ändert, hat nichts mit (Selbst-) Änderung von Code zu tun. Außerdem verwechseln Sie Daten mit Informationen. Ich kann Ihren Kommentar zu meiner Antwort in LSE nicht beantworten. B / c Ich bin dort seit Februar für 3 Jahre (1.000 Tage) gesperrt, weil ich in Meta-LSE meine Meinung ausgedrückt habe, dass Amerikaner und Briten kein Englisch besitzen.
Gennady Vanin Геннадий Ванин

4

Ich habe ein Programm implementiert, das Evolution verwendet, um den besten Algorithmus zu erstellen. Es wurde selbstmodifizierender Code verwendet, um den DNA-Entwurf zu modifizieren.


2

Ein Anwendungsfall ist die EICAR-Testdatei , eine legitime ausführbare DOS-COM-Datei zum Testen von Antivirenprogrammen.

X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*

Es muss eine Selbstcodemodifikation verwendet werden, da die ausführbare Datei nur druckbare / typisierbare ASCII-Zeichen im Bereich [21h-60h, 7Bh-7Dh] enthalten darf, wodurch die Anzahl der codierbaren Anweisungen erheblich begrenzt wird

Die Details werden hier erklärt


Es wird auch für das Versenden von Gleitkommaoperationen unter DOS verwendet

Einige Compiler senden CD xxmit xx im Bereich von 0x34-0x3B anstelle von x87-Gleitkommaanweisungen. Da dies CDder Opcode für den intBefehl ist, springt er in den Interrupt 34h-3Bh und emuliert diesen Befehl in der Software, wenn der x87-Coprozessor nicht verfügbar ist. Andernfalls ersetzt der Interrupt-Handler diese 2 Bytes durch, 9B Dxsodass spätere Ausführungen direkt von x87 ohne Emulation verarbeitet werden.

Was ist das Protokoll für die x87-Gleitkommaemulation unter MS-DOS?


1

Der Linux-Kernel verfügt über ladbare Kernel-Module, die genau das tun.

Emacs hat auch diese Fähigkeit und ich benutze sie die ganze Zeit.

Alles, was eine dynamische Plugin-Architektur unterstützt, ändert im Wesentlichen den Code zur Laufzeit.


4
kaum. Eine dynamisch ladbare Bibliothek, die nicht immer resident ist, hat sehr wenig mit selbstmodifizierendem Code zu tun.
Dov

1

Ich führe statistische Analysen für eine ständig aktualisierte Datenbank durch. Mein statistisches Modell wird jedes Mal geschrieben und neu geschrieben, wenn der Code ausgeführt wird, um neue Daten aufzunehmen, die verfügbar werden.


0

Das Szenario, in dem dies verwendet werden kann, ist ein Lernprogramm. In Reaktion auf Benutzereingaben lernt das Programm einen neuen Algorithmus:

  1. Es sucht in der vorhandenen Codebasis nach einem ähnlichen Algorithmus
  2. Befindet sich kein ähnlicher Algorithmus in der Codebasis, fügt das Programm lediglich einen neuen Algorithmus hinzu
  3. Wenn ein ähnlicher Algorithmus vorhanden ist, ändert das Programm (möglicherweise mit Hilfe des Benutzers) den vorhandenen Algorithmus, um sowohl dem alten als auch dem neuen Zweck dienen zu können

Es gibt eine Frage, wie das in Java gemacht werden kann: Welche Möglichkeiten gibt es, um Java-Code selbst zu ändern?


-1

Die beste Version davon sind möglicherweise Lisp-Makros. Im Gegensatz zu C-Makros, die nur ein Präprozessor sind, können Sie mit Lisp jederzeit auf die gesamte Programmiersprache zugreifen. Dies ist ungefähr die mächtigste Funktion in Lisp und existiert in keiner anderen Sprache.

Ich bin kein Experte, aber bringen Sie einen der Lisp-Typen dazu, darüber zu sprechen! Es gibt einen Grund, warum sie sagen, dass Lisp die mächtigste Sprache ist und die klugen Leute nein, dass sie wahrscheinlich Recht haben.


2
Erstellt das tatsächlich selbstmodifizierenden Code oder ist es nur ein leistungsfähigerer Präprozessor (einer, der Funktionen generiert)?
Brendan Long

@Brendan: in der Tat, aber es ist der richtige Weg, um Vorverarbeitung zu tun. Hier erfolgt keine Änderung des Laufzeitcodes.
Alexandre C.
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.