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