Je suis en train d'implémenter un compilateur JIT simpliste dans une machine virtuelle que j'écris pour m'amuser (surtout pour en apprendre plus sur la conception de langage) et j'obtiens un comportement étrange Quelqu'un peut me dire pourquoi.ebp + 6 au lieu de +8 dans un compilateur JIT
D'abord, je définir un JIT "prototype" à la fois pour C et C++:
#ifdef __cplusplus
typedef void* (*_JIT_METHOD) (...);
#else
typedef (*_JIT_METHOD)();
#endif
J'ai une fonction compile()
qui compile des choses dans l'ASM et le coller quelque part en mémoire:
void* compile (void* something)
{
// grab some memory
unsigned char* buffer = (unsigned char*) malloc (1024);
// xor eax, eax
// inc eax
// inc eax
// inc eax
// ret -> eax should be 3
/* WORKS!
buffer[0] = 0x67;
buffer[1] = 0x31;
buffer[2] = 0xC0;
buffer[3] = 0x67;
buffer[4] = 0x40;
buffer[5] = 0x67;
buffer[6] = 0x40;
buffer[7] = 0x67;
buffer[8] = 0x40;
buffer[9] = 0xC3; */
// xor eax, eax
// mov eax, 9
// ret 4 -> eax should be 9
/* WORKS!
buffer[0] = 0x67;
buffer[1] = 0x31;
buffer[2] = 0xC0;
buffer[3] = 0x67;
buffer[4] = 0xB8;
buffer[5] = 0x09;
buffer[6] = 0x00;
buffer[7] = 0x00;
buffer[8] = 0x00;
buffer[9] = 0xC3; */
// push ebp
// mov ebp, esp
// mov eax, [ebp + 6] ; wtf? shouldn't this be [ebp + 8]!?
// mov esp, ebp
// pop ebp
// ret -> eax should be the first value sent to the function
/* WORKS! */
buffer[0] = 0x66;
buffer[1] = 0x55;
buffer[2] = 0x66;
buffer[3] = 0x89;
buffer[4] = 0xE5;
buffer[5] = 0x66;
buffer[6] = 0x66;
buffer[7] = 0x8B;
buffer[8] = 0x45;
buffer[9] = 0x06;
buffer[10] = 0x66;
buffer[11] = 0x89;
buffer[12] = 0xEC;
buffer[13] = 0x66;
buffer[14] = 0x5D;
buffer[15] = 0xC3;
// mov eax, 5
// add eax, ecx
// ret -> eax should be 50
/* WORKS!
buffer[0] = 0x67;
buffer[1] = 0xB8;
buffer[2] = 0x05;
buffer[3] = 0x00;
buffer[4] = 0x00;
buffer[5] = 0x00;
buffer[6] = 0x66;
buffer[7] = 0x01;
buffer[8] = 0xC8;
buffer[9] = 0xC3; */
return buffer;
}
Et enfin j'ai le morceau principal du programme:
int main (int argc, char **args)
{
DWORD oldProtect = (DWORD) NULL;
int i = 667, j = 1, k = 5, l = 0;
// generate some arbitrary function
_JIT_METHOD someFunc = (_JIT_METHOD) compile(NULL);
// windows only
#if defined _WIN64 || defined _WIN32
// set memory permissions and flush CPU code cache
VirtualProtect(someFunc,1024,PAGE_EXECUTE_READWRITE, &oldProtect);
FlushInstructionCache(GetCurrentProcess(), someFunc, 1024);
#endif
// this asm just for some debugging/testing purposes
__asm mov ecx, i
// run compiled function (from wherever *someFunc is pointing to)
l = (int)someFunc(i, k);
// did it work?
printf("result: %d", l);
free (someFunc);
_getch();
return 0;
}
Comme vous c un voir, la fonction compile()
a un couple de tests que j'ai couru pour m'assurer que les résultats attendus, et à peu près tout fonctionne mais j'ai une question ...
Sur la plupart des tutoriels ou des ressources de documentation, pour obtenir la première valeur d'une fonction passée (dans le cas d'ints) vous faites [ebp+8]
, le second [ebp+12]
et ainsi de suite. Pour une raison quelconque, je dois faire [ebp+6]
puis [ebp+10]
et ainsi de suite. Quelqu'un pourrait-il me dire pourquoi?
Merci, donc que dois-je utiliser à la place de '0x66' et' 0x67'. De plus, le bytecode ASM a été assemblé avec TASM (probablement en mode 16 bits). –
Je ne suis pas familier avec TASM ... mais vous devez assembler en mode 32 bits. (Bien que, dans ces exemples simples, probablement tout ce que vous devez faire est de supprimer complètement les octets '0x66' et' 0x67': ils sont juste des octets de préfixe qui modifient la signification de l'opcode suivant, par exemple 'push ebp; mov ebp, esp 'est juste' 0x55', '0x89',' 0xE5' en mode 32 bits. –
Génial, merci pour l'aide, je l'apprécie! –