1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4
5 This file is part of Quake III Arena source code.
6
7 Quake III Arena source code is free software; you can redistribute it
8 and/or modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the License,
10 or (at your option) any later version.
11
12 Quake III Arena source code is distributed in the hope that it will be
13 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Quake III Arena source code; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 ===========================================================================
21 */
22 // vm_x86.c -- load time compiler and execution environment for x86
23
24 #include "vm_local.h"
25
26 #ifdef _WIN32
27 #include <windows.h>
28 #else
29 #ifdef __FreeBSD__
30 #include <sys/types.h>
31 #endif
32
33 #include <sys/mman.h> // for PROT_ stuff
34
35 /* need this on NX enabled systems (i386 with PAE kernel or
36 * noexec32=on x86_64) */
37 #define VM_X86_MMAP
38
39 // workaround for systems that use the old MAP_ANON macro
40 #ifndef MAP_ANONYMOUS
41 #define MAP_ANONYMOUS MAP_ANON
42 #endif
43 #endif
44
45 static void VM_Destroy_Compiled(vm_t* self);
46
47 /*
48
49 eax scratch
50 ebx/bl opStack offset
51 ecx scratch (required for shifts)
52 edx scratch (required for divisions)
53 esi program stack
54 edi opStack base
55 x86_64:
56 r8 vm->instructionPointers
57 r9 vm->dataBase
58
59 */
60
61 #define VMFREE_BUFFERS() do {Z_Free(buf); Z_Free(jused);} while(0)
62 static byte *buf = NULL;
63 static byte *jused = NULL;
64 static int jusedSize = 0;
65 static int compiledOfs = 0;
66 static byte *code = NULL;
67 static int pc = 0;
68
69 #define FTOL_PTR
70
71 static int instruction, pass;
72 static int lastConst = 0;
73 static int oc0, oc1, pop0, pop1;
74 static int jlabel;
75
76 typedef enum
77 {
78 LAST_COMMAND_NONE = 0,
79 LAST_COMMAND_MOV_STACK_EAX,
80 LAST_COMMAND_SUB_BL_1,
81 LAST_COMMAND_SUB_BL_2,
82 } ELastCommand;
83
84 typedef enum
85 {
86 VM_JMP_VIOLATION = 0,
87 VM_BLOCK_COPY = 1
88 } ESysCallType;
89
90 static ELastCommand LastCommand;
91
iss8(int32_t v)92 static int iss8(int32_t v)
93 {
94 return (SCHAR_MIN <= v && v <= SCHAR_MAX);
95 }
96
97 #if 0
98 static int isu8(uint32_t v)
99 {
100 return (v <= UCHAR_MAX);
101 }
102 #endif
103
NextConstant4(void)104 static int NextConstant4(void)
105 {
106 return ((unsigned int)code[pc] | ((unsigned int)code[pc+1]<<8) | ((unsigned int)code[pc+2]<<16) | ((unsigned int)code[pc+3]<<24));
107 }
108
Constant4(void)109 static int Constant4( void ) {
110 int v;
111
112 v = NextConstant4();
113 pc += 4;
114 return v;
115 }
116
Constant1(void)117 static int Constant1( void ) {
118 int v;
119
120 v = code[pc];
121 pc += 1;
122 return v;
123 }
124
Emit1(int v)125 static void Emit1( int v )
126 {
127 buf[ compiledOfs ] = v;
128 compiledOfs++;
129
130 LastCommand = LAST_COMMAND_NONE;
131 }
132
Emit2(int v)133 static void Emit2(int v)
134 {
135 Emit1(v & 255);
136 Emit1((v >> 8) & 255);
137 }
138
139
Emit4(int v)140 static void Emit4(int v)
141 {
142 Emit1(v & 0xFF);
143 Emit1((v >> 8) & 0xFF);
144 Emit1((v >> 16) & 0xFF);
145 Emit1((v >> 24) & 0xFF);
146 }
147
EmitPtr(void * ptr)148 static void EmitPtr(void *ptr)
149 {
150 intptr_t v = (intptr_t) ptr;
151
152 Emit4(v);
153 #if idx64
154 Emit1((v >> 32) & 0xFF);
155 Emit1((v >> 40) & 0xFF);
156 Emit1((v >> 48) & 0xFF);
157 Emit1((v >> 56) & 0xFF);
158 #endif
159 }
160
Hex(int c)161 static int Hex( int c ) {
162 if ( c >= 'a' && c <= 'f' ) {
163 return 10 + c - 'a';
164 }
165 if ( c >= 'A' && c <= 'F' ) {
166 return 10 + c - 'A';
167 }
168 if ( c >= '0' && c <= '9' ) {
169 return c - '0';
170 }
171
172 VMFREE_BUFFERS();
173 Com_Error( ERR_DROP, "Hex: bad char '%c'", c );
174
175 return 0;
176 }
EmitString(const char * string)177 static void EmitString( const char *string ) {
178 int c1, c2;
179 int v;
180
181 while ( 1 ) {
182 c1 = string[0];
183 c2 = string[1];
184
185 v = ( Hex( c1 ) << 4 ) | Hex( c2 );
186 Emit1( v );
187
188 if ( !string[2] ) {
189 break;
190 }
191 string += 3;
192 }
193 }
EmitRexString(byte rex,const char * string)194 static void EmitRexString(byte rex, const char *string)
195 {
196 #if idx64
197 if(rex)
198 Emit1(rex);
199 #endif
200
201 EmitString(string);
202 }
203
204
205 #define MASK_REG(modrm, mask) \
206 do { \
207 EmitString("81"); \
208 EmitString((modrm)); \
209 Emit4((mask)); \
210 } while(0)
211
212 // add bl, bytes
213 #define STACK_PUSH(bytes) \
214 do { \
215 EmitString("80 C3"); \
216 Emit1(bytes); \
217 } while(0)
218
219 // sub bl, bytes
220 #define STACK_POP(bytes) \
221 do { \
222 EmitString("80 EB"); \
223 Emit1(bytes); \
224 } while(0)
225
EmitCommand(ELastCommand command)226 static void EmitCommand(ELastCommand command)
227 {
228 switch(command)
229 {
230 case LAST_COMMAND_MOV_STACK_EAX:
231 EmitString("89 04 9F"); // mov dword ptr [edi + ebx * 4], eax
232 break;
233
234 case LAST_COMMAND_SUB_BL_1:
235 STACK_POP(1); // sub bl, 1
236 break;
237
238 case LAST_COMMAND_SUB_BL_2:
239 STACK_POP(2); // sub bl, 2
240 break;
241 default:
242 break;
243 }
244 LastCommand = command;
245 }
246
EmitPushStack(vm_t * vm)247 static void EmitPushStack(vm_t *vm)
248 {
249 if (!jlabel)
250 {
251 if(LastCommand == LAST_COMMAND_SUB_BL_1)
252 { // sub bl, 1
253 compiledOfs -= 3;
254 vm->instructionPointers[instruction - 1] = compiledOfs;
255 return;
256 }
257 if(LastCommand == LAST_COMMAND_SUB_BL_2)
258 { // sub bl, 2
259 compiledOfs -= 3;
260 vm->instructionPointers[instruction - 1] = compiledOfs;
261 STACK_POP(1); // sub bl, 1
262 return;
263 }
264 }
265
266 STACK_PUSH(1); // add bl, 1
267 }
268
EmitMovEAXStack(vm_t * vm,int andit)269 static void EmitMovEAXStack(vm_t *vm, int andit)
270 {
271 if(!jlabel)
272 {
273 if(LastCommand == LAST_COMMAND_MOV_STACK_EAX)
274 { // mov [edi + ebx * 4], eax
275 compiledOfs -= 3;
276 vm->instructionPointers[instruction - 1] = compiledOfs;
277 }
278 else if(pop1 == OP_CONST && buf[compiledOfs-7] == 0xC7 && buf[compiledOfs-6] == 0x04 && buf[compiledOfs - 5] == 0x9F)
279 { // mov [edi + ebx * 4], 0x12345678
280 compiledOfs -= 7;
281 vm->instructionPointers[instruction - 1] = compiledOfs;
282 EmitString("B8"); // mov eax, 0x12345678
283
284 if(andit)
285 Emit4(lastConst & andit);
286 else
287 Emit4(lastConst);
288
289 return;
290 }
291 else if(pop1 != OP_DIVI && pop1 != OP_DIVU && pop1 != OP_MULI && pop1 != OP_MULU &&
292 pop1 != OP_STORE4 && pop1 != OP_STORE2 && pop1 != OP_STORE1)
293 {
294 EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
295 }
296 }
297 else
298 EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
299
300 if(andit)
301 {
302 EmitString("25"); // and eax, 0x12345678
303 Emit4(andit);
304 }
305 }
306
EmitMovECXStack(vm_t * vm)307 void EmitMovECXStack(vm_t *vm)
308 {
309 if(!jlabel)
310 {
311 if(LastCommand == LAST_COMMAND_MOV_STACK_EAX) // mov [edi + ebx * 4], eax
312 {
313 compiledOfs -= 3;
314 vm->instructionPointers[instruction - 1] = compiledOfs;
315 EmitString("89 C1"); // mov ecx, eax
316 return;
317 }
318 if(pop1 == OP_DIVI || pop1 == OP_DIVU || pop1 == OP_MULI || pop1 == OP_MULU ||
319 pop1 == OP_STORE4 || pop1 == OP_STORE2 || pop1 == OP_STORE1)
320 {
321 EmitString("89 C1"); // mov ecx, eax
322 return;
323 }
324 }
325
326 EmitString("8B 0C 9F"); // mov ecx, dword ptr [edi + ebx * 4]
327 }
328
329
EmitMovEDXStack(vm_t * vm,int andit)330 void EmitMovEDXStack(vm_t *vm, int andit)
331 {
332 if(!jlabel)
333 {
334 if(LastCommand == LAST_COMMAND_MOV_STACK_EAX)
335 { // mov dword ptr [edi + ebx * 4], eax
336 compiledOfs -= 3;
337 vm->instructionPointers[instruction - 1] = compiledOfs;
338
339 EmitString("8B D0"); // mov edx, eax
340 }
341 else if(pop1 == OP_DIVI || pop1 == OP_DIVU || pop1 == OP_MULI || pop1 == OP_MULU ||
342 pop1 == OP_STORE4 || pop1 == OP_STORE2 || pop1 == OP_STORE1)
343 {
344 EmitString("8B D0"); // mov edx, eax
345 }
346 else if(pop1 == OP_CONST && buf[compiledOfs-7] == 0xC7 && buf[compiledOfs-6] == 0x07 && buf[compiledOfs - 5] == 0x9F)
347 { // mov dword ptr [edi + ebx * 4], 0x12345678
348 compiledOfs -= 7;
349 vm->instructionPointers[instruction - 1] = compiledOfs;
350 EmitString("BA"); // mov edx, 0x12345678
351
352 if(andit)
353 Emit4(lastConst & andit);
354 else
355 Emit4(lastConst);
356
357 return;
358 }
359 else
360 EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
361
362 }
363 else
364 EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
365
366 if(andit)
367 MASK_REG("E2", andit); // and edx, 0x12345678
368 }
369
370 #define JUSED(x) \
371 do { \
372 if (x < 0 || x >= vm->instructionCount) { \
373 VMFREE_BUFFERS(); \
374 Com_Error( ERR_DROP, \
375 "VM_CompileX86: jump target out of range at offset %d", pc ); \
376 } \
377 jused[x] = 1; \
378 } while(0)
379
380 #define SET_JMPOFS(x) do { buf[(x)] = compiledOfs - ((x) + 1); } while(0)
381
382
383 /*
384 =================
385 ErrJump
386 Error handler for jump/call to invalid instruction number
387 =================
388 */
389
ErrJump(void)390 static void __attribute__((__noreturn__)) ErrJump(void)
391 {
392 Com_Error(ERR_DROP, "program tried to execute code outside VM");
393 }
394
395 /*
396 =================
397 DoSyscall
398
399 Assembler helper routines will write its arguments directly to global variables so as to
400 work around different calling conventions
401 =================
402 */
403
404 int vm_syscallNum;
405 int vm_programStack;
406 int *vm_opStackBase;
407 uint8_t vm_opStackOfs;
408 intptr_t vm_arg;
409
DoSyscall(void)410 static void DoSyscall(void)
411 {
412 vm_t *savedVM;
413
414 // save currentVM so as to allow for recursive VM entry
415 savedVM = currentVM;
416 // modify VM stack pointer for recursive VM entry
417 currentVM->programStack = vm_programStack - 4;
418
419 if(vm_syscallNum < 0)
420 {
421 int *data, *ret;
422 #if idx64
423 int index;
424 intptr_t args[MAX_VMSYSCALL_ARGS];
425 #endif
426
427 data = (int *) (savedVM->dataBase + vm_programStack + 4);
428 ret = &vm_opStackBase[vm_opStackOfs + 1];
429
430 #if idx64
431 args[0] = ~vm_syscallNum;
432 for(index = 1; index < ARRAY_LEN(args); index++)
433 args[index] = data[index];
434
435 *ret = savedVM->systemCall(args);
436 #else
437 data[0] = ~vm_syscallNum;
438 *ret = savedVM->systemCall((intptr_t *) data);
439 #endif
440 }
441 else
442 {
443 switch(vm_syscallNum)
444 {
445 case VM_JMP_VIOLATION:
446 ErrJump();
447 break;
448 case VM_BLOCK_COPY:
449 if(vm_opStackOfs < 1)
450 Com_Error(ERR_DROP, "VM_BLOCK_COPY failed due to corrupted opStack");
451
452 VM_BlockCopy(vm_opStackBase[(vm_opStackOfs - 1)], vm_opStackBase[vm_opStackOfs], vm_arg);
453 break;
454 default:
455 Com_Error(ERR_DROP, "Unknown VM operation %d", vm_syscallNum);
456 break;
457 }
458 }
459
460 currentVM = savedVM;
461 }
462
463 /*
464 =================
465 EmitCallRel
466 Relative call to vm->codeBase + callOfs
467 =================
468 */
469
EmitCallRel(vm_t * vm,int callOfs)470 void EmitCallRel(vm_t *vm, int callOfs)
471 {
472 EmitString("E8"); // call 0x12345678
473 Emit4(callOfs - compiledOfs - 4);
474 }
475
476 /*
477 =================
478 EmitCallDoSyscall
479 Call to DoSyscall()
480 =================
481 */
482
EmitCallDoSyscall(vm_t * vm)483 int EmitCallDoSyscall(vm_t *vm)
484 {
485 // use edx register to store DoSyscall address
486 EmitRexString(0x48, "BA"); // mov edx, DoSyscall
487 EmitPtr(DoSyscall);
488
489 // Push important registers to stack as we can't really make
490 // any assumptions about calling conventions.
491 EmitString("51"); // push ebx
492 EmitString("56"); // push esi
493 EmitString("57"); // push edi
494 #if idx64
495 EmitRexString(0x41, "50"); // push r8
496 EmitRexString(0x41, "51"); // push r9
497 #endif
498
499 // write arguments to global vars
500 // syscall number
501 EmitString("A3"); // mov [0x12345678], eax
502 EmitPtr(&vm_syscallNum);
503 // vm_programStack value
504 EmitString("89 F0"); // mov eax, esi
505 EmitString("A3"); // mov [0x12345678], eax
506 EmitPtr(&vm_programStack);
507 // vm_opStackOfs
508 EmitString("88 D8"); // mov al, bl
509 EmitString("A2"); // mov [0x12345678], al
510 EmitPtr(&vm_opStackOfs);
511 // vm_opStackBase
512 EmitRexString(0x48, "89 F8"); // mov eax, edi
513 EmitRexString(0x48, "A3"); // mov [0x12345678], eax
514 EmitPtr(&vm_opStackBase);
515 // vm_arg
516 EmitString("89 C8"); // mov eax, ecx
517 EmitString("A3"); // mov [0x12345678], eax
518 EmitPtr(&vm_arg);
519
520 // align the stack pointer to a 16-byte-boundary
521 EmitString("55"); // push ebp
522 EmitRexString(0x48, "89 E5"); // mov ebp, esp
523 EmitRexString(0x48, "83 E4 F0"); // and esp, 0xFFFFFFF0
524
525 // call the syscall wrapper function DoSyscall()
526
527 EmitString("FF D2"); // call edx
528
529 // reset the stack pointer to its previous value
530 EmitRexString(0x48, "89 EC"); // mov esp, ebp
531 EmitString("5D"); // pop ebp
532
533 #if idx64
534 EmitRexString(0x41, "59"); // pop r9
535 EmitRexString(0x41, "58"); // pop r8
536 #endif
537 EmitString("5F"); // pop edi
538 EmitString("5E"); // pop esi
539 EmitString("59"); // pop ebx
540
541 EmitString("C3"); // ret
542
543 return compiledOfs;
544 }
545
546 /*
547 =================
548 EmitCallErrJump
549 Emit the code that triggers execution of the jump violation handler
550 =================
551 */
552
EmitCallErrJump(vm_t * vm,int sysCallOfs)553 static void EmitCallErrJump(vm_t *vm, int sysCallOfs)
554 {
555 EmitString("B8"); // mov eax, 0x12345678
556 Emit4(VM_JMP_VIOLATION);
557
558 EmitCallRel(vm, sysCallOfs);
559 }
560
561 /*
562 =================
563 EmitCallProcedure
564 VM OP_CALL procedure for call destinations obtained at runtime
565 =================
566 */
567
EmitCallProcedure(vm_t * vm,int sysCallOfs)568 int EmitCallProcedure(vm_t *vm, int sysCallOfs)
569 {
570 int jmpSystemCall, jmpBadAddr;
571 int retval;
572
573 EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
574 STACK_POP(1); // sub bl, 1
575 EmitString("85 C0"); // test eax, eax
576
577 // Jump to syscall code, 1 byte offset should suffice
578 EmitString("7C"); // jl systemCall
579 jmpSystemCall = compiledOfs++;
580
581 /************ Call inside VM ************/
582
583 EmitString("81 F8"); // cmp eax, vm->instructionCount
584 Emit4(vm->instructionCount);
585
586 // Error jump if invalid jump target
587 EmitString("73"); // jae badAddr
588 jmpBadAddr = compiledOfs++;
589
590 #if idx64
591 EmitRexString(0x49, "FF 14 C0"); // call qword ptr [r8 + eax * 8]
592 #else
593 EmitString("FF 14 85"); // call dword ptr [vm->instructionPointers + eax * 4]
594 Emit4((intptr_t) vm->instructionPointers);
595 #endif
596 EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
597 EmitString("C3"); // ret
598
599 // badAddr:
600 SET_JMPOFS(jmpBadAddr);
601 EmitCallErrJump(vm, sysCallOfs);
602
603 /************ System Call ************/
604
605 // systemCall:
606 SET_JMPOFS(jmpSystemCall);
607 retval = compiledOfs;
608
609 EmitCallRel(vm, sysCallOfs);
610
611 // have opStack reg point at return value
612 STACK_PUSH(1); // add bl, 1
613 EmitString("C3"); // ret
614
615 return retval;
616 }
617
618 /*
619 =================
620 EmitJumpIns
621 Jump to constant instruction number
622 =================
623 */
624
EmitJumpIns(vm_t * vm,const char * jmpop,int cdest)625 void EmitJumpIns(vm_t *vm, const char *jmpop, int cdest)
626 {
627 JUSED(cdest);
628
629 EmitString(jmpop); // j??? 0x12345678
630
631 // we only know all the jump addresses in the third pass
632 if(pass == 2)
633 Emit4(vm->instructionPointers[cdest] - compiledOfs - 4);
634 else
635 compiledOfs += 4;
636 }
637
638 /*
639 =================
640 EmitCallIns
641 Call to constant instruction number
642 =================
643 */
644
EmitCallIns(vm_t * vm,int cdest)645 void EmitCallIns(vm_t *vm, int cdest)
646 {
647 JUSED(cdest);
648
649 EmitString("E8"); // call 0x12345678
650
651 // we only know all the jump addresses in the third pass
652 if(pass == 2)
653 Emit4(vm->instructionPointers[cdest] - compiledOfs - 4);
654 else
655 compiledOfs += 4;
656 }
657
658 /*
659 =================
660 EmitCallConst
661 Call to constant instruction number or syscall
662 =================
663 */
664
EmitCallConst(vm_t * vm,int cdest,int callProcOfsSyscall)665 void EmitCallConst(vm_t *vm, int cdest, int callProcOfsSyscall)
666 {
667 if(cdest < 0)
668 {
669 EmitString("B8"); // mov eax, cdest
670 Emit4(cdest);
671
672 EmitCallRel(vm, callProcOfsSyscall);
673 }
674 else
675 EmitCallIns(vm, cdest);
676 }
677
678 /*
679 =================
680 EmitBranchConditions
681 Emits x86 branch condition as given in op
682 =================
683 */
EmitBranchConditions(vm_t * vm,int op)684 void EmitBranchConditions(vm_t *vm, int op)
685 {
686 switch(op)
687 {
688 case OP_EQ:
689 EmitJumpIns(vm, "0F 84", Constant4()); // je 0x12345678
690 break;
691 case OP_NE:
692 EmitJumpIns(vm, "0F 85", Constant4()); // jne 0x12345678
693 break;
694 case OP_LTI:
695 EmitJumpIns(vm, "0F 8C", Constant4()); // jl 0x12345678
696 break;
697 case OP_LEI:
698 EmitJumpIns(vm, "0F 8E", Constant4()); // jle 0x12345678
699 break;
700 case OP_GTI:
701 EmitJumpIns(vm, "0F 8F", Constant4()); // jg 0x12345678
702 break;
703 case OP_GEI:
704 EmitJumpIns(vm, "0F 8D", Constant4()); // jge 0x12345678
705 break;
706 case OP_LTU:
707 EmitJumpIns(vm, "0F 82", Constant4()); // jb 0x12345678
708 break;
709 case OP_LEU:
710 EmitJumpIns(vm, "0F 86", Constant4()); // jbe 0x12345678
711 break;
712 case OP_GTU:
713 EmitJumpIns(vm, "0F 87", Constant4()); // ja 0x12345678
714 break;
715 case OP_GEU:
716 EmitJumpIns(vm, "0F 83", Constant4()); // jae 0x12345678
717 break;
718 }
719 }
720
721
722 /*
723 =================
724 ConstOptimize
725 Constant values for immediately following instructions may be translated to immediate values
726 instead of opStack operations, which will save expensive operations on memory
727 =================
728 */
729
ConstOptimize(vm_t * vm,int callProcOfsSyscall)730 qboolean ConstOptimize(vm_t *vm, int callProcOfsSyscall)
731 {
732 int v;
733 int op1;
734
735 // we can safely perform optimizations only in case if
736 // we are 100% sure that next instruction is not a jump label
737 if (vm->jumpTableTargets && !jused[instruction])
738 op1 = code[pc+4];
739 else
740 return qfalse;
741
742 switch ( op1 ) {
743
744 case OP_LOAD4:
745 EmitPushStack(vm);
746 #if idx64
747 EmitRexString(0x41, "8B 81"); // mov eax, dword ptr [r9 + 0x12345678]
748 Emit4(Constant4() & vm->dataMask);
749 #else
750 EmitString("B8"); // mov eax, 0x12345678
751 EmitPtr(vm->dataBase + (Constant4() & vm->dataMask));
752 EmitString("8B 00"); // mov eax, dword ptr [eax]
753 #endif
754 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
755
756 pc++; // OP_LOAD4
757 instruction += 1;
758 return qtrue;
759
760 case OP_LOAD2:
761 EmitPushStack(vm);
762 #if idx64
763 EmitRexString(0x41, "0F B7 81"); // movzx eax, word ptr [r9 + 0x12345678]
764 Emit4(Constant4() & vm->dataMask);
765 #else
766 EmitString("B8"); // mov eax, 0x12345678
767 EmitPtr(vm->dataBase + (Constant4() & vm->dataMask));
768 EmitString("0F B7 00"); // movzx eax, word ptr [eax]
769 #endif
770 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
771
772 pc++; // OP_LOAD2
773 instruction += 1;
774 return qtrue;
775
776 case OP_LOAD1:
777 EmitPushStack(vm);
778 #if idx64
779 EmitRexString(0x41, "0F B6 81"); // movzx eax, byte ptr [r9 + 0x12345678]
780 Emit4(Constant4() & vm->dataMask);
781 #else
782 EmitString("B8"); // mov eax, 0x12345678
783 EmitPtr(vm->dataBase + (Constant4() & vm->dataMask));
784 EmitString("0F B6 00"); // movzx eax, byte ptr [eax]
785 #endif
786 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
787
788 pc++; // OP_LOAD1
789 instruction += 1;
790 return qtrue;
791
792 case OP_STORE4:
793 EmitMovEAXStack(vm, vm->dataMask);
794 #if idx64
795 EmitRexString(0x41, "C7 04 01"); // mov dword ptr [r9 + eax], 0x12345678
796 Emit4(Constant4());
797 #else
798 EmitString("C7 80"); // mov dword ptr [eax + 0x12345678], 0x12345678
799 Emit4((intptr_t) vm->dataBase);
800 Emit4(Constant4());
801 #endif
802 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
803 pc++; // OP_STORE4
804 instruction += 1;
805 return qtrue;
806
807 case OP_STORE2:
808 EmitMovEAXStack(vm, vm->dataMask);
809 #if idx64
810 Emit1(0x66); // mov word ptr [r9 + eax], 0x1234
811 EmitRexString(0x41, "C7 04 01");
812 Emit2(Constant4());
813 #else
814 EmitString("66 C7 80"); // mov word ptr [eax + 0x12345678], 0x1234
815 Emit4((intptr_t) vm->dataBase);
816 Emit2(Constant4());
817 #endif
818 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
819
820 pc++; // OP_STORE2
821 instruction += 1;
822 return qtrue;
823
824 case OP_STORE1:
825 EmitMovEAXStack(vm, vm->dataMask);
826 #if idx64
827 EmitRexString(0x41, "C6 04 01"); // mov byte [r9 + eax], 0x12
828 Emit1(Constant4());
829 #else
830 EmitString("C6 80"); // mov byte ptr [eax + 0x12345678], 0x12
831 Emit4((intptr_t) vm->dataBase);
832 Emit1(Constant4());
833 #endif
834 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
835
836 pc++; // OP_STORE1
837 instruction += 1;
838 return qtrue;
839
840 case OP_ADD:
841 v = Constant4();
842
843 EmitMovEAXStack(vm, 0);
844 if(iss8(v))
845 {
846 EmitString("83 C0"); // add eax, 0x7F
847 Emit1(v);
848 }
849 else
850 {
851 EmitString("05"); // add eax, 0x12345678
852 Emit4(v);
853 }
854 EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
855
856 pc++; // OP_ADD
857 instruction += 1;
858 return qtrue;
859
860 case OP_SUB:
861 v = Constant4();
862
863 EmitMovEAXStack(vm, 0);
864 if(iss8(v))
865 {
866 EmitString("83 E8"); // sub eax, 0x7F
867 Emit1(v);
868 }
869 else
870 {
871 EmitString("2D"); // sub eax, 0x12345678
872 Emit4(v);
873 }
874 EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
875
876 pc++; // OP_SUB
877 instruction += 1;
878 return qtrue;
879
880 case OP_MULI:
881 v = Constant4();
882
883 EmitMovEAXStack(vm, 0);
884 if(iss8(v))
885 {
886 EmitString("6B C0"); // imul eax, 0x7F
887 Emit1(v);
888 }
889 else
890 {
891 EmitString("69 C0"); // imul eax, 0x12345678
892 Emit4(v);
893 }
894 EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
895 pc++; // OP_MULI
896 instruction += 1;
897
898 return qtrue;
899
900 case OP_LSH:
901 v = NextConstant4();
902 if(v < 0 || v > 31)
903 break;
904
905 EmitMovEAXStack(vm, 0);
906 EmitString("C1 E0"); // shl eax, 0x12
907 Emit1(v);
908 EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
909
910 pc += 5; // CONST + OP_LSH
911 instruction += 1;
912 return qtrue;
913
914 case OP_RSHI:
915 v = NextConstant4();
916 if(v < 0 || v > 31)
917 break;
918
919 EmitMovEAXStack(vm, 0);
920 EmitString("C1 F8"); // sar eax, 0x12
921 Emit1(v);
922 EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
923
924 pc += 5; // CONST + OP_RSHI
925 instruction += 1;
926 return qtrue;
927
928 case OP_RSHU:
929 v = NextConstant4();
930 if(v < 0 || v > 31)
931 break;
932
933 EmitMovEAXStack(vm, 0);
934 EmitString("C1 E8"); // shr eax, 0x12
935 Emit1(v);
936 EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
937
938 pc += 5; // CONST + OP_RSHU
939 instruction += 1;
940 return qtrue;
941
942 case OP_BAND:
943 v = Constant4();
944
945 EmitMovEAXStack(vm, 0);
946 if(iss8(v))
947 {
948 EmitString("83 E0"); // and eax, 0x7F
949 Emit1(v);
950 }
951 else
952 {
953 EmitString("25"); // and eax, 0x12345678
954 Emit4(v);
955 }
956 EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
957
958 pc += 1; // OP_BAND
959 instruction += 1;
960 return qtrue;
961
962 case OP_BOR:
963 v = Constant4();
964
965 EmitMovEAXStack(vm, 0);
966 if(iss8(v))
967 {
968 EmitString("83 C8"); // or eax, 0x7F
969 Emit1(v);
970 }
971 else
972 {
973 EmitString("0D"); // or eax, 0x12345678
974 Emit4(v);
975 }
976 EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
977
978 pc += 1; // OP_BOR
979 instruction += 1;
980 return qtrue;
981
982 case OP_BXOR:
983 v = Constant4();
984
985 EmitMovEAXStack(vm, 0);
986 if(iss8(v))
987 {
988 EmitString("83 F0"); // xor eax, 0x7F
989 Emit1(v);
990 }
991 else
992 {
993 EmitString("35"); // xor eax, 0x12345678
994 Emit4(v);
995 }
996 EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
997
998 pc += 1; // OP_BXOR
999 instruction += 1;
1000 return qtrue;
1001
1002 case OP_EQ:
1003 case OP_NE:
1004 case OP_LTI:
1005 case OP_LEI:
1006 case OP_GTI:
1007 case OP_GEI:
1008 case OP_LTU:
1009 case OP_LEU:
1010 case OP_GTU:
1011 case OP_GEU:
1012 EmitMovEAXStack(vm, 0);
1013 EmitCommand(LAST_COMMAND_SUB_BL_1);
1014 EmitString("3D"); // cmp eax, 0x12345678
1015 Emit4(Constant4());
1016
1017 pc++; // OP_*
1018 EmitBranchConditions(vm, op1);
1019 instruction++;
1020
1021 return qtrue;
1022
1023 case OP_EQF:
1024 case OP_NEF:
1025 if(NextConstant4())
1026 break;
1027 pc += 5; // CONST + OP_EQF|OP_NEF
1028
1029 EmitMovEAXStack(vm, 0);
1030 EmitCommand(LAST_COMMAND_SUB_BL_1);
1031 // floating point hack :)
1032 EmitString("25"); // and eax, 0x7FFFFFFF
1033 Emit4(0x7FFFFFFF);
1034 if(op1 == OP_EQF)
1035 EmitJumpIns(vm, "0F 84", Constant4()); // jz 0x12345678
1036 else
1037 EmitJumpIns(vm, "0F 85", Constant4()); // jnz 0x12345678
1038
1039 instruction += 1;
1040 return qtrue;
1041
1042
1043 case OP_JUMP:
1044 EmitJumpIns(vm, "E9", Constant4()); // jmp 0x12345678
1045
1046 pc += 1; // OP_JUMP
1047 instruction += 1;
1048 return qtrue;
1049
1050 case OP_CALL:
1051 v = Constant4();
1052 EmitCallConst(vm, v, callProcOfsSyscall);
1053
1054 pc += 1; // OP_CALL
1055 instruction += 1;
1056 return qtrue;
1057
1058 default:
1059 break;
1060 }
1061
1062 return qfalse;
1063 }
1064
1065 /*
1066 =================
1067 VM_Compile
1068 =================
1069 */
VM_Compile(vm_t * vm,vmHeader_t * header)1070 void VM_Compile(vm_t *vm, vmHeader_t *header)
1071 {
1072 int op;
1073 int maxLength;
1074 int v;
1075 int i;
1076 int callProcOfsSyscall, callProcOfs, callDoSyscallOfs;
1077
1078 jusedSize = header->instructionCount + 2;
1079
1080 // allocate a very large temp buffer, we will shrink it later
1081 maxLength = header->codeLength * 8 + 64;
1082 buf = Z_Malloc(maxLength);
1083 jused = Z_Malloc(jusedSize);
1084 code = Z_Malloc(header->codeLength+32);
1085
1086 Com_Memset(jused, 0, jusedSize);
1087 Com_Memset(buf, 0, maxLength);
1088
1089 // copy code in larger buffer and put some zeros at the end
1090 // so we can safely look ahead for a few instructions in it
1091 // without a chance to get false-positive because of some garbage bytes
1092 Com_Memset(code, 0, header->codeLength+32);
1093 Com_Memcpy(code, (byte *)header + header->codeOffset, header->codeLength );
1094
1095 // ensure that the optimisation pass knows about all the jump
1096 // table targets
1097 pc = -1; // a bogus value to be printed in out-of-bounds error messages
1098 for( i = 0; i < vm->numJumpTableTargets; i++ ) {
1099 JUSED( *(int *)(vm->jumpTableTargets + ( i * sizeof( int ) ) ) );
1100 }
1101
1102 // Start buffer with x86-VM specific procedures
1103 compiledOfs = 0;
1104
1105 callDoSyscallOfs = compiledOfs;
1106 callProcOfs = EmitCallDoSyscall(vm);
1107 callProcOfsSyscall = EmitCallProcedure(vm, callDoSyscallOfs);
1108 vm->entryOfs = compiledOfs;
1109
1110 for(pass=0; pass < 3; pass++) {
1111 oc0 = -23423;
1112 oc1 = -234354;
1113 pop0 = -43435;
1114 pop1 = -545455;
1115
1116 // translate all instructions
1117 pc = 0;
1118 instruction = 0;
1119 //code = (byte *)header + header->codeOffset;
1120 compiledOfs = vm->entryOfs;
1121
1122 LastCommand = LAST_COMMAND_NONE;
1123
1124 while(instruction < header->instructionCount)
1125 {
1126 if(compiledOfs > maxLength - 16)
1127 {
1128 VMFREE_BUFFERS();
1129 Com_Error(ERR_DROP, "VM_CompileX86: maxLength exceeded");
1130 }
1131
1132 vm->instructionPointers[ instruction ] = compiledOfs;
1133
1134 if ( !vm->jumpTableTargets )
1135 jlabel = 1;
1136 else
1137 jlabel = jused[ instruction ];
1138
1139 instruction++;
1140
1141 if(pc > header->codeLength)
1142 {
1143 VMFREE_BUFFERS();
1144 Com_Error(ERR_DROP, "VM_CompileX86: pc > header->codeLength");
1145 }
1146
1147 op = code[ pc ];
1148 pc++;
1149 switch ( op ) {
1150 case 0:
1151 break;
1152 case OP_BREAK:
1153 EmitString("CC"); // int 3
1154 break;
1155 case OP_ENTER:
1156 EmitString("81 EE"); // sub esi, 0x12345678
1157 Emit4(Constant4());
1158 break;
1159 case OP_CONST:
1160 if(ConstOptimize(vm, callProcOfsSyscall))
1161 break;
1162
1163 EmitPushStack(vm);
1164 EmitString("C7 04 9F"); // mov dword ptr [edi + ebx * 4], 0x12345678
1165 lastConst = Constant4();
1166
1167 Emit4(lastConst);
1168 if(code[pc] == OP_JUMP)
1169 JUSED(lastConst);
1170
1171 break;
1172 case OP_LOCAL:
1173 EmitPushStack(vm);
1174 EmitString("8D 86"); // lea eax, [0x12345678 + esi]
1175 oc0 = oc1;
1176 oc1 = Constant4();
1177 Emit4(oc1);
1178 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
1179 break;
1180 case OP_ARG:
1181 EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
1182 EmitString("8B D6"); // mov edx, esi
1183 EmitString("81 C2"); // add edx, 0x12345678
1184 Emit4((Constant1() & 0xFF));
1185 MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
1186 #if idx64
1187 EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
1188 #else
1189 EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
1190 Emit4((intptr_t) vm->dataBase);
1191 #endif
1192 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1193 break;
1194 case OP_CALL:
1195 EmitCallRel(vm, callProcOfs);
1196 break;
1197 case OP_PUSH:
1198 EmitPushStack(vm);
1199 break;
1200 case OP_POP:
1201 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1202 break;
1203 case OP_LEAVE:
1204 v = Constant4();
1205 EmitString("81 C6"); // add esi, 0x12345678
1206 Emit4(v);
1207 EmitString("C3"); // ret
1208 break;
1209 case OP_LOAD4:
1210 if (code[pc] == OP_CONST && code[pc+5] == OP_ADD && code[pc+6] == OP_STORE4)
1211 {
1212 if(oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
1213 {
1214 compiledOfs -= 12;
1215 vm->instructionPointers[instruction - 1] = compiledOfs;
1216 }
1217
1218 pc++; // OP_CONST
1219 v = Constant4();
1220
1221 EmitMovEDXStack(vm, vm->dataMask);
1222 if(v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
1223 {
1224 #if idx64
1225 EmitRexString(0x41, "FF 04 11"); // inc dword ptr [r9 + edx]
1226 #else
1227 EmitString("FF 82"); // inc dword ptr [edx + 0x12345678]
1228 Emit4((intptr_t) vm->dataBase);
1229 #endif
1230 }
1231 else
1232 {
1233 #if idx64
1234 EmitRexString(0x41, "8B 04 11"); // mov eax, dword ptr [r9 + edx]
1235 #else
1236 EmitString("8B 82"); // mov eax, dword ptr [edx + 0x12345678]
1237 Emit4((intptr_t) vm->dataBase);
1238 #endif
1239 EmitString("05"); // add eax, v
1240 Emit4(v);
1241
1242 if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
1243 {
1244 #if idx64
1245 EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
1246 #else
1247 EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
1248 Emit4((intptr_t) vm->dataBase);
1249 #endif
1250 }
1251 else
1252 {
1253 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1254 EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
1255 MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
1256 #if idx64
1257 EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
1258 #else
1259 EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
1260 Emit4((intptr_t) vm->dataBase);
1261 #endif
1262 }
1263 }
1264
1265 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1266 pc++; // OP_ADD
1267 pc++; // OP_STORE
1268 instruction += 3;
1269 break;
1270 }
1271
1272 if(code[pc] == OP_CONST && code[pc+5] == OP_SUB && code[pc+6] == OP_STORE4)
1273 {
1274 if(oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
1275 {
1276 compiledOfs -= 12;
1277 vm->instructionPointers[instruction - 1] = compiledOfs;
1278 }
1279
1280 pc++; // OP_CONST
1281 v = Constant4();
1282
1283 EmitMovEDXStack(vm, vm->dataMask);
1284 if(v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
1285 {
1286 #if idx64
1287 EmitRexString(0x41, "FF 0C 11"); // dec dword ptr [r9 + edx]
1288 #else
1289 EmitString("FF 8A"); // dec dword ptr [edx + 0x12345678]
1290 Emit4((intptr_t) vm->dataBase);
1291 #endif
1292 }
1293 else
1294 {
1295 #if idx64
1296 EmitRexString(0x41, "8B 04 11"); // mov eax, dword ptr [r9 + edx]
1297 #else
1298 EmitString("8B 82"); // mov eax, dword ptr [edx + 0x12345678]
1299 Emit4((intptr_t) vm->dataBase);
1300 #endif
1301 EmitString("2D"); // sub eax, v
1302 Emit4(v);
1303
1304 if(oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
1305 {
1306 #if idx64
1307 EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
1308 #else
1309 EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
1310 Emit4((intptr_t) vm->dataBase);
1311 #endif
1312 }
1313 else
1314 {
1315 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1316 EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
1317 MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
1318 #if idx64
1319 EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
1320 #else
1321 EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
1322 Emit4((intptr_t) vm->dataBase);
1323 #endif
1324 }
1325 }
1326 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1327 pc++; // OP_SUB
1328 pc++; // OP_STORE
1329 instruction += 3;
1330 break;
1331 }
1332
1333 if(buf[compiledOfs - 3] == 0x89 && buf[compiledOfs - 2] == 0x04 && buf[compiledOfs - 1] == 0x9F)
1334 {
1335 compiledOfs -= 3;
1336 vm->instructionPointers[instruction - 1] = compiledOfs;
1337 MASK_REG("E0", vm->dataMask); // and eax, 0x12345678
1338 #if idx64
1339 EmitRexString(0x41, "8B 04 01"); // mov eax, dword ptr [r9 + eax]
1340 #else
1341 EmitString("8B 80"); // mov eax, dword ptr [eax + 0x1234567]
1342 Emit4((intptr_t) vm->dataBase);
1343 #endif
1344 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
1345 break;
1346 }
1347
1348 EmitMovEAXStack(vm, vm->dataMask);
1349 #if idx64
1350 EmitRexString(0x41, "8B 04 01"); // mov eax, dword ptr [r9 + eax]
1351 #else
1352 EmitString("8B 80"); // mov eax, dword ptr [eax + 0x12345678]
1353 Emit4((intptr_t) vm->dataBase);
1354 #endif
1355 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
1356 break;
1357 case OP_LOAD2:
1358 EmitMovEAXStack(vm, vm->dataMask);
1359 #if idx64
1360 EmitRexString(0x41, "0F B7 04 01"); // movzx eax, word ptr [r9 + eax]
1361 #else
1362 EmitString("0F B7 80"); // movzx eax, word ptr [eax + 0x12345678]
1363 Emit4((intptr_t) vm->dataBase);
1364 #endif
1365 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
1366 break;
1367 case OP_LOAD1:
1368 EmitMovEAXStack(vm, vm->dataMask);
1369 #if idx64
1370 EmitRexString(0x41, "0F B6 04 01"); // movzx eax, byte ptr [r9 + eax]
1371 #else
1372 EmitString("0F B6 80"); // movzx eax, byte ptr [eax + 0x12345678]
1373 Emit4((intptr_t) vm->dataBase);
1374 #endif
1375 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
1376 break;
1377 case OP_STORE4:
1378 EmitMovEAXStack(vm, 0);
1379 EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4]
1380 MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
1381 #if idx64
1382 EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
1383 #else
1384 EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
1385 Emit4((intptr_t) vm->dataBase);
1386 #endif
1387 EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
1388 break;
1389 case OP_STORE2:
1390 EmitMovEAXStack(vm, 0);
1391 EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4]
1392 MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
1393 #if idx64
1394 Emit1(0x66); // mov word ptr [r9 + edx], eax
1395 EmitRexString(0x41, "89 04 11");
1396 #else
1397 EmitString("66 89 82"); // mov word ptr [edx + 0x12345678], eax
1398 Emit4((intptr_t) vm->dataBase);
1399 #endif
1400 EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
1401 break;
1402 case OP_STORE1:
1403 EmitMovEAXStack(vm, 0);
1404 EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4]
1405 MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
1406 #if idx64
1407 EmitRexString(0x41, "88 04 11"); // mov byte ptr [r9 + edx], eax
1408 #else
1409 EmitString("88 82"); // mov byte ptr [edx + 0x12345678], eax
1410 Emit4((intptr_t) vm->dataBase);
1411 #endif
1412 EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
1413 break;
1414
1415 case OP_EQ:
1416 case OP_NE:
1417 case OP_LTI:
1418 case OP_LEI:
1419 case OP_GTI:
1420 case OP_GEI:
1421 case OP_LTU:
1422 case OP_LEU:
1423 case OP_GTU:
1424 case OP_GEU:
1425 EmitMovEAXStack(vm, 0);
1426 EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
1427 EmitString("39 44 9F 04"); // cmp eax, dword ptr 4[edi + ebx * 4]
1428
1429 EmitBranchConditions(vm, op);
1430 break;
1431 case OP_EQF:
1432 case OP_NEF:
1433 case OP_LTF:
1434 case OP_LEF:
1435 case OP_GTF:
1436 case OP_GEF:
1437 EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
1438 EmitString("D9 44 9F 04"); // fld dword ptr 4[edi + ebx * 4]
1439 EmitString("D8 5C 9F 08"); // fcomp dword ptr 8[edi + ebx * 4]
1440 EmitString("DF E0"); // fnstsw ax
1441
1442 switch(op)
1443 {
1444 case OP_EQF:
1445 EmitString("F6 C4 40"); // test ah,0x40
1446 EmitJumpIns(vm, "0F 85", Constant4()); // jne 0x12345678
1447 break;
1448 case OP_NEF:
1449 EmitString("F6 C4 40"); // test ah,0x40
1450 EmitJumpIns(vm, "0F 84", Constant4()); // je 0x12345678
1451 break;
1452 case OP_LTF:
1453 EmitString("F6 C4 01"); // test ah,0x01
1454 EmitJumpIns(vm, "0F 85", Constant4()); // jne 0x12345678
1455 break;
1456 case OP_LEF:
1457 EmitString("F6 C4 41"); // test ah,0x41
1458 EmitJumpIns(vm, "0F 85", Constant4()); // jne 0x12345678
1459 break;
1460 case OP_GTF:
1461 EmitString("F6 C4 41"); // test ah,0x41
1462 EmitJumpIns(vm, "0F 84", Constant4()); // je 0x12345678
1463 break;
1464 case OP_GEF:
1465 EmitString("F6 C4 01"); // test ah,0x01
1466 EmitJumpIns(vm, "0F 84", Constant4()); // je 0x12345678
1467 break;
1468 }
1469 break;
1470 case OP_NEGI:
1471 EmitMovEAXStack(vm, 0);
1472 EmitString("F7 D8"); // neg eax
1473 EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
1474 break;
1475 case OP_ADD:
1476 EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
1477 EmitString("01 44 9F FC"); // add dword ptr -4[edi + ebx * 4], eax
1478 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1479 break;
1480 case OP_SUB:
1481 EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
1482 EmitString("29 44 9F FC"); // sub dword ptr -4[edi + ebx * 4], eax
1483 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1484 break;
1485 case OP_DIVI:
1486 EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
1487 EmitString("99"); // cdq
1488 EmitString("F7 3C 9F"); // idiv dword ptr [edi + ebx * 4]
1489 EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
1490 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1491 break;
1492 case OP_DIVU:
1493 EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
1494 EmitString("33 D2"); // xor edx, edx
1495 EmitString("F7 34 9F"); // div dword ptr [edi + ebx * 4]
1496 EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
1497 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1498 break;
1499 case OP_MODI:
1500 EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
1501 EmitString("99" ); // cdq
1502 EmitString("F7 3C 9F"); // idiv dword ptr [edi + ebx * 4]
1503 EmitString("89 54 9F FC"); // mov dword ptr -4[edi + ebx * 4],edx
1504 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1505 break;
1506 case OP_MODU:
1507 EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
1508 EmitString("33 D2"); // xor edx, edx
1509 EmitString("F7 34 9F"); // div dword ptr [edi + ebx * 4]
1510 EmitString("89 54 9F FC"); // mov dword ptr -4[edi + ebx * 4],edx
1511 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1512 break;
1513 case OP_MULI:
1514 EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
1515 EmitString("F7 2C 9F"); // imul dword ptr [edi + ebx * 4]
1516 EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
1517 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1518 break;
1519 case OP_MULU:
1520 EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
1521 EmitString("F7 24 9F"); // mul dword ptr [edi + ebx * 4]
1522 EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
1523 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1524 break;
1525 case OP_BAND:
1526 EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
1527 EmitString("21 44 9F FC"); // and dword ptr -4[edi + ebx * 4],eax
1528 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1529 break;
1530 case OP_BOR:
1531 EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
1532 EmitString("09 44 9F FC"); // or dword ptr -4[edi + ebx * 4],eax
1533 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1534 break;
1535 case OP_BXOR:
1536 EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
1537 EmitString("31 44 9F FC"); // xor dword ptr -4[edi + ebx * 4],eax
1538 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1539 break;
1540 case OP_BCOM:
1541 EmitString("F7 14 9F"); // not dword ptr [edi + ebx * 4]
1542 break;
1543 case OP_LSH:
1544 EmitMovECXStack(vm);
1545 EmitString("D3 64 9F FC"); // shl dword ptr -4[edi + ebx * 4], cl
1546 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1547 break;
1548 case OP_RSHI:
1549 EmitMovECXStack(vm);
1550 EmitString("D3 7C 9F FC"); // sar dword ptr -4[edi + ebx * 4], cl
1551 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1552 break;
1553 case OP_RSHU:
1554 EmitMovECXStack(vm);
1555 EmitString("D3 6C 9F FC"); // shr dword ptr -4[edi + ebx * 4], cl
1556 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1557 break;
1558 case OP_NEGF:
1559 EmitString("D9 04 9F"); // fld dword ptr [edi + ebx * 4]
1560 EmitString("D9 E0"); // fchs
1561 EmitString("D9 1C 9F"); // fstp dword ptr [edi + ebx * 4]
1562 break;
1563 case OP_ADDF:
1564 EmitString("D9 44 9F FC"); // fld dword ptr -4[edi + ebx * 4]
1565 EmitString("D8 04 9F"); // fadd dword ptr [edi + ebx * 4]
1566 EmitString("D9 5C 9F FC"); // fstp dword ptr -4[edi + ebx * 4]
1567 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1568 break;
1569 case OP_SUBF:
1570 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1571 EmitString("D9 04 9F"); // fld dword ptr [edi + ebx * 4]
1572 EmitString("D8 64 9F 04"); // fsub dword ptr 4[edi + ebx * 4]
1573 EmitString("D9 1C 9F"); // fstp dword ptr [edi + ebx * 4]
1574 break;
1575 case OP_DIVF:
1576 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1577 EmitString("D9 04 9F"); // fld dword ptr [edi + ebx * 4]
1578 EmitString("D8 74 9F 04"); // fdiv dword ptr 4[edi + ebx * 4]
1579 EmitString("D9 1C 9F"); // fstp dword ptr [edi + ebx * 4]
1580 break;
1581 case OP_MULF:
1582 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1583 EmitString("D9 04 9F"); // fld dword ptr [edi + ebx * 4]
1584 EmitString("D8 4C 9F 04"); // fmul dword ptr 4[edi + ebx * 4]
1585 EmitString("D9 1C 9F"); // fstp dword ptr [edi + ebx * 4]
1586 break;
1587 case OP_CVIF:
1588 EmitString("DB 04 9F"); // fild dword ptr [edi + ebx * 4]
1589 EmitString("D9 1C 9F"); // fstp dword ptr [edi + ebx * 4]
1590 break;
1591 case OP_CVFI:
1592 #ifndef FTOL_PTR // WHENHELLISFROZENOVER
1593 // not IEEE complient, but simple and fast
1594 EmitString("D9 04 9F"); // fld dword ptr [edi + ebx * 4]
1595 EmitString("DB 1C 9F"); // fistp dword ptr [edi + ebx * 4]
1596 #else // FTOL_PTR
1597 // call the library conversion function
1598 EmitRexString(0x48, "BA"); // mov edx, Q_VMftol
1599 EmitPtr(Q_VMftol);
1600 EmitRexString(0x48, "FF D2"); // call edx
1601 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
1602 #endif
1603 break;
1604 case OP_SEX8:
1605 EmitString("0F BE 04 9F"); // movsx eax, byte ptr [edi + ebx * 4]
1606 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
1607 break;
1608 case OP_SEX16:
1609 EmitString("0F BF 04 9F"); // movsx eax, word ptr [edi + ebx * 4]
1610 EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
1611 break;
1612
1613 case OP_BLOCK_COPY:
1614 EmitString("B8"); // mov eax, 0x12345678
1615 Emit4(VM_BLOCK_COPY);
1616 EmitString("B9"); // mov ecx, 0x12345678
1617 Emit4(Constant4());
1618
1619 EmitCallRel(vm, callDoSyscallOfs);
1620
1621 EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
1622 break;
1623
1624 case OP_JUMP:
1625 EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
1626 EmitString("8B 44 9F 04"); // mov eax, dword ptr 4[edi + ebx * 4]
1627 EmitString("81 F8"); // cmp eax, vm->instructionCount
1628 Emit4(vm->instructionCount);
1629 #if idx64
1630 EmitString("73 04"); // jae +4
1631 EmitRexString(0x49, "FF 24 C0"); // jmp qword ptr [r8 + eax * 8]
1632 #else
1633 EmitString("73 07"); // jae +7
1634 EmitString("FF 24 85"); // jmp dword ptr [instructionPointers + eax * 4]
1635 Emit4((intptr_t) vm->instructionPointers);
1636 #endif
1637 EmitCallErrJump(vm, callDoSyscallOfs);
1638 break;
1639 default:
1640 VMFREE_BUFFERS();
1641 Com_Error(ERR_DROP, "VM_CompileX86: bad opcode %i at offset %i", op, pc);
1642 }
1643 pop0 = pop1;
1644 pop1 = op;
1645 }
1646 }
1647
1648 // copy to an exact sized buffer with the appropriate permission bits
1649 vm->codeLength = compiledOfs;
1650 #ifdef VM_X86_MMAP
1651 vm->codeBase = mmap(NULL, compiledOfs, PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
1652 if(vm->codeBase == MAP_FAILED)
1653 Com_Error(ERR_FATAL, "VM_CompileX86: can't mmap memory");
1654 #elif _WIN32
1655 // allocate memory with EXECUTE permissions under windows.
1656 vm->codeBase = VirtualAlloc(NULL, compiledOfs, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
1657 if(!vm->codeBase)
1658 Com_Error(ERR_FATAL, "VM_CompileX86: VirtualAlloc failed");
1659 #else
1660 vm->codeBase = malloc(compiledOfs);
1661 if(!vm->codeBase)
1662 Com_Error(ERR_FATAL, "VM_CompileX86: malloc failed");
1663 #endif
1664
1665 Com_Memcpy( vm->codeBase, buf, compiledOfs );
1666
1667 #ifdef VM_X86_MMAP
1668 if(mprotect(vm->codeBase, compiledOfs, PROT_READ|PROT_EXEC))
1669 Com_Error(ERR_FATAL, "VM_CompileX86: mprotect failed");
1670 #elif _WIN32
1671 {
1672 DWORD oldProtect = 0;
1673
1674 // remove write permissions.
1675 if(!VirtualProtect(vm->codeBase, compiledOfs, PAGE_EXECUTE_READ, &oldProtect))
1676 Com_Error(ERR_FATAL, "VM_CompileX86: VirtualProtect failed");
1677 }
1678 #endif
1679
1680 Z_Free( code );
1681 Z_Free( buf );
1682 Z_Free( jused );
1683 Com_Printf( "VM file %s compiled to %i bytes of code\n", vm->name, compiledOfs );
1684
1685 vm->destroy = VM_Destroy_Compiled;
1686
1687 // offset all the instruction pointers for the new location
1688 for ( i = 0 ; i < header->instructionCount ; i++ ) {
1689 vm->instructionPointers[i] += (intptr_t) vm->codeBase;
1690 }
1691 }
1692
VM_Destroy_Compiled(vm_t * self)1693 void VM_Destroy_Compiled(vm_t* self)
1694 {
1695 #ifdef VM_X86_MMAP
1696 munmap(self->codeBase, self->codeLength);
1697 #elif _WIN32
1698 VirtualFree(self->codeBase, 0, MEM_RELEASE);
1699 #else
1700 free(self->codeBase);
1701 #endif
1702 }
1703
1704 /*
1705 ==============
1706 VM_CallCompiled
1707
1708 This function is called directly by the generated code
1709 ==============
1710 */
1711
1712 #if defined(_MSC_VER) && defined(idx64)
1713 extern uint8_t qvmcall64(int *programStack, int *opStack, intptr_t *instructionPointers, byte *dataBase);
1714 #endif
1715
VM_CallCompiled(vm_t * vm,int * args)1716 int VM_CallCompiled(vm_t *vm, int *args)
1717 {
1718 byte stack[OPSTACK_SIZE + 15];
1719 void *entryPoint;
1720 int programStack, stackOnEntry;
1721 byte *image;
1722 int *opStack;
1723 int opStackOfs;
1724 int arg;
1725
1726 currentVM = vm;
1727
1728 // interpret the code
1729 vm->currentlyInterpreting = qtrue;
1730
1731 // we might be called recursively, so this might not be the very top
1732 programStack = stackOnEntry = vm->programStack;
1733
1734 // set up the stack frame
1735 image = vm->dataBase;
1736
1737 programStack -= ( 8 + 4 * MAX_VMMAIN_ARGS );
1738
1739 for ( arg = 0; arg < MAX_VMMAIN_ARGS; arg++ )
1740 *(int *)&image[ programStack + 8 + arg * 4 ] = args[ arg ];
1741
1742 *(int *)&image[ programStack + 4 ] = 0; // return stack
1743 *(int *)&image[ programStack ] = -1; // will terminate the loop on return
1744
1745 // off we go into generated code...
1746 entryPoint = vm->codeBase + vm->entryOfs;
1747 opStack = PADP(stack, 16);
1748 *opStack = 0xDEADBEEF;
1749 opStackOfs = 0;
1750
1751 #ifdef _MSC_VER
1752 #if idx64
1753 opStackOfs = qvmcall64(&programStack, opStack, vm->instructionPointers, vm->dataBase);
1754 #else
1755 __asm
1756 {
1757 pushad
1758
1759 mov esi, dword ptr programStack
1760 mov edi, dword ptr opStack
1761 mov ebx, dword ptr opStackOfs
1762
1763 call entryPoint
1764
1765 mov dword ptr opStackOfs, ebx
1766 mov dword ptr opStack, edi
1767 mov dword ptr programStack, esi
1768
1769 popad
1770 }
1771 #endif
1772 #elif idx64
1773 __asm__ volatile(
1774 "movq %5, %%rax\n"
1775 "movq %3, %%r8\n"
1776 "movq %4, %%r9\n"
1777 "push %%r15\n"
1778 "push %%r14\n"
1779 "push %%r13\n"
1780 "push %%r12\n"
1781 "callq *%%rax\n"
1782 "pop %%r12\n"
1783 "pop %%r13\n"
1784 "pop %%r14\n"
1785 "pop %%r15\n"
1786 : "+S" (programStack), "+D" (opStack), "+b" (opStackOfs)
1787 : "g" (vm->instructionPointers), "g" (vm->dataBase), "g" (entryPoint)
1788 : "cc", "memory", "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11"
1789 );
1790 #else
1791 __asm__ volatile(
1792 "calll *%3\n"
1793 : "+S" (programStack), "+D" (opStack), "+b" (opStackOfs)
1794 : "g" (entryPoint)
1795 : "cc", "memory", "%eax", "%ecx", "%edx"
1796 );
1797 #endif
1798
1799 if(opStackOfs != 1 || *opStack != 0xDEADBEEF)
1800 {
1801 Com_Error(ERR_DROP, "opStack corrupted in compiled code");
1802 }
1803 if(programStack != stackOnEntry - (8 + 4 * MAX_VMMAIN_ARGS))
1804 Com_Error(ERR_DROP, "programStack corrupted in compiled code");
1805
1806 vm->programStack = stackOnEntry;
1807
1808 return opStack[opStackOfs];
1809 }
1810