Tengo la intención de escribir mi propio intérprete JIT como parte de un curso sobre máquinas virtuales. Tengo mucho conocimiento sobre lenguajes de alto nivel, compiladores e intérpretes, pero tengo poco o ningún conocimiento sobre el ensamblaje x86 (o C para el caso).Escribo mi propio intérprete JIT. ¿Cómo ejecuto las instrucciones generadas?
En realidad, no sé cómo funciona un JIT, pero aquí está mi opinión: Lea en el programa en algún idioma intermedio. Compila eso con las instrucciones x86. Asegúrese de que la última instrucción regrese a algún lugar sano en el código de VM. Guarde las instrucciones en algún lugar de la memoria. Haz un salto incondicional a la primera instrucción. Voila!
Entonces, con esto en mente, tengo el siguiente pequeño programa C:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
int main() {
int *m = malloc(sizeof(int));
*m = 0x90; // NOP instruction code
asm("jmp *%0"
: /* outputs: */ /* none */
: /* inputs: */ "d" (m)
: /* clobbers: */ "eax");
return 42;
}
bien, así que mi intención es que este programa para almacenar la instrucción NOP en algún lugar de la memoria, salto a esa ubicación y luego probablemente se cuelgue (porque no configuré ninguna forma para que el programa regrese a main).
Pregunta: ¿Estoy en el camino correcto?
Pregunta: ¿Podría mostrarme un programa modificado que logra encontrar su camino de regreso a algún lugar dentro de main?
Pregunta: Otros problemas que debería tener cuidado?
PD: Mi objetivo es obtener comprensión, no necesariamente hacer todo de la manera correcta.
Gracias por todo el comentario. El siguiente código parece ser el lugar para iniciar y funciona en mi máquina Linux:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
unsigned char *m;
int main() {
unsigned int pagesize = getpagesize();
printf("pagesize: %u\n", pagesize);
m = malloc(1023+pagesize+1);
if(m==NULL) return(1);
printf("%p\n", m);
m = (unsigned char *)(((long)m + pagesize-1) & ~(pagesize-1));
printf("%p\n", m);
if(mprotect(m, 1024, PROT_READ|PROT_EXEC|PROT_WRITE)) {
printf("mprotect fail...\n");
return 0;
}
m[0] = 0xc9; //leave
m[1] = 0xc3; //ret
m[2] = 0x90; //nop
printf("%p\n", m);
asm("jmp *%0"
: /* outputs: */ /* none */
: /* inputs: */ "d" (m)
: /* clobbers: */ "ebx");
return 21;
}
Otra opción es simplemente interpretar las instrucciones o código intermedio w/o ejecutar cualquier cosa directamente. –
@Alex: esa es otra opción para implementar un idioma, pero por definición no es un JIT. –