JIT ohne Bibliothek



  • Hiho

    Nachdem ich nun einen einfachen Parser für eine C-Artige Programmiersprache geschrieben habe, möchte ich den generierten Code gernen JITen und "laufen lassen". Der Input sieht z.B. so aus:

    int f(int x, int y)
    {
        return x * y;
    }
    

    Daraus habe ich den folgenden x86-64 Code generiert:

    imul rdi, rsi
    mov rax, rdi
    ret
    

    Und nun möchte ich in meinem C++-Programm den generierten Assembler-Code zur Laufzeit aufrufen. Wie genau geht das? Für asm() muss der Code ja zur Compile-Time schon bekannt sein.

    Grüsse

    PS: Ich weiss, dass das mit LLVM oder so vielleicht viel einfacher wäre, aber es geht mir um den Lerneffekt. Wenn ich alles selber mache, dann lerne ich am meisten. 🙂



  • wäregerneschlauer schrieb:

    Und nun möchte ich in meinem C++-Programm den generierten Assembler-Code zur Laufzeit aufrufen. Wie genau geht das? Für asm() muss der Code ja zur Compile-Time schon bekannt sein.

    Wie das "genau" geht, kann ich dir leider nicht sagen, da ich es noch nicht selbt gemacht habe.
    Allerdings kann ich dir ein paar Hinweise geben, wie man dabei vorgehen müsste (Details musst du dann selbst recherchieren):

    1. Du schaust dir die Aufrufkonventionen für deine Plattform an, also wie eine Funktion in Maschinencode strukturiert sein muss:
    z.B. welche Register gesichert/wiederhergestellt werden müssen, wo die Funktionsparamteter liegen (Register/Stack), wie Rückbagewerte
    abgelegt werden, und was es sonst noch zu beachten gibt, wenn man eine ABI-konforme Funktion in Maschinencode erzeugen will.

    2. Du holst dir einen Speicherbereich vom Betriebssystem, den du beschreiben darfst, und für den die Ausführung von Maschinencode erlaubt ist
    (wichtig wegen DEP auf moderenen Systemen! Unter Windows müsste das mit VirtualAlloc/Ex und einem Speicherschutz-Flag wie z.B.
    PAGE_EXECUTE_READWRITE gehen).

    3. Du schreibst eine ABI-konforme Funktion in Maschinencode in diesen Speicherbereich, also die Bytes des Maschinencode-Äquivalents
    deines erzeugten Assembler-Codes (Das hier scheint eine brauchbare Referenz zu sein). Das stelle ich mir als recht mühsam vor, da man
    dafür quasi zumindest einen Teil eines Assemblers selbst implemetieren muss. Ich denke eine passende Bibliothek könnte die Arbeit
    erleichtern (oberflächliche Suche hat dieses Projekt zutage gefördert, "in-memory assembler" klingt vielversprechend).

    4. Nun müsstest du die generierte Funktion eigentlich aufrufen können, wenn ich nicht noch irgendwas übersehen habe: Z.B., indem du
    einen Funktionspointer mit den deiner erzeugten Funktion enstsprechenden Parametern und Rückgabewert auf den Beginn des Speicherbereichs
    mit dem Maschinencode setzt und diesen dann aufrufst.

    Hoffe das führt dich in die richtige Richtung, lass mich wissen ob du damit Erfolg hast,
    Finnegan


Log in to reply