2017-02-01 5 views
0

Ich versuche, meine eigene JIT zu erstellen und bis jetzt geschafft, sehr einfachen Assembly-Code (in Maschinen-Code) zu laufen, aber Probleme herauszufinden, wie man Funktionen auf diese Weise aufrufen. In Visual Studio kann ich Funktionen im Disassemblierungsfenster sehen.Call-Funktionen von x86_64 Assembly

Eine andere verwandte Frage ist, wie ich Win32 MessageBox() in Maschinencode aufrufen?

Nächste Frage ist, wie ich externe DLL/LIB-Funktionen auf diese Weise aufrufen?

Gibt es auch Bücher oder Tutorials, die mich in diesem Thema weiterbringen könnten? Ich habe versucht, danach zu suchen, aber Ergebnisse wie .NET, JVM und LLVM zu bekommen, die ich glaube nicht wirklich, was ich suche.

Hier ist eine vereinfachte Version des Codes, arbeite ich an:

#include <iostream> 
#include <Windows.h> 

int main(int argc, char* argv[]) 
{ 
    // b8 03 00 00 00 83 c0 02 c3 
    unsigned char code[] = { 
     0xb8,     // mov eax, 3 
     0x03, 0x00, 0x00, 0x00, // 3 (32 bit) 
     0x83,     // add eax, 2 // 0x83 = add, 
     0xc0,     // ModR/M with immediate 8 bit value 
     0x02,     // 2 (8 bit) 
     0xc3     // ret 
    }; 

    void* mem = VirtualAlloc(0, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE); 

    memcpy(mem, code, sizeof(code)); 

    DWORD old; 
    VirtualProtect(mem, sizeof(mem), PAGE_EXECUTE_READ, &old); 

    int(*func)() = reinterpret_cast<int(*)()>(mem); 

    printf("Number is %d\n", func()); 

    VirtualFree(mem, 0, MEM_RELEASE); 

    return 0; 
} 

Ist es möglich, den JIT-Assembler-Code zu haben, eine C++ Funktion aufzurufen?

Vor diesem Projekt habe ich einen Byte-Code-Interpreter in C++ erstellt, aber ich war nicht wirklich glücklich mit der Geschwindigkeit beim Vergleich mit äquivalenten Testprogramm in C#. C# war ungefähr 25x schneller. Also bin ich auf etwas namens JIT gestoßen, um es schneller zu machen. Ich hoffe, Sie alle können sehen, wo ich dieses JIT-Projekt nehme. Und wenn möglich, sollte es mit der GUI umgehen.

+0

Natürlich ist es möglich. Gazillion Beispiele herum, vielleicht ein Blick auf eine [Hallo Welt] (http://Stackoverflow.com/a/1032422/547981) für den Anfang. Die Montage per Hand wird jedoch nicht viel Spaß machen. Beachten Sie, dass Funktionsaufrufe wahrscheinlich nicht Ihr Engpass sind. – Jester

+1

Ich schreibe in der Regel ein Beispielprogramm in C/C++, dann haben Sie den Compiler Ausgabe Assembly-Code, um die Assembly-Level-Namen und Aufrufreihenfolge zu bekommen. Im Fall von Visual Studio 2015 ist printf jetzt Teil einer Include-Datei, was bedeutet, dass es effektiv mit C/C++ - Code verknüpft wird. Eine Möglichkeit, dies zu umgehen, ist ein Projekt, das eine C-Datei für printf und eine Assemblydatei für den Rest des Projekts enthält. Möglicherweise gibt es eine Option, um bestimmte Bibliotheken zu importieren, die noch den alten Stil printf enthalten. – rcgldr

+0

Nun, IIRC, es ist möglich, clang zu kompilieren, um C-Quelle in Speicher zu kompilieren, und dann JIT es von LLVM in Maschinencode und führen Sie es aus, so LLVM-Quellen erhalten Sie wahrscheinlich Ihre Antworten .. in wenigen Jahren ... Es ist Auch ist mir unklar, warum Sie von der Sprachgeschwindigkeit geärgert werden, schreiben Sie einfach Performance-Teile in C++ und Assembly, der JIT wird im besten Fall im besten Fall kaum gleichwertig sein, und in jedem richtig gestimmten Performance-Fall nicht. Für nicht leistungskritische Teile sollte der 25x auch keine Rolle spielen. Obwohl dies eine gute Übung sein kann, um herauszufinden, wie cool C++ ist. :) – Ped7g

Antwort

0

Sie können wahrscheinlich einige Tutorials über das Schreiben eines Compilers/Linkers finden. Es kann helfen, dynamische Bibliotheken zu implementieren/aufzurufen.

Ich bin nicht sicher, was Sie genau meinen, indem Sie C++ - Funktionen aufrufen. Jedenfalls habe ich das folgende Demo-Programm geschrieben, das man sich ansehen kann und ob es überhaupt hilft.

#include <Windows.h> 
#include <iostream> 


using namespace std; 

__int64 sub(__int64 a, __int64 b) 
{ 
    return a - b; 
} 

int main(int argc, char **argv) 
{ 
    char code[] = 
    { 
     0x48, 0x89, 0xC8,   // mov rax, rcx 
     0xC3,      // ret 

     0x48, 0x83, 0xEC, 0x20,  // sub rsp, 0x20 
     0xFF, 0xD0,     // call rax 
     0x48, 0x83, 0xC4, 0x20,  // add rsp, 0x20 
     0xC3      // ret 
    }; 


    char *mem = static_cast<char *>(VirtualAlloc(0, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE)); 

    MoveMemory(mem, code, sizeof(code)); 

    auto setFunc = reinterpret_cast<void *(*)(void *)>(mem); 
    auto callFunc = reinterpret_cast<__int64 (*)(__int64, __int64)>(mem + 4); 

    setFunc(sub); 
    __int64 r = callFunc(0, 1); 
    cout << "r = " << r << endl; 

    VirtualFree(mem, 0, MEM_RELEASE); 


    cin.ignore(); 
    return 0; 
} 
+0

Wie verhindert man, dass C++ 'rax' zwischen' setFunc (sub); 'und' __int64 r = callFunc (0, 1); '? IMO, das ist kein stabiles Beispiel (obwohl es mit einem relativ niedrigen Optimierungslevel und genug Glück funktionieren kann). – Ped7g

+0

@ Ped7g Mein Punkt ist nicht, dass Sie einfach den Code verwenden können, wie es ist. Wenn du eine Funktion direkt aufrufen kannst, warum würdest du es trotzdem tun? Ich wollte damit sagen, dass Sie die Adresse einer Funktion auf diese Weise abrufen können und dann können Sie damit machen, was Sie wollen, indem Sie beispielsweise eine Sprungtabelle einrichten. – MegaStupidMonkeys

+0

@MegaStupidMonkeys Das hat mich wirklich interessiert. Danke! –

Verwandte Themen