#include "jitc_x86.h" #include #include #include #define PARAM_INT 1 #define PARAM_FLOAT 2 #define PARAM_REG 3 #define PARAM_dispREG 4 #define PARAM_DISP32 5 #define PARAM_LABEL 6 #define PARAM_NONE 666 typedef struct { int id; int i; double f; int reg; int disp; char label[256]; } IParam; struct { char *name; int reg; } RegsName[] = { {"eax",EAX}, {"ebx",EBX}, {"ecx",ECX}, {"edx",EDX}, {"edi",EDI}, {"esi",ESI}, {"ebp",EBP}, {"esp",ESP}, {"st0",0}, {"st1",1}, {"st2",2}, {"st3",3}, {"st4",4}, {"st5",5}, {"st6",6}, {"st7",7}, {"mm0",0}, {"mm1",1}, {"mm2",2}, {"mm3",3}, {"mm4",4}, {"mm5",5}, {"mm6",6}, {"mm7",7}, {NULL,0} }; void modrm(JitcX86Env *jitc, int opcode, IParam *iparam) { int dest = 0; int src = 1; int direction = 0x0; unsigned int byte = 666; unsigned int int32 = 0; unsigned int need32 = 0; if ((iparam[0].id == PARAM_REG) && (iparam[1].id != PARAM_REG)) { dest = 1; src = 0; direction = 0x02; } if (iparam[src].id != PARAM_REG) { fprintf(stderr, "JITC_x86: Invalid Instruction Parameters: %d %d.\n", iparam[0].id, iparam[1].id); exit(1); } if (iparam[dest].id == PARAM_REG) { byte = ((int)JITC_MOD_REG_REG << 6) | (iparam[src].reg << 3) | (iparam[0].reg); } else if (iparam[dest].id == PARAM_dispREG) { if (iparam[dest].disp == 0) byte = ((int)JITC_MOD_pREG_REG << 6) | (iparam[src].reg << 3) | (iparam[dest].reg); } else if (iparam[dest].id == PARAM_DISP32) { byte = ((int)JITC_MOD_pREG_REG << 6) | (iparam[src].reg << 3) | JITC_RM_DISP32; need32 = 1; int32 = iparam[dest].disp; } if (byte == 666) { fprintf(stderr, "JITC_x86: Invalid Instruction Parameters: %d %d.\n", iparam[0].id, iparam[1].id); exit(1); } else { if (opcode < 0x100) JITC_ADD_UCHAR(jitc, opcode + direction); else { JITC_ADD_UCHAR(jitc, (opcode>>8)&0xff); JITC_ADD_UCHAR(jitc, (opcode&0xff)/* + direction*/); } JITC_ADD_UCHAR(jitc, byte); if (need32) JITC_ADD_UINT(jitc, int32); } } static void imul_like_modrm_1param(JitcX86Env *jitc, int opcode, int digit, IParam *iparam) { if (iparam[0].id == PARAM_REG) { JITC_ADD_UCHAR(jitc, opcode); JITC_MODRM(jitc, 0x03, digit, iparam[0].reg); return; } if (iparam[0].id == PARAM_dispREG) { JITC_ADD_UCHAR(jitc, opcode); if (iparam[0].disp == 0) { JITC_MODRM(jitc, 0x00, digit, iparam[0].reg); } else if ((iparam[0].disp & 0xff) == iparam[0].disp) { JITC_MODRM(jitc, 0x01, digit, iparam[0].reg); JITC_ADD_UCHAR(jitc, iparam[0].disp); } else { JITC_MODRM(jitc, 0x02, digit, iparam[0].reg); JITC_ADD_UINT(jitc, iparam[0].disp); } return; } if (iparam[0].id == PARAM_DISP32) { JITC_ADD_UCHAR(jitc, opcode); JITC_MODRM(jitc, JITC_MOD_pREG_REG, digit, JITC_RM_DISP32); JITC_ADD_UINT(jitc, iparam[0].disp); return; } } /* 1 byte encoded opcode including register... imm32 parameter */ #define INSTR_1bReg_IMM32(opcode,dest,src) { \ JITC_ADD_UCHAR(jitc, opcode + iparam[dest].reg); \ JITC_ADD_UINT (jitc, (int)iparam[src].i); } typedef struct { char *name; int opcode; int opcode_reg_int; int digit_reg_int; int opcode_eax_int; } AddLikeInstr; static AddLikeInstr addLike[] = { { "add", 0x01, 0x81, 0x00, 0x05 }, { "and", 0x21, 0x81, 0x04, 0x25 }, { "or", 0x0B, 0x81, 0x01, 0x0D }, { "cmp", 0x39, 0x81, 0x07, 0x3D }, { "imul", 0x0FAF, 0x69, 0x00, 0x10000 }, { "sub", 0x29, 0x81, 0x05, 0X2D }, { NULL, -1, -1, -1, -1 } }; int checkAddLike(JitcX86Env *jitc, char *op, IParam *iparam, int nbParams) { int i; for (i=0;addLike[i].name;++i) { if (strcmp(op,addLike[i].name) == 0) { if ((iparam[0].id == PARAM_REG) && (iparam[1].id == PARAM_INT)) { if ((iparam[0].reg == EAX) && (addLike[i].opcode_eax_int != 0x10000)) { JITC_ADD_UCHAR(jitc, addLike[i].opcode_eax_int); JITC_ADD_UINT(jitc, iparam[1].i); return 1; } else { JITC_ADD_UCHAR(jitc, addLike[i].opcode_reg_int); JITC_MODRM(jitc, 0x03, addLike[i].digit_reg_int, iparam[0].reg); JITC_ADD_UINT(jitc, iparam[1].i); return 1; } } else if ((iparam[0].id == PARAM_dispREG) && (iparam[1].id == PARAM_INT)) { JITC_ADD_UCHAR(jitc, addLike[i].opcode_reg_int); if ((iparam[0].disp & 0xff) == iparam[0].disp) { JITC_MODRM(jitc, 0x01, addLike[i].digit_reg_int, iparam[0].reg); JITC_ADD_UCHAR(jitc, iparam[0].disp); } else { JITC_MODRM(jitc, 0x00, addLike[i].digit_reg_int, iparam[0].reg); JITC_ADD_UINT(jitc, iparam[0].disp); } JITC_ADD_UINT(jitc, iparam[1].i); return 1; } else if ((iparam[0].id == PARAM_DISP32) && (iparam[1].id == PARAM_INT)) { JITC_ADD_UCHAR(jitc, addLike[i].opcode_reg_int); JITC_MODRM(jitc, 0x00, addLike[i].digit_reg_int, 0x05); JITC_ADD_UINT(jitc, iparam[0].disp); JITC_ADD_UINT(jitc, iparam[1].i); return 1; } else { modrm(jitc, addLike[i].opcode, iparam); return 1; } } } return 0; } /** * Check all kind of known instruction... perform special optimisations.. */ static void jitc_add_op(JitcX86Env *jitc, char *op, IParam *iparam, int nbParams) { /* MOV */ if (strcmp(op,"mov") == 0) { if ((iparam[0].id == PARAM_REG) && (iparam[1].id == PARAM_INT)) { INSTR_1bReg_IMM32(0xb8,0,1); } else if ((iparam[0].id == PARAM_DISP32) && (iparam[1].id == PARAM_INT)) { JITC_ADD_UCHAR(jitc, 0xc7); JITC_MODRM(jitc, 0x00, 0x00, 0x05); JITC_ADD_UINT(jitc, iparam[0].disp); JITC_ADD_UINT(jitc, iparam[1].i); } else modrm(jitc, 0x89, iparam); return; } #define IMUL_LIKE(_OP,_opcode,_digit)\ if (strcmp(op, _OP) == 0) { \ if (nbParams == 1) { \ imul_like_modrm_1param(jitc, _opcode, _digit, iparam); \ return; }} #define SHIFT_LIKE(_name,_op1,_op2,_digit) \ if (strcmp(op, _name) == 0) { \ if (iparam[1].id == PARAM_INT) { \ if (iparam[1].i == 1) \ imul_like_modrm_1param(jitc, _op1, _digit, iparam); \ else { \ imul_like_modrm_1param(jitc, _op2, _digit, iparam); \ JITC_ADD_UCHAR(jitc, iparam[1].i); \ } \ return; \ } \ } #define POP_LIKE(_OP,_opcode) \ if (strcmp(op, _OP) == 0) { \ if (iparam[0].id == PARAM_REG) { \ JITC_ADD_UCHAR(jitc, _opcode + iparam[0].reg); \ return; } } IMUL_LIKE("neg", 0xf7, 0x03); IMUL_LIKE("imul", 0xf7, 0x05); IMUL_LIKE("idiv", 0xf7, 0x07); POP_LIKE("pop", 0x58); POP_LIKE("push", 0x50); SHIFT_LIKE("sal", 0xd1, 0xc1, 0x04); SHIFT_LIKE("sar", 0xd1, 0xc1, 0x07); SHIFT_LIKE("shl", 0xd1, 0xc1, 0x04); SHIFT_LIKE("shr", 0xd1, 0xc1, 0x05); /* INC */ if (strcmp(op, "inc") == 0) { if (iparam[0].id == PARAM_REG) { JITC_ADD_UCHAR(jitc, 0x40 + iparam[0].reg); return; } imul_like_modrm_1param(jitc, 0xff, 0x00, iparam); return; } /* DEC */ if (strcmp(op, "dec") == 0) { if (iparam[0].id == PARAM_REG) { JITC_ADD_UCHAR(jitc, 0x48 + iparam[0].reg); return; } imul_like_modrm_1param(jitc, 0xff, 0x01, iparam); return; } if (strcmp(op, "call") == 0) { if (iparam[0].id == PARAM_LABEL) { jitc_add_used_label(jitc,iparam[0].label,jitc->used+1); JITC_CALL(jitc,0); return; } if (iparam[0].id == PARAM_INT) { JITC_CALL(jitc,iparam[0].i); return; } if (iparam[0].id == PARAM_dispREG) { JITC_ADD_UCHAR(jitc,0xff); JITC_ADD_UCHAR(jitc,0xd0+iparam[0].reg); return; } } #define MONOBYTE_INSTR(_OP,_opcode) \ if (strcmp(op, _OP) == 0) { \ JITC_ADD_UCHAR(jitc, _opcode); \ return; } MONOBYTE_INSTR("ret", 0xc3); MONOBYTE_INSTR("leave", 0xc9); MONOBYTE_INSTR("cdq", 0x99); /* JNE */ if (strcmp(op, "jne") == 0) { if (iparam[0].id == PARAM_LABEL) { JITC_JUMP_COND_LABEL(jitc,COND_NOT_EQUAL,iparam[0].label); return; } if (iparam[0].id == PARAM_INT) { JITC_JUMP_COND(jitc,COND_NOT_EQUAL,iparam[0].i); return; } } /* JE */ if (strcmp(op, "je") == 0) { if (iparam[0].id == PARAM_LABEL) { JITC_JUMP_COND_LABEL(jitc,COND_EQUAL,iparam[0].label); return; } if (iparam[0].id == PARAM_INT) { JITC_JUMP_COND(jitc,COND_EQUAL,iparam[0].i); return; } } /* ADD LIKE */ if (checkAddLike(jitc, op, iparam, nbParams)) return; /* BSWAP : 0F C8+rd */ fprintf(stderr, "JITC_x86: Invalid Operation '%s'\n", op); exit(1); } /** * Adds a new instruction to the just in time compiled function */ void jitc_add(JitcX86Env *jitc, const char *_instr, ...) { char instr[256]; char *op; char *sparam[16]; int nbParam=0; int i; IParam iparam[16]; va_list ap; strcpy(instr,_instr); #ifdef DISPLAY_GENCODE printf("|"); #endif op = strtok(instr, " ,"); if (!op) return; /* decoupage en tokens */ while ((sparam[nbParam] = strtok(NULL, " ,")) != NULL) if (strlen(sparam[nbParam])>0) nbParam++; /* Reconnaissance des parametres */ va_start(ap, _instr); for (i=0;i 0) { if (strcmp(regname, RegsName[r].name) == 0) { iparam[i].id = PARAM_dispREG; iparam[i].reg = RegsName[r].reg; iparam[i].disp = va_arg(ap, int); } } if (sscanf(sparam[i], "[%3s]", regname) > 0) { if (strcmp(regname, RegsName[r].name) == 0) { iparam[i].id = PARAM_dispREG; iparam[i].reg = RegsName[r].reg; iparam[i].disp = 0; } } } } if (iparam[i].id == PARAM_NONE) { fprintf(stderr, "JITC_x86: Unrecognized parameter '%s'\n", sparam[i]); exit(1); } } va_end(ap); jitc_add_op(jitc, op, &(iparam[0]), nbParam); #ifdef DISPLAY_GENCODE printf(" ;;; %s", op); for (i=0;i_memory = (unsigned char*)malloc(memory_size); jitc->used = 0; jitc->memory = (unsigned char*)((int)jitc->_memory + (32-((int)jitc->_memory)%32)%32); jitc->nbUsedLabel = 0; jitc->nbKnownLabel = 0; jitc->usedLabel = (LabelAddr*)malloc(sizeof(LabelAddr) * JITC_MAXLABEL); jitc->knownLabel = (LabelAddr*)malloc(sizeof(LabelAddr) * JITC_MAXLABEL); return jitc; } void jitc_x86_delete(JitcX86Env *jitc) { free(jitc->usedLabel); free(jitc->knownLabel); free(jitc->_memory); free(jitc); } JitcFunc jitc_prepare_func(JitcX86Env *jitc) { JitcFunc ptr = 0; jitc->used = (32 - jitc->used%32)%32; ptr = (JitcFunc)&(jitc->memory[jitc->used]); #ifdef DISPLAY_GENCODE printf("\n------------------------------------------\n"); printf("-- Function Intro --\n"); printf("------------------------------------------\n"); #endif /* save the state */ jitc_add(jitc,"push ebp"); jitc_add(jitc,"mov ebp, esp"); jitc_add(jitc,"sub esp, $d", 8); JITC_PUSH_ALL(jitc); #ifdef DISPLAY_GENCODE printf("\n------------------------------------------\n"); #endif return ptr; } void jitc_validate_func(JitcX86Env *jitc) { #ifdef DISPLAY_GENCODE printf("\n------------------------------------------\n"); printf("-- Function Outro --\n"); printf("------------------------------------------\n"); #endif /* restore the state */ JITC_POP_ALL(jitc); jitc_add(jitc, "leave"); jitc_add(jitc, "ret"); jitc_resolve_labels(jitc); #ifdef DISPLAY_GENCODE printf("\n------------------------------------------\n"); #endif } void jitc_add_used_label(JitcX86Env *jitc, char *label, int where) { strncpy(jitc->usedLabel[jitc->nbUsedLabel].label, label, JITC_LABEL_SIZE); jitc->usedLabel[jitc->nbUsedLabel].address = where; jitc->nbUsedLabel++; } void jitc_add_known_label(JitcX86Env *jitc, char *label, int where) { #ifdef DISPLAY_GENCODE printf("%s:\n", label); #endif strncpy(jitc->knownLabel[jitc->nbKnownLabel].label, label, JITC_LABEL_SIZE); jitc->knownLabel[jitc->nbKnownLabel].address = where; jitc->nbKnownLabel++; } void jitc_resolve_labels(JitcX86Env *jitc) { int i,j; for (i=jitc->nbUsedLabel;i-->0;) { LabelAddr used = jitc->usedLabel[i]; for (j=jitc->nbKnownLabel;j-->0;) { LabelAddr known = jitc->knownLabel[j]; if (strcmp(known.label, used.label) == 0) { int *offset = (int*)&(jitc->memory[used.address]); *offset = known.address - used.address - 4; /* always using long offset */ break; } } } jitc->nbUsedLabel = jitc->nbKnownLabel = 0; }