2017-02-01 3 views
0

Je suis en train de créer mon propre JIT et j'ai réussi à exécuter un code d'assemblage très simple (en code machine), mais j'ai du mal à comprendre comment appeler les fonctions de cette manière. Dans Visual Studio, je peux voir les fonctions dans la fenêtre de désassemblage.Fonctions d'appel de l'assembly x86_64

Une autre question connexe est comment puis-je appeler Win32 MessageBox() en code machine?

La question suivante est comment puis-je appeler des fonctions DLL/LIB externes de cette manière?

Y a-t-il aussi des livres ou des tutoriels qui pourraient m'apprendre davantage sur ce sujet? J'ai essayé de le rechercher mais obtenir des résultats comme .NET, JVM et LLVM qui je pense n'est pas vraiment ce que je cherche.

Voici une version simplifiée du code que je travaille sur:

#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; 
} 

Est-il possible d'avoir le code assembleur JIT pour appeler une fonction C++?

Avant ce projet j'ai fait un interpréteur de code octet en C++, mais je n'étais pas vraiment content de la vitesse en le comparant à un programme de test équivalent en C#. C# était environ 25 fois plus rapide. Donc je suis tombé sur quelque chose appelé JIT pour le rendre plus rapide. Donc j'espère que vous pouvez tous voir où je prends ce projet JIT. Et peut-être si possible, faites-le gérer l'interface graphique.

+0

Bien sûr, c'est possible. Exemples Gazillion autour, peut-être regarder un [bonjour monde] (http://stackoverflow.com/a/1032422/547981) pour les débutants. L'assembler à la main ne sera pas amusant. Notez que les appels de fonction sont peu susceptibles d'être votre goulot d'étranglement. – Jester

+1

J'écris généralement un exemple de programme en C/C++, puis j'ai le code de l'assemblage de sortie du compilateur afin d'obtenir les noms de niveau d'assemblage et la séquence d'appel. Dans le cas de Visual Studio 2015, printf fait maintenant partie d'un fichier d'inclusion, ce qui signifie qu'il est effectivement intégré au code C/C++. Une façon de gérer cela est d'avoir un projet qui inclut un fichier C pour le printf, et un fichier d'assemblage pour le reste du projet. Il peut y avoir une option pour importer des bibliothèques spécifiques qui incluent toujours l'ancien style printf. – rcgldr

+0

Eh bien, IIRC, il est possible de plier clang pour compiler la source C en mémoire, puis le mettre par LLVM dans le code machine et l'exécuter, donc en étudiant les sources LLVM vous obtiendrez probablement vos réponses ... dans quelques années ... C'est Je ne comprends pas non plus pourquoi vous êtes gêné par la vitesse du langage interprété, écrivez simplement des parties de performance en C++ et en assembleur, le JIT sera généralement à peine dans le meilleur des cas et sous-performera dans n'importe quel cas de performance. Pour les pièces critiques non performantes, le 25x ne devrait pas non plus avoir d'importance. Bien que cela puisse être un bon exercice pour vous faire comprendre à quel point C++ est cool. :) – Ped7g

Répondre

0

Vous pouvez probablement trouver quelques tutoriels sur l'écriture d'un compilateur/éditeur de liens. Cela peut aider à implémenter/appeler des bibliothèques dynamiques.

Je ne suis pas sûr de ce que vous voulez dire exactement en appelant des fonctions C++. Quoi qu'il en soit, j'ai écrit le programme de démonstration suivant que vous pouvez jeter un coup d'oeil et voir si cela aide du tout.

#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

Comment empêcher C++ de polluer 'rax' entre' setFunc (sub); 'et' __int64 r = callFunc (0, 1); '? IMO ceci n'est pas un exemple stable (bien que cela puisse fonctionner avec un niveau d'optimisation raisonnablement bas et assez de chance). – Ped7g

+0

@ Ped7g Mon point n'est pas que vous pouvez simplement utiliser le code tel quel. Si vous pouvez appeler directement une fonction, pourquoi le feriez-vous de cette façon? Je voulais dire que vous pouvez récupérer l'adresse d'une fonction de cette façon et ensuite vous pouvez faire ce que vous voulez avec, en mettant en place une table de saut, par exemple. – MegaStupidMonkeys

+0

@MegaStupidMonkeys C'est ce qui m'intéressait vraiment. Merci! –