2011-02-18 14:31:32 +00:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
Quake III Arena source code is distributed in the hope that it will be
|
|
|
|
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with Quake III Arena source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
// vm_x86.c -- load time compiler and execution environment for x86
|
|
|
|
|
|
|
|
#include "vm_local.h"
|
2011-07-26 08:52:24 +00:00
|
|
|
|
2011-02-18 14:31:32 +00:00
|
|
|
#ifdef _WIN32
|
2011-12-10 00:15:42 +00:00
|
|
|
#include <windows.h>
|
|
|
|
#else
|
|
|
|
#ifdef __FreeBSD__
|
|
|
|
#include <sys/types.h>
|
|
|
|
#endif
|
2011-02-18 14:31:32 +00:00
|
|
|
|
2011-12-10 00:15:42 +00:00
|
|
|
#include <sys/mman.h> // for PROT_ stuff
|
2011-02-18 14:31:32 +00:00
|
|
|
|
2011-12-10 00:15:42 +00:00
|
|
|
/* need this on NX enabled systems (i386 with PAE kernel or
|
|
|
|
* noexec32=on x86_64) */
|
|
|
|
#define VM_X86_MMAP
|
|
|
|
|
|
|
|
// workaround for systems that use the old MAP_ANON macro
|
|
|
|
#ifndef MAP_ANONYMOUS
|
|
|
|
#define MAP_ANONYMOUS MAP_ANON
|
|
|
|
#endif
|
2011-02-18 14:31:32 +00:00
|
|
|
#endif
|
|
|
|
|
2023-03-03 04:26:22 +00:00
|
|
|
#if defined (__i386__) || defined(__x86_64__) || defined(_M_IX86) || defined(_M_X64)
|
2011-02-18 14:31:32 +00:00
|
|
|
static void VM_Destroy_Compiled(vm_t* self);
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
eax scratch
|
|
|
|
ebx/bl opStack offset
|
|
|
|
ecx scratch (required for shifts)
|
|
|
|
edx scratch (required for divisions)
|
|
|
|
esi program stack
|
|
|
|
edi opStack base
|
|
|
|
x86_64:
|
|
|
|
r8 vm->instructionPointers
|
|
|
|
r9 vm->dataBase
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
*/
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
#define VMFREE_BUFFERS() do {Z_Free(buf); Z_Free(jused);} while(0)
|
2011-02-18 14:31:32 +00:00
|
|
|
static byte *buf = NULL;
|
|
|
|
static byte *jused = NULL;
|
2011-07-26 08:52:24 +00:00
|
|
|
static int jusedSize = 0;
|
2011-02-18 14:31:32 +00:00
|
|
|
static int compiledOfs = 0;
|
|
|
|
static byte *code = NULL;
|
|
|
|
static int pc = 0;
|
|
|
|
|
|
|
|
#define FTOL_PTR
|
|
|
|
|
|
|
|
static int instruction, pass;
|
|
|
|
static int lastConst = 0;
|
|
|
|
static int oc0, oc1, pop0, pop1;
|
2011-07-26 08:52:24 +00:00
|
|
|
static int jlabel;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
LAST_COMMAND_NONE = 0,
|
2011-07-26 08:52:24 +00:00
|
|
|
LAST_COMMAND_MOV_STACK_EAX,
|
|
|
|
LAST_COMMAND_SUB_BL_1,
|
|
|
|
LAST_COMMAND_SUB_BL_2,
|
2011-02-18 14:31:32 +00:00
|
|
|
} ELastCommand;
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
VM_JMP_VIOLATION = 0,
|
|
|
|
VM_BLOCK_COPY = 1
|
|
|
|
} ESysCallType;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
static ELastCommand LastCommand;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
static int iss8(int32_t v)
|
|
|
|
{
|
|
|
|
return (SCHAR_MIN <= v && v <= SCHAR_MAX);
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
#if 0
|
|
|
|
static int isu8(uint32_t v)
|
|
|
|
{
|
|
|
|
return (v <= UCHAR_MAX);
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
static int NextConstant4(void)
|
2011-02-18 14:31:32 +00:00
|
|
|
{
|
2018-04-07 23:02:52 +00:00
|
|
|
return ((unsigned int)code[pc] | ((unsigned int)code[pc+1]<<8) | ((unsigned int)code[pc+2]<<16) | ((unsigned int)code[pc+3]<<24));
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int Constant4( void ) {
|
|
|
|
int v;
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
v = NextConstant4();
|
2011-02-18 14:31:32 +00:00
|
|
|
pc += 4;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int Constant1( void ) {
|
|
|
|
int v;
|
|
|
|
|
|
|
|
v = code[pc];
|
|
|
|
pc += 1;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Emit1( int v )
|
|
|
|
{
|
|
|
|
buf[ compiledOfs ] = v;
|
|
|
|
compiledOfs++;
|
|
|
|
|
|
|
|
LastCommand = LAST_COMMAND_NONE;
|
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
static void Emit2(int v)
|
|
|
|
{
|
|
|
|
Emit1(v & 255);
|
|
|
|
Emit1((v >> 8) & 255);
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
static void Emit4(int v)
|
|
|
|
{
|
|
|
|
Emit1(v & 0xFF);
|
|
|
|
Emit1((v >> 8) & 0xFF);
|
|
|
|
Emit1((v >> 16) & 0xFF);
|
|
|
|
Emit1((v >> 24) & 0xFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void EmitPtr(void *ptr)
|
|
|
|
{
|
|
|
|
intptr_t v = (intptr_t) ptr;
|
|
|
|
|
|
|
|
Emit4(v);
|
|
|
|
#if idx64
|
|
|
|
Emit1((v >> 32) & 0xFF);
|
|
|
|
Emit1((v >> 40) & 0xFF);
|
|
|
|
Emit1((v >> 48) & 0xFF);
|
|
|
|
Emit1((v >> 56) & 0xFF);
|
|
|
|
#endif
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int Hex( int c ) {
|
|
|
|
if ( c >= 'a' && c <= 'f' ) {
|
|
|
|
return 10 + c - 'a';
|
|
|
|
}
|
|
|
|
if ( c >= 'A' && c <= 'F' ) {
|
|
|
|
return 10 + c - 'A';
|
|
|
|
}
|
|
|
|
if ( c >= '0' && c <= '9' ) {
|
|
|
|
return c - '0';
|
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
VMFREE_BUFFERS();
|
2011-02-18 14:31:32 +00:00
|
|
|
Com_Error( ERR_DROP, "Hex: bad char '%c'", c );
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static void EmitString( const char *string ) {
|
|
|
|
int c1, c2;
|
|
|
|
int v;
|
|
|
|
|
|
|
|
while ( 1 ) {
|
|
|
|
c1 = string[0];
|
|
|
|
c2 = string[1];
|
|
|
|
|
|
|
|
v = ( Hex( c1 ) << 4 ) | Hex( c2 );
|
|
|
|
Emit1( v );
|
|
|
|
|
|
|
|
if ( !string[2] ) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
string += 3;
|
|
|
|
}
|
|
|
|
}
|
2011-07-26 08:52:24 +00:00
|
|
|
static void EmitRexString(byte rex, const char *string)
|
|
|
|
{
|
|
|
|
#if idx64
|
|
|
|
if(rex)
|
|
|
|
Emit1(rex);
|
|
|
|
#endif
|
2011-02-18 14:31:32 +00:00
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString(string);
|
|
|
|
}
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
#define MASK_REG(modrm, mask) \
|
2017-07-10 01:33:41 +00:00
|
|
|
do { \
|
|
|
|
EmitString("81"); \
|
|
|
|
EmitString((modrm)); \
|
|
|
|
Emit4((mask)); \
|
|
|
|
} while(0)
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
// add bl, bytes
|
|
|
|
#define STACK_PUSH(bytes) \
|
2017-07-10 01:33:41 +00:00
|
|
|
do { \
|
|
|
|
EmitString("80 C3"); \
|
|
|
|
Emit1(bytes); \
|
|
|
|
} while(0)
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
// sub bl, bytes
|
|
|
|
#define STACK_POP(bytes) \
|
2017-07-10 01:33:41 +00:00
|
|
|
do { \
|
|
|
|
EmitString("80 EB"); \
|
|
|
|
Emit1(bytes); \
|
|
|
|
} while(0)
|
2011-07-26 08:52:24 +00:00
|
|
|
|
2011-02-18 14:31:32 +00:00
|
|
|
static void EmitCommand(ELastCommand command)
|
|
|
|
{
|
|
|
|
switch(command)
|
|
|
|
{
|
2011-07-26 08:52:24 +00:00
|
|
|
case LAST_COMMAND_MOV_STACK_EAX:
|
|
|
|
EmitString("89 04 9F"); // mov dword ptr [edi + ebx * 4], eax
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
case LAST_COMMAND_SUB_BL_1:
|
|
|
|
STACK_POP(1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
case LAST_COMMAND_SUB_BL_2:
|
|
|
|
STACK_POP(2); // sub bl, 2
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
LastCommand = command;
|
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
static void EmitPushStack(vm_t *vm)
|
|
|
|
{
|
|
|
|
if (!jlabel)
|
|
|
|
{
|
|
|
|
if(LastCommand == LAST_COMMAND_SUB_BL_1)
|
|
|
|
{ // sub bl, 1
|
|
|
|
compiledOfs -= 3;
|
|
|
|
vm->instructionPointers[instruction - 1] = compiledOfs;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(LastCommand == LAST_COMMAND_SUB_BL_2)
|
|
|
|
{ // sub bl, 2
|
|
|
|
compiledOfs -= 3;
|
|
|
|
vm->instructionPointers[instruction - 1] = compiledOfs;
|
|
|
|
STACK_POP(1); // sub bl, 1
|
|
|
|
return;
|
|
|
|
}
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
STACK_PUSH(1); // add bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
static void EmitMovEAXStack(vm_t *vm, int andit)
|
|
|
|
{
|
|
|
|
if(!jlabel)
|
|
|
|
{
|
|
|
|
if(LastCommand == LAST_COMMAND_MOV_STACK_EAX)
|
|
|
|
{ // mov [edi + ebx * 4], eax
|
|
|
|
compiledOfs -= 3;
|
|
|
|
vm->instructionPointers[instruction - 1] = compiledOfs;
|
|
|
|
}
|
|
|
|
else if(pop1 == OP_CONST && buf[compiledOfs-7] == 0xC7 && buf[compiledOfs-6] == 0x04 && buf[compiledOfs - 5] == 0x9F)
|
|
|
|
{ // mov [edi + ebx * 4], 0x12345678
|
|
|
|
compiledOfs -= 7;
|
|
|
|
vm->instructionPointers[instruction - 1] = compiledOfs;
|
|
|
|
EmitString("B8"); // mov eax, 0x12345678
|
|
|
|
|
|
|
|
if(andit)
|
|
|
|
Emit4(lastConst & andit);
|
|
|
|
else
|
|
|
|
Emit4(lastConst);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else if(pop1 != OP_DIVI && pop1 != OP_DIVU && pop1 != OP_MULI && pop1 != OP_MULU &&
|
|
|
|
pop1 != OP_STORE4 && pop1 != OP_STORE2 && pop1 != OP_STORE1)
|
|
|
|
{
|
|
|
|
EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
|
|
|
|
}
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
2011-07-26 08:52:24 +00:00
|
|
|
else
|
|
|
|
EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
|
|
|
|
|
|
|
|
if(andit)
|
|
|
|
{
|
|
|
|
EmitString("25"); // and eax, 0x12345678
|
|
|
|
Emit4(andit);
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
void EmitMovECXStack(vm_t *vm)
|
|
|
|
{
|
|
|
|
if(!jlabel)
|
|
|
|
{
|
|
|
|
if(LastCommand == LAST_COMMAND_MOV_STACK_EAX) // mov [edi + ebx * 4], eax
|
|
|
|
{
|
|
|
|
compiledOfs -= 3;
|
|
|
|
vm->instructionPointers[instruction - 1] = compiledOfs;
|
|
|
|
EmitString("89 C1"); // mov ecx, eax
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if(pop1 == OP_DIVI || pop1 == OP_DIVU || pop1 == OP_MULI || pop1 == OP_MULU ||
|
|
|
|
pop1 == OP_STORE4 || pop1 == OP_STORE2 || pop1 == OP_STORE1)
|
|
|
|
{
|
|
|
|
EmitString("89 C1"); // mov ecx, eax
|
|
|
|
return;
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("8B 0C 9F"); // mov ecx, dword ptr [edi + ebx * 4]
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void EmitMovEDXStack(vm_t *vm, int andit)
|
|
|
|
{
|
|
|
|
if(!jlabel)
|
|
|
|
{
|
|
|
|
if(LastCommand == LAST_COMMAND_MOV_STACK_EAX)
|
|
|
|
{ // mov dword ptr [edi + ebx * 4], eax
|
|
|
|
compiledOfs -= 3;
|
|
|
|
vm->instructionPointers[instruction - 1] = compiledOfs;
|
|
|
|
|
|
|
|
EmitString("8B D0"); // mov edx, eax
|
|
|
|
}
|
|
|
|
else if(pop1 == OP_DIVI || pop1 == OP_DIVU || pop1 == OP_MULI || pop1 == OP_MULU ||
|
|
|
|
pop1 == OP_STORE4 || pop1 == OP_STORE2 || pop1 == OP_STORE1)
|
|
|
|
{
|
|
|
|
EmitString("8B D0"); // mov edx, eax
|
|
|
|
}
|
|
|
|
else if(pop1 == OP_CONST && buf[compiledOfs-7] == 0xC7 && buf[compiledOfs-6] == 0x07 && buf[compiledOfs - 5] == 0x9F)
|
|
|
|
{ // mov dword ptr [edi + ebx * 4], 0x12345678
|
|
|
|
compiledOfs -= 7;
|
|
|
|
vm->instructionPointers[instruction - 1] = compiledOfs;
|
|
|
|
EmitString("BA"); // mov edx, 0x12345678
|
|
|
|
|
|
|
|
if(andit)
|
|
|
|
Emit4(lastConst & andit);
|
|
|
|
else
|
|
|
|
Emit4(lastConst);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
|
|
|
|
|
|
|
|
}
|
|
|
|
else
|
|
|
|
EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
|
|
|
|
|
|
|
|
if(andit)
|
|
|
|
MASK_REG("E2", andit); // and edx, 0x12345678
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define JUSED(x) \
|
|
|
|
do { \
|
2011-07-26 08:52:24 +00:00
|
|
|
if (x < 0 || x >= vm->instructionCount) { \
|
|
|
|
VMFREE_BUFFERS(); \
|
2011-02-18 14:31:32 +00:00
|
|
|
Com_Error( ERR_DROP, \
|
|
|
|
"VM_CompileX86: jump target out of range at offset %d", pc ); \
|
|
|
|
} \
|
|
|
|
jused[x] = 1; \
|
|
|
|
} while(0)
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
#define SET_JMPOFS(x) do { buf[(x)] = compiledOfs - ((x) + 1); } while(0)
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
ErrJump
|
|
|
|
Error handler for jump/call to invalid instruction number
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
2017-07-10 01:33:41 +00:00
|
|
|
static void __attribute__((__noreturn__)) ErrJump(void)
|
2011-07-26 08:52:24 +00:00
|
|
|
{
|
|
|
|
Com_Error(ERR_DROP, "program tried to execute code outside VM");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
DoSyscall
|
2012-09-15 03:03:44 +00:00
|
|
|
|
|
|
|
Assembler helper routines will write its arguments directly to global variables so as to
|
|
|
|
work around different calling conventions
|
2011-07-26 08:52:24 +00:00
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
2012-09-15 03:03:44 +00:00
|
|
|
int vm_syscallNum;
|
|
|
|
int vm_programStack;
|
|
|
|
int *vm_opStackBase;
|
|
|
|
uint8_t vm_opStackOfs;
|
|
|
|
intptr_t vm_arg;
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
static void DoSyscall(void)
|
|
|
|
{
|
|
|
|
vm_t *savedVM;
|
|
|
|
|
|
|
|
// save currentVM so as to allow for recursive VM entry
|
|
|
|
savedVM = currentVM;
|
|
|
|
// modify VM stack pointer for recursive VM entry
|
2012-09-15 03:03:44 +00:00
|
|
|
currentVM->programStack = vm_programStack - 4;
|
2011-07-26 08:52:24 +00:00
|
|
|
|
2012-09-15 03:03:44 +00:00
|
|
|
if(vm_syscallNum < 0)
|
2011-07-26 08:52:24 +00:00
|
|
|
{
|
2017-07-10 01:33:41 +00:00
|
|
|
int *data, *ret;
|
2011-07-26 08:52:24 +00:00
|
|
|
#if idx64
|
|
|
|
int index;
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
intptr_t args[MAX_VMSYSCALL_ARGS];
|
2011-07-26 08:52:24 +00:00
|
|
|
#endif
|
|
|
|
|
2012-09-15 03:03:44 +00:00
|
|
|
data = (int *) (savedVM->dataBase + vm_programStack + 4);
|
2017-07-10 01:33:41 +00:00
|
|
|
ret = &vm_opStackBase[vm_opStackOfs + 1];
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
#if idx64
|
2012-09-15 03:03:44 +00:00
|
|
|
args[0] = ~vm_syscallNum;
|
2011-07-26 08:52:24 +00:00
|
|
|
for(index = 1; index < ARRAY_LEN(args); index++)
|
|
|
|
args[index] = data[index];
|
|
|
|
|
2017-07-10 01:33:41 +00:00
|
|
|
*ret = savedVM->systemCall(args);
|
2011-07-26 08:52:24 +00:00
|
|
|
#else
|
2012-09-15 03:03:44 +00:00
|
|
|
data[0] = ~vm_syscallNum;
|
2017-07-10 01:33:41 +00:00
|
|
|
*ret = savedVM->systemCall((intptr_t *) data);
|
2011-07-26 08:52:24 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-09-15 03:03:44 +00:00
|
|
|
switch(vm_syscallNum)
|
2011-07-26 08:52:24 +00:00
|
|
|
{
|
|
|
|
case VM_JMP_VIOLATION:
|
|
|
|
ErrJump();
|
|
|
|
break;
|
|
|
|
case VM_BLOCK_COPY:
|
2012-09-15 03:03:44 +00:00
|
|
|
if(vm_opStackOfs < 1)
|
2011-07-26 08:52:24 +00:00
|
|
|
Com_Error(ERR_DROP, "VM_BLOCK_COPY failed due to corrupted opStack");
|
|
|
|
|
2012-09-15 03:03:44 +00:00
|
|
|
VM_BlockCopy(vm_opStackBase[(vm_opStackOfs - 1)], vm_opStackBase[vm_opStackOfs], vm_arg);
|
2011-07-26 08:52:24 +00:00
|
|
|
break;
|
|
|
|
default:
|
2012-09-15 03:03:44 +00:00
|
|
|
Com_Error(ERR_DROP, "Unknown VM operation %d", vm_syscallNum);
|
2011-07-26 08:52:24 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
currentVM = savedVM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
EmitCallRel
|
|
|
|
Relative call to vm->codeBase + callOfs
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void EmitCallRel(vm_t *vm, int callOfs)
|
|
|
|
{
|
|
|
|
EmitString("E8"); // call 0x12345678
|
|
|
|
Emit4(callOfs - compiledOfs - 4);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
EmitCallDoSyscall
|
|
|
|
Call to DoSyscall()
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
|
|
|
int EmitCallDoSyscall(vm_t *vm)
|
|
|
|
{
|
|
|
|
// use edx register to store DoSyscall address
|
|
|
|
EmitRexString(0x48, "BA"); // mov edx, DoSyscall
|
|
|
|
EmitPtr(DoSyscall);
|
|
|
|
|
|
|
|
// Push important registers to stack as we can't really make
|
|
|
|
// any assumptions about calling conventions.
|
|
|
|
EmitString("51"); // push ebx
|
|
|
|
EmitString("56"); // push esi
|
|
|
|
EmitString("57"); // push edi
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "50"); // push r8
|
|
|
|
EmitRexString(0x41, "51"); // push r9
|
|
|
|
#endif
|
|
|
|
|
2012-09-15 03:03:44 +00:00
|
|
|
// write arguments to global vars
|
|
|
|
// syscall number
|
|
|
|
EmitString("A3"); // mov [0x12345678], eax
|
|
|
|
EmitPtr(&vm_syscallNum);
|
|
|
|
// vm_programStack value
|
|
|
|
EmitString("89 F0"); // mov eax, esi
|
|
|
|
EmitString("A3"); // mov [0x12345678], eax
|
|
|
|
EmitPtr(&vm_programStack);
|
|
|
|
// vm_opStackOfs
|
|
|
|
EmitString("88 D8"); // mov al, bl
|
|
|
|
EmitString("A2"); // mov [0x12345678], al
|
|
|
|
EmitPtr(&vm_opStackOfs);
|
|
|
|
// vm_opStackBase
|
|
|
|
EmitRexString(0x48, "89 F8"); // mov eax, edi
|
|
|
|
EmitRexString(0x48, "A3"); // mov [0x12345678], eax
|
|
|
|
EmitPtr(&vm_opStackBase);
|
|
|
|
// vm_arg
|
|
|
|
EmitString("89 C8"); // mov eax, ecx
|
|
|
|
EmitString("A3"); // mov [0x12345678], eax
|
|
|
|
EmitPtr(&vm_arg);
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
// align the stack pointer to a 16-byte-boundary
|
|
|
|
EmitString("55"); // push ebp
|
|
|
|
EmitRexString(0x48, "89 E5"); // mov ebp, esp
|
|
|
|
EmitRexString(0x48, "83 E4 F0"); // and esp, 0xFFFFFFF0
|
|
|
|
|
|
|
|
// call the syscall wrapper function DoSyscall()
|
|
|
|
|
|
|
|
EmitString("FF D2"); // call edx
|
|
|
|
|
|
|
|
// reset the stack pointer to its previous value
|
|
|
|
EmitRexString(0x48, "89 EC"); // mov esp, ebp
|
|
|
|
EmitString("5D"); // pop ebp
|
|
|
|
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "59"); // pop r9
|
|
|
|
EmitRexString(0x41, "58"); // pop r8
|
|
|
|
#endif
|
|
|
|
EmitString("5F"); // pop edi
|
|
|
|
EmitString("5E"); // pop esi
|
|
|
|
EmitString("59"); // pop ebx
|
|
|
|
|
|
|
|
EmitString("C3"); // ret
|
|
|
|
|
|
|
|
return compiledOfs;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
EmitCallErrJump
|
|
|
|
Emit the code that triggers execution of the jump violation handler
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void EmitCallErrJump(vm_t *vm, int sysCallOfs)
|
|
|
|
{
|
|
|
|
EmitString("B8"); // mov eax, 0x12345678
|
|
|
|
Emit4(VM_JMP_VIOLATION);
|
|
|
|
|
|
|
|
EmitCallRel(vm, sysCallOfs);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
EmitCallProcedure
|
|
|
|
VM OP_CALL procedure for call destinations obtained at runtime
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
|
|
|
int EmitCallProcedure(vm_t *vm, int sysCallOfs)
|
|
|
|
{
|
|
|
|
int jmpSystemCall, jmpBadAddr;
|
|
|
|
int retval;
|
|
|
|
|
|
|
|
EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
|
|
|
|
STACK_POP(1); // sub bl, 1
|
|
|
|
EmitString("85 C0"); // test eax, eax
|
|
|
|
|
|
|
|
// Jump to syscall code, 1 byte offset should suffice
|
|
|
|
EmitString("7C"); // jl systemCall
|
|
|
|
jmpSystemCall = compiledOfs++;
|
|
|
|
|
|
|
|
/************ Call inside VM ************/
|
|
|
|
|
|
|
|
EmitString("81 F8"); // cmp eax, vm->instructionCount
|
|
|
|
Emit4(vm->instructionCount);
|
|
|
|
|
|
|
|
// Error jump if invalid jump target
|
|
|
|
EmitString("73"); // jae badAddr
|
|
|
|
jmpBadAddr = compiledOfs++;
|
|
|
|
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x49, "FF 14 C0"); // call qword ptr [r8 + eax * 8]
|
|
|
|
#else
|
|
|
|
EmitString("FF 14 85"); // call dword ptr [vm->instructionPointers + eax * 4]
|
|
|
|
Emit4((intptr_t) vm->instructionPointers);
|
|
|
|
#endif
|
|
|
|
EmitString("8B 04 9F"); // mov eax, dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("C3"); // ret
|
|
|
|
|
|
|
|
// badAddr:
|
|
|
|
SET_JMPOFS(jmpBadAddr);
|
|
|
|
EmitCallErrJump(vm, sysCallOfs);
|
|
|
|
|
|
|
|
/************ System Call ************/
|
|
|
|
|
|
|
|
// systemCall:
|
|
|
|
SET_JMPOFS(jmpSystemCall);
|
|
|
|
retval = compiledOfs;
|
|
|
|
|
|
|
|
EmitCallRel(vm, sysCallOfs);
|
|
|
|
|
|
|
|
// have opStack reg point at return value
|
|
|
|
STACK_PUSH(1); // add bl, 1
|
|
|
|
EmitString("C3"); // ret
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
EmitJumpIns
|
|
|
|
Jump to constant instruction number
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void EmitJumpIns(vm_t *vm, const char *jmpop, int cdest)
|
|
|
|
{
|
|
|
|
JUSED(cdest);
|
|
|
|
|
|
|
|
EmitString(jmpop); // j??? 0x12345678
|
|
|
|
|
|
|
|
// we only know all the jump addresses in the third pass
|
|
|
|
if(pass == 2)
|
|
|
|
Emit4(vm->instructionPointers[cdest] - compiledOfs - 4);
|
|
|
|
else
|
|
|
|
compiledOfs += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
EmitCallIns
|
|
|
|
Call to constant instruction number
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void EmitCallIns(vm_t *vm, int cdest)
|
|
|
|
{
|
|
|
|
JUSED(cdest);
|
|
|
|
|
|
|
|
EmitString("E8"); // call 0x12345678
|
|
|
|
|
|
|
|
// we only know all the jump addresses in the third pass
|
|
|
|
if(pass == 2)
|
|
|
|
Emit4(vm->instructionPointers[cdest] - compiledOfs - 4);
|
|
|
|
else
|
|
|
|
compiledOfs += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
EmitCallConst
|
|
|
|
Call to constant instruction number or syscall
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
|
|
|
void EmitCallConst(vm_t *vm, int cdest, int callProcOfsSyscall)
|
|
|
|
{
|
|
|
|
if(cdest < 0)
|
|
|
|
{
|
|
|
|
EmitString("B8"); // mov eax, cdest
|
|
|
|
Emit4(cdest);
|
|
|
|
|
|
|
|
EmitCallRel(vm, callProcOfsSyscall);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
EmitCallIns(vm, cdest);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
EmitBranchConditions
|
|
|
|
Emits x86 branch condition as given in op
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
void EmitBranchConditions(vm_t *vm, int op)
|
|
|
|
{
|
|
|
|
switch(op)
|
|
|
|
{
|
|
|
|
case OP_EQ:
|
|
|
|
EmitJumpIns(vm, "0F 84", Constant4()); // je 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_NE:
|
|
|
|
EmitJumpIns(vm, "0F 85", Constant4()); // jne 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_LTI:
|
|
|
|
EmitJumpIns(vm, "0F 8C", Constant4()); // jl 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_LEI:
|
|
|
|
EmitJumpIns(vm, "0F 8E", Constant4()); // jle 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_GTI:
|
|
|
|
EmitJumpIns(vm, "0F 8F", Constant4()); // jg 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_GEI:
|
|
|
|
EmitJumpIns(vm, "0F 8D", Constant4()); // jge 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_LTU:
|
|
|
|
EmitJumpIns(vm, "0F 82", Constant4()); // jb 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_LEU:
|
|
|
|
EmitJumpIns(vm, "0F 86", Constant4()); // jbe 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_GTU:
|
|
|
|
EmitJumpIns(vm, "0F 87", Constant4()); // ja 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_GEU:
|
|
|
|
EmitJumpIns(vm, "0F 83", Constant4()); // jae 0x12345678
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
=================
|
|
|
|
ConstOptimize
|
|
|
|
Constant values for immediately following instructions may be translated to immediate values
|
|
|
|
instead of opStack operations, which will save expensive operations on memory
|
|
|
|
=================
|
|
|
|
*/
|
|
|
|
|
|
|
|
qboolean ConstOptimize(vm_t *vm, int callProcOfsSyscall)
|
|
|
|
{
|
|
|
|
int v;
|
|
|
|
int op1;
|
|
|
|
|
|
|
|
// we can safely perform optimizations only in case if
|
|
|
|
// we are 100% sure that next instruction is not a jump label
|
|
|
|
if (vm->jumpTableTargets && !jused[instruction])
|
|
|
|
op1 = code[pc+4];
|
|
|
|
else
|
|
|
|
return qfalse;
|
|
|
|
|
|
|
|
switch ( op1 ) {
|
|
|
|
|
|
|
|
case OP_LOAD4:
|
|
|
|
EmitPushStack(vm);
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "8B 81"); // mov eax, dword ptr [r9 + 0x12345678]
|
|
|
|
Emit4(Constant4() & vm->dataMask);
|
|
|
|
#else
|
|
|
|
EmitString("B8"); // mov eax, 0x12345678
|
|
|
|
EmitPtr(vm->dataBase + (Constant4() & vm->dataMask));
|
|
|
|
EmitString("8B 00"); // mov eax, dword ptr [eax]
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
|
|
|
|
|
|
|
pc++; // OP_LOAD4
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_LOAD2:
|
|
|
|
EmitPushStack(vm);
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "0F B7 81"); // movzx eax, word ptr [r9 + 0x12345678]
|
|
|
|
Emit4(Constant4() & vm->dataMask);
|
|
|
|
#else
|
|
|
|
EmitString("B8"); // mov eax, 0x12345678
|
|
|
|
EmitPtr(vm->dataBase + (Constant4() & vm->dataMask));
|
|
|
|
EmitString("0F B7 00"); // movzx eax, word ptr [eax]
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
|
|
|
|
|
|
|
pc++; // OP_LOAD2
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_LOAD1:
|
|
|
|
EmitPushStack(vm);
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "0F B6 81"); // movzx eax, byte ptr [r9 + 0x12345678]
|
|
|
|
Emit4(Constant4() & vm->dataMask);
|
|
|
|
#else
|
|
|
|
EmitString("B8"); // mov eax, 0x12345678
|
|
|
|
EmitPtr(vm->dataBase + (Constant4() & vm->dataMask));
|
|
|
|
EmitString("0F B6 00"); // movzx eax, byte ptr [eax]
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
|
|
|
|
|
|
|
pc++; // OP_LOAD1
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_STORE4:
|
2017-07-10 01:33:41 +00:00
|
|
|
EmitMovEAXStack(vm, vm->dataMask);
|
2011-07-26 08:52:24 +00:00
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "C7 04 01"); // mov dword ptr [r9 + eax], 0x12345678
|
|
|
|
Emit4(Constant4());
|
|
|
|
#else
|
|
|
|
EmitString("C7 80"); // mov dword ptr [eax + 0x12345678], 0x12345678
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
Emit4(Constant4());
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
|
|
|
pc++; // OP_STORE4
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_STORE2:
|
2017-07-10 01:33:41 +00:00
|
|
|
EmitMovEAXStack(vm, vm->dataMask);
|
2011-07-26 08:52:24 +00:00
|
|
|
#if idx64
|
|
|
|
Emit1(0x66); // mov word ptr [r9 + eax], 0x1234
|
|
|
|
EmitRexString(0x41, "C7 04 01");
|
|
|
|
Emit2(Constant4());
|
|
|
|
#else
|
|
|
|
EmitString("66 C7 80"); // mov word ptr [eax + 0x12345678], 0x1234
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
Emit2(Constant4());
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
|
|
|
|
|
|
|
pc++; // OP_STORE2
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_STORE1:
|
|
|
|
EmitMovEAXStack(vm, vm->dataMask);
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "C6 04 01"); // mov byte [r9 + eax], 0x12
|
|
|
|
Emit1(Constant4());
|
|
|
|
#else
|
|
|
|
EmitString("C6 80"); // mov byte ptr [eax + 0x12345678], 0x12
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
Emit1(Constant4());
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
|
|
|
|
|
|
|
pc++; // OP_STORE1
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_ADD:
|
|
|
|
v = Constant4();
|
|
|
|
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
if(iss8(v))
|
|
|
|
{
|
|
|
|
EmitString("83 C0"); // add eax, 0x7F
|
|
|
|
Emit1(v);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EmitString("05"); // add eax, 0x12345678
|
|
|
|
Emit4(v);
|
|
|
|
}
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
|
|
|
|
|
|
|
|
pc++; // OP_ADD
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_SUB:
|
|
|
|
v = Constant4();
|
|
|
|
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
if(iss8(v))
|
|
|
|
{
|
|
|
|
EmitString("83 E8"); // sub eax, 0x7F
|
|
|
|
Emit1(v);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EmitString("2D"); // sub eax, 0x12345678
|
|
|
|
Emit4(v);
|
|
|
|
}
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
|
|
|
|
|
|
|
|
pc++; // OP_SUB
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_MULI:
|
|
|
|
v = Constant4();
|
|
|
|
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
if(iss8(v))
|
|
|
|
{
|
|
|
|
EmitString("6B C0"); // imul eax, 0x7F
|
|
|
|
Emit1(v);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EmitString("69 C0"); // imul eax, 0x12345678
|
|
|
|
Emit4(v);
|
|
|
|
}
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
|
|
|
|
pc++; // OP_MULI
|
|
|
|
instruction += 1;
|
|
|
|
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_LSH:
|
|
|
|
v = NextConstant4();
|
|
|
|
if(v < 0 || v > 31)
|
|
|
|
break;
|
|
|
|
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
EmitString("C1 E0"); // shl eax, 0x12
|
|
|
|
Emit1(v);
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
|
|
|
|
|
|
|
|
pc += 5; // CONST + OP_LSH
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_RSHI:
|
|
|
|
v = NextConstant4();
|
|
|
|
if(v < 0 || v > 31)
|
|
|
|
break;
|
|
|
|
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
EmitString("C1 F8"); // sar eax, 0x12
|
|
|
|
Emit1(v);
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
|
|
|
|
|
|
|
|
pc += 5; // CONST + OP_RSHI
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_RSHU:
|
|
|
|
v = NextConstant4();
|
|
|
|
if(v < 0 || v > 31)
|
|
|
|
break;
|
|
|
|
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
EmitString("C1 E8"); // shr eax, 0x12
|
|
|
|
Emit1(v);
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
|
|
|
|
|
|
|
|
pc += 5; // CONST + OP_RSHU
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_BAND:
|
|
|
|
v = Constant4();
|
|
|
|
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
if(iss8(v))
|
|
|
|
{
|
|
|
|
EmitString("83 E0"); // and eax, 0x7F
|
|
|
|
Emit1(v);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EmitString("25"); // and eax, 0x12345678
|
|
|
|
Emit4(v);
|
|
|
|
}
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
|
|
|
|
|
|
|
|
pc += 1; // OP_BAND
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_BOR:
|
|
|
|
v = Constant4();
|
|
|
|
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
if(iss8(v))
|
|
|
|
{
|
|
|
|
EmitString("83 C8"); // or eax, 0x7F
|
|
|
|
Emit1(v);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EmitString("0D"); // or eax, 0x12345678
|
|
|
|
Emit4(v);
|
|
|
|
}
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
|
|
|
|
|
|
|
|
pc += 1; // OP_BOR
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_BXOR:
|
|
|
|
v = Constant4();
|
|
|
|
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
if(iss8(v))
|
|
|
|
{
|
|
|
|
EmitString("83 F0"); // xor eax, 0x7F
|
|
|
|
Emit1(v);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EmitString("35"); // xor eax, 0x12345678
|
|
|
|
Emit4(v);
|
|
|
|
}
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
|
|
|
|
|
|
|
|
pc += 1; // OP_BXOR
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_EQ:
|
|
|
|
case OP_NE:
|
|
|
|
case OP_LTI:
|
|
|
|
case OP_LEI:
|
|
|
|
case OP_GTI:
|
|
|
|
case OP_GEI:
|
|
|
|
case OP_LTU:
|
|
|
|
case OP_LEU:
|
|
|
|
case OP_GTU:
|
|
|
|
case OP_GEU:
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1);
|
|
|
|
EmitString("3D"); // cmp eax, 0x12345678
|
|
|
|
Emit4(Constant4());
|
|
|
|
|
|
|
|
pc++; // OP_*
|
|
|
|
EmitBranchConditions(vm, op1);
|
|
|
|
instruction++;
|
|
|
|
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_EQF:
|
|
|
|
case OP_NEF:
|
|
|
|
if(NextConstant4())
|
|
|
|
break;
|
|
|
|
pc += 5; // CONST + OP_EQF|OP_NEF
|
|
|
|
|
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1);
|
|
|
|
// floating point hack :)
|
|
|
|
EmitString("25"); // and eax, 0x7FFFFFFF
|
|
|
|
Emit4(0x7FFFFFFF);
|
|
|
|
if(op1 == OP_EQF)
|
|
|
|
EmitJumpIns(vm, "0F 84", Constant4()); // jz 0x12345678
|
|
|
|
else
|
|
|
|
EmitJumpIns(vm, "0F 85", Constant4()); // jnz 0x12345678
|
|
|
|
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
|
|
|
|
case OP_JUMP:
|
|
|
|
EmitJumpIns(vm, "E9", Constant4()); // jmp 0x12345678
|
|
|
|
|
|
|
|
pc += 1; // OP_JUMP
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
case OP_CALL:
|
|
|
|
v = Constant4();
|
|
|
|
EmitCallConst(vm, v, callProcOfsSyscall);
|
|
|
|
|
|
|
|
pc += 1; // OP_CALL
|
|
|
|
instruction += 1;
|
|
|
|
return qtrue;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return qfalse;
|
|
|
|
}
|
|
|
|
|
2011-02-18 14:31:32 +00:00
|
|
|
/*
|
|
|
|
=================
|
|
|
|
VM_Compile
|
|
|
|
=================
|
|
|
|
*/
|
2011-07-26 08:52:24 +00:00
|
|
|
void VM_Compile(vm_t *vm, vmHeader_t *header)
|
|
|
|
{
|
2011-02-18 14:31:32 +00:00
|
|
|
int op;
|
|
|
|
int maxLength;
|
|
|
|
int v;
|
|
|
|
int i;
|
2011-07-26 08:52:24 +00:00
|
|
|
int callProcOfsSyscall, callProcOfs, callDoSyscallOfs;
|
|
|
|
|
|
|
|
jusedSize = header->instructionCount + 2;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
// allocate a very large temp buffer, we will shrink it later
|
2011-07-26 08:52:24 +00:00
|
|
|
maxLength = header->codeLength * 8 + 64;
|
|
|
|
buf = Z_Malloc(maxLength);
|
2011-02-18 14:31:32 +00:00
|
|
|
jused = Z_Malloc(jusedSize);
|
2011-07-26 08:52:24 +00:00
|
|
|
code = Z_Malloc(header->codeLength+32);
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
Com_Memset(jused, 0, jusedSize);
|
2011-07-26 08:52:24 +00:00
|
|
|
Com_Memset(buf, 0, maxLength);
|
|
|
|
|
|
|
|
// copy code in larger buffer and put some zeros at the end
|
|
|
|
// so we can safely look ahead for a few instructions in it
|
|
|
|
// without a chance to get false-positive because of some garbage bytes
|
|
|
|
Com_Memset(code, 0, header->codeLength+32);
|
|
|
|
Com_Memcpy(code, (byte *)header + header->codeOffset, header->codeLength );
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
// ensure that the optimisation pass knows about all the jump
|
|
|
|
// table targets
|
2017-07-10 01:33:41 +00:00
|
|
|
pc = -1; // a bogus value to be printed in out-of-bounds error messages
|
2011-02-18 14:31:32 +00:00
|
|
|
for( i = 0; i < vm->numJumpTableTargets; i++ ) {
|
2017-07-10 01:33:41 +00:00
|
|
|
JUSED( *(int *)(vm->jumpTableTargets + ( i * sizeof( int ) ) ) );
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
// Start buffer with x86-VM specific procedures
|
|
|
|
compiledOfs = 0;
|
|
|
|
|
|
|
|
callDoSyscallOfs = compiledOfs;
|
|
|
|
callProcOfs = EmitCallDoSyscall(vm);
|
|
|
|
callProcOfsSyscall = EmitCallProcedure(vm, callDoSyscallOfs);
|
|
|
|
vm->entryOfs = compiledOfs;
|
|
|
|
|
|
|
|
for(pass=0; pass < 3; pass++) {
|
2011-02-18 14:31:32 +00:00
|
|
|
oc0 = -23423;
|
|
|
|
oc1 = -234354;
|
|
|
|
pop0 = -43435;
|
|
|
|
pop1 = -545455;
|
|
|
|
|
|
|
|
// translate all instructions
|
|
|
|
pc = 0;
|
|
|
|
instruction = 0;
|
2011-07-26 08:52:24 +00:00
|
|
|
//code = (byte *)header + header->codeOffset;
|
|
|
|
compiledOfs = vm->entryOfs;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
LastCommand = LAST_COMMAND_NONE;
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
while(instruction < header->instructionCount)
|
|
|
|
{
|
|
|
|
if(compiledOfs > maxLength - 16)
|
|
|
|
{
|
|
|
|
VMFREE_BUFFERS();
|
|
|
|
Com_Error(ERR_DROP, "VM_CompileX86: maxLength exceeded");
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vm->instructionPointers[ instruction ] = compiledOfs;
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
if ( !vm->jumpTableTargets )
|
|
|
|
jlabel = 1;
|
|
|
|
else
|
|
|
|
jlabel = jused[ instruction ];
|
|
|
|
|
2011-02-18 14:31:32 +00:00
|
|
|
instruction++;
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
if(pc > header->codeLength)
|
|
|
|
{
|
|
|
|
VMFREE_BUFFERS();
|
|
|
|
Com_Error(ERR_DROP, "VM_CompileX86: pc > header->codeLength");
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
op = code[ pc ];
|
|
|
|
pc++;
|
|
|
|
switch ( op ) {
|
|
|
|
case 0:
|
|
|
|
break;
|
|
|
|
case OP_BREAK:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("CC"); // int 3
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_ENTER:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("81 EE"); // sub esi, 0x12345678
|
|
|
|
Emit4(Constant4());
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_CONST:
|
2011-07-26 08:52:24 +00:00
|
|
|
if(ConstOptimize(vm, callProcOfsSyscall))
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
EmitPushStack(vm);
|
|
|
|
EmitString("C7 04 9F"); // mov dword ptr [edi + ebx * 4], 0x12345678
|
2011-02-18 14:31:32 +00:00
|
|
|
lastConst = Constant4();
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
Emit4(lastConst);
|
|
|
|
if(code[pc] == OP_JUMP)
|
2011-02-18 14:31:32 +00:00
|
|
|
JUSED(lastConst);
|
2011-07-26 08:52:24 +00:00
|
|
|
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_LOCAL:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitPushStack(vm);
|
|
|
|
EmitString("8D 86"); // lea eax, [0x12345678 + esi]
|
2011-02-18 14:31:32 +00:00
|
|
|
oc0 = oc1;
|
|
|
|
oc1 = Constant4();
|
2011-07-26 08:52:24 +00:00
|
|
|
Emit4(oc1);
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_ARG:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("8B D6"); // mov edx, esi
|
|
|
|
EmitString("81 C2"); // add edx, 0x12345678
|
|
|
|
Emit4((Constant1() & 0xFF));
|
|
|
|
MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
|
|
|
|
#else
|
|
|
|
EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_CALL:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitCallRel(vm, callProcOfs);
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_PUSH:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitPushStack(vm);
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_POP:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_LEAVE:
|
|
|
|
v = Constant4();
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("81 C6"); // add esi, 0x12345678
|
|
|
|
Emit4(v);
|
|
|
|
EmitString("C3"); // ret
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_LOAD4:
|
2011-07-26 08:52:24 +00:00
|
|
|
if (code[pc] == OP_CONST && code[pc+5] == OP_ADD && code[pc+6] == OP_STORE4)
|
|
|
|
{
|
|
|
|
if(oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
|
|
|
|
{
|
|
|
|
compiledOfs -= 12;
|
|
|
|
vm->instructionPointers[instruction - 1] = compiledOfs;
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
pc++; // OP_CONST
|
2011-02-18 14:31:32 +00:00
|
|
|
v = Constant4();
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
EmitMovEDXStack(vm, vm->dataMask);
|
|
|
|
if(v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
|
|
|
|
{
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "FF 04 11"); // inc dword ptr [r9 + edx]
|
|
|
|
#else
|
|
|
|
EmitString("FF 82"); // inc dword ptr [edx + 0x12345678]
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "8B 04 11"); // mov eax, dword ptr [r9 + edx]
|
|
|
|
#else
|
|
|
|
EmitString("8B 82"); // mov eax, dword ptr [edx + 0x12345678]
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
EmitString("05"); // add eax, v
|
|
|
|
Emit4(v);
|
|
|
|
|
|
|
|
if (oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
|
|
|
|
{
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
|
|
|
|
#else
|
|
|
|
EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
|
|
|
EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
|
|
|
|
MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
|
|
|
|
#else
|
|
|
|
EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
}
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
pc++; // OP_ADD
|
|
|
|
pc++; // OP_STORE
|
|
|
|
instruction += 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
if(code[pc] == OP_CONST && code[pc+5] == OP_SUB && code[pc+6] == OP_STORE4)
|
|
|
|
{
|
|
|
|
if(oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
|
|
|
|
{
|
|
|
|
compiledOfs -= 12;
|
|
|
|
vm->instructionPointers[instruction - 1] = compiledOfs;
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
pc++; // OP_CONST
|
2011-02-18 14:31:32 +00:00
|
|
|
v = Constant4();
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
EmitMovEDXStack(vm, vm->dataMask);
|
|
|
|
if(v == 1 && oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
|
|
|
|
{
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "FF 0C 11"); // dec dword ptr [r9 + edx]
|
|
|
|
#else
|
|
|
|
EmitString("FF 8A"); // dec dword ptr [edx + 0x12345678]
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "8B 04 11"); // mov eax, dword ptr [r9 + edx]
|
|
|
|
#else
|
|
|
|
EmitString("8B 82"); // mov eax, dword ptr [edx + 0x12345678]
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
EmitString("2D"); // sub eax, v
|
|
|
|
Emit4(v);
|
|
|
|
|
|
|
|
if(oc0 == oc1 && pop0 == OP_LOCAL && pop1 == OP_LOCAL)
|
|
|
|
{
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
|
|
|
|
#else
|
|
|
|
EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
|
|
|
EmitString("8B 14 9F"); // mov edx, dword ptr [edi + ebx * 4]
|
|
|
|
MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
|
|
|
|
#else
|
|
|
|
EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
}
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
pc++; // OP_SUB
|
|
|
|
pc++; // OP_STORE
|
|
|
|
instruction += 3;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
if(buf[compiledOfs - 3] == 0x89 && buf[compiledOfs - 2] == 0x04 && buf[compiledOfs - 1] == 0x9F)
|
|
|
|
{
|
|
|
|
compiledOfs -= 3;
|
|
|
|
vm->instructionPointers[instruction - 1] = compiledOfs;
|
|
|
|
MASK_REG("E0", vm->dataMask); // and eax, 0x12345678
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "8B 04 01"); // mov eax, dword ptr [r9 + eax]
|
|
|
|
#else
|
|
|
|
EmitString("8B 80"); // mov eax, dword ptr [eax + 0x1234567]
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
}
|
2011-07-26 08:52:24 +00:00
|
|
|
|
|
|
|
EmitMovEAXStack(vm, vm->dataMask);
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "8B 04 01"); // mov eax, dword ptr [r9 + eax]
|
|
|
|
#else
|
|
|
|
EmitString("8B 80"); // mov eax, dword ptr [eax + 0x12345678]
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_LOAD2:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, vm->dataMask);
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "0F B7 04 01"); // movzx eax, word ptr [r9 + eax]
|
|
|
|
#else
|
|
|
|
EmitString("0F B7 80"); // movzx eax, word ptr [eax + 0x12345678]
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_LOAD1:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, vm->dataMask);
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "0F B6 04 01"); // movzx eax, byte ptr [r9 + eax]
|
|
|
|
#else
|
|
|
|
EmitString("0F B6 80"); // movzx eax, byte ptr [eax + 0x12345678]
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_STORE4:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4]
|
2017-07-10 01:33:41 +00:00
|
|
|
MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
|
2011-07-26 08:52:24 +00:00
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "89 04 11"); // mov dword ptr [r9 + edx], eax
|
|
|
|
#else
|
|
|
|
EmitString("89 82"); // mov dword ptr [edx + 0x12345678], eax
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_STORE2:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4]
|
2017-07-10 01:33:41 +00:00
|
|
|
MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
|
2011-07-26 08:52:24 +00:00
|
|
|
#if idx64
|
|
|
|
Emit1(0x66); // mov word ptr [r9 + edx], eax
|
|
|
|
EmitRexString(0x41, "89 04 11");
|
|
|
|
#else
|
|
|
|
EmitString("66 89 82"); // mov word ptr [edx + 0x12345678], eax
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_STORE1:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
EmitString("8B 54 9F FC"); // mov edx, dword ptr -4[edi + ebx * 4]
|
|
|
|
MASK_REG("E2", vm->dataMask); // and edx, 0x12345678
|
|
|
|
#if idx64
|
|
|
|
EmitRexString(0x41, "88 04 11"); // mov byte ptr [r9 + edx], eax
|
|
|
|
#else
|
|
|
|
EmitString("88 82"); // mov byte ptr [edx + 0x12345678], eax
|
|
|
|
Emit4((intptr_t) vm->dataBase);
|
|
|
|
#endif
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_EQ:
|
|
|
|
case OP_NE:
|
|
|
|
case OP_LTI:
|
|
|
|
case OP_LEI:
|
|
|
|
case OP_GTI:
|
|
|
|
case OP_GEI:
|
|
|
|
case OP_LTU:
|
|
|
|
case OP_LEU:
|
|
|
|
case OP_GTU:
|
|
|
|
case OP_GEU:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
|
|
|
|
EmitString("39 44 9F 04"); // cmp eax, dword ptr 4[edi + ebx * 4]
|
|
|
|
|
|
|
|
EmitBranchConditions(vm, op);
|
|
|
|
break;
|
2011-02-18 14:31:32 +00:00
|
|
|
case OP_EQF:
|
|
|
|
case OP_NEF:
|
|
|
|
case OP_LTF:
|
|
|
|
case OP_LEF:
|
|
|
|
case OP_GTF:
|
|
|
|
case OP_GEF:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
|
|
|
|
EmitString("D9 44 9F 04"); // fld dword ptr 4[edi + ebx * 4]
|
|
|
|
EmitString("D8 5C 9F 08"); // fcomp dword ptr 8[edi + ebx * 4]
|
|
|
|
EmitString("DF E0"); // fnstsw ax
|
|
|
|
|
|
|
|
switch(op)
|
|
|
|
{
|
|
|
|
case OP_EQF:
|
|
|
|
EmitString("F6 C4 40"); // test ah,0x40
|
|
|
|
EmitJumpIns(vm, "0F 85", Constant4()); // jne 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_NEF:
|
|
|
|
EmitString("F6 C4 40"); // test ah,0x40
|
|
|
|
EmitJumpIns(vm, "0F 84", Constant4()); // je 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_LTF:
|
|
|
|
EmitString("F6 C4 01"); // test ah,0x01
|
|
|
|
EmitJumpIns(vm, "0F 85", Constant4()); // jne 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_LEF:
|
|
|
|
EmitString("F6 C4 41"); // test ah,0x41
|
|
|
|
EmitJumpIns(vm, "0F 85", Constant4()); // jne 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_GTF:
|
|
|
|
EmitString("F6 C4 41"); // test ah,0x41
|
|
|
|
EmitJumpIns(vm, "0F 84", Constant4()); // je 0x12345678
|
|
|
|
break;
|
|
|
|
case OP_GEF:
|
|
|
|
EmitString("F6 C4 01"); // test ah,0x01
|
|
|
|
EmitJumpIns(vm, "0F 84", Constant4()); // je 0x12345678
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2011-02-18 14:31:32 +00:00
|
|
|
case OP_NEGI:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0);
|
|
|
|
EmitString("F7 D8"); // neg eax
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX);
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_ADD:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("01 44 9F FC"); // add dword ptr -4[edi + ebx * 4], eax
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_SUB:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("29 44 9F FC"); // sub dword ptr -4[edi + ebx * 4], eax
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_DIVI:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
|
|
|
|
EmitString("99"); // cdq
|
|
|
|
EmitString("F7 3C 9F"); // idiv dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_DIVU:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
|
|
|
|
EmitString("33 D2"); // xor edx, edx
|
|
|
|
EmitString("F7 34 9F"); // div dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_MODI:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
|
|
|
|
EmitString("99" ); // cdq
|
|
|
|
EmitString("F7 3C 9F"); // idiv dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("89 54 9F FC"); // mov dword ptr -4[edi + ebx * 4],edx
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_MODU:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
|
|
|
|
EmitString("33 D2"); // xor edx, edx
|
|
|
|
EmitString("F7 34 9F"); // div dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("89 54 9F FC"); // mov dword ptr -4[edi + ebx * 4],edx
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_MULI:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
|
|
|
|
EmitString("F7 2C 9F"); // imul dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_MULU:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("8B 44 9F FC"); // mov eax,dword ptr -4[edi + ebx * 4]
|
|
|
|
EmitString("F7 24 9F"); // mul dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("89 44 9F FC"); // mov dword ptr -4[edi + ebx * 4],eax
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_BAND:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("21 44 9F FC"); // and dword ptr -4[edi + ebx * 4],eax
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_BOR:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("09 44 9F FC"); // or dword ptr -4[edi + ebx * 4],eax
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_BXOR:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovEAXStack(vm, 0); // mov eax, dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("31 44 9F FC"); // xor dword ptr -4[edi + ebx * 4],eax
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_BCOM:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("F7 14 9F"); // not dword ptr [edi + ebx * 4]
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_LSH:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovECXStack(vm);
|
|
|
|
EmitString("D3 64 9F FC"); // shl dword ptr -4[edi + ebx * 4], cl
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_RSHI:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovECXStack(vm);
|
|
|
|
EmitString("D3 7C 9F FC"); // sar dword ptr -4[edi + ebx * 4], cl
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_RSHU:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitMovECXStack(vm);
|
|
|
|
EmitString("D3 6C 9F FC"); // shr dword ptr -4[edi + ebx * 4], cl
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_NEGF:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("D9 04 9F"); // fld dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("D9 E0"); // fchs
|
|
|
|
EmitString("D9 1C 9F"); // fstp dword ptr [edi + ebx * 4]
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_ADDF:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("D9 44 9F FC"); // fld dword ptr -4[edi + ebx * 4]
|
|
|
|
EmitString("D8 04 9F"); // fadd dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("D9 5C 9F FC"); // fstp dword ptr -4[edi + ebx * 4]
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_SUBF:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
|
|
|
EmitString("D9 04 9F"); // fld dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("D8 64 9F 04"); // fsub dword ptr 4[edi + ebx * 4]
|
|
|
|
EmitString("D9 1C 9F"); // fstp dword ptr [edi + ebx * 4]
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_DIVF:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
|
|
|
EmitString("D9 04 9F"); // fld dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("D8 74 9F 04"); // fdiv dword ptr 4[edi + ebx * 4]
|
|
|
|
EmitString("D9 1C 9F"); // fstp dword ptr [edi + ebx * 4]
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_MULF:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
|
|
|
EmitString("D9 04 9F"); // fld dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("D8 4C 9F 04"); // fmul dword ptr 4[edi + ebx * 4]
|
|
|
|
EmitString("D9 1C 9F"); // fstp dword ptr [edi + ebx * 4]
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_CVIF:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("DB 04 9F"); // fild dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("D9 1C 9F"); // fstp dword ptr [edi + ebx * 4]
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_CVFI:
|
|
|
|
#ifndef FTOL_PTR // WHENHELLISFROZENOVER
|
|
|
|
// not IEEE complient, but simple and fast
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("D9 04 9F"); // fld dword ptr [edi + ebx * 4]
|
|
|
|
EmitString("DB 1C 9F"); // fistp dword ptr [edi + ebx * 4]
|
2011-02-18 14:31:32 +00:00
|
|
|
#else // FTOL_PTR
|
|
|
|
// call the library conversion function
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitRexString(0x48, "BA"); // mov edx, Q_VMftol
|
|
|
|
EmitPtr(Q_VMftol);
|
|
|
|
EmitRexString(0x48, "FF D2"); // call edx
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
2011-02-18 14:31:32 +00:00
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
case OP_SEX8:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("0F BE 04 9F"); // movsx eax, byte ptr [edi + ebx * 4]
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
case OP_SEX16:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("0F BF 04 9F"); // movsx eax, word ptr [edi + ebx * 4]
|
|
|
|
EmitCommand(LAST_COMMAND_MOV_STACK_EAX); // mov dword ptr [edi + ebx * 4], eax
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BLOCK_COPY:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitString("B8"); // mov eax, 0x12345678
|
|
|
|
Emit4(VM_BLOCK_COPY);
|
|
|
|
EmitString("B9"); // mov ecx, 0x12345678
|
|
|
|
Emit4(Constant4());
|
|
|
|
|
|
|
|
EmitCallRel(vm, callDoSyscallOfs);
|
|
|
|
|
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_2); // sub bl, 2
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_JUMP:
|
2011-07-26 08:52:24 +00:00
|
|
|
EmitCommand(LAST_COMMAND_SUB_BL_1); // sub bl, 1
|
|
|
|
EmitString("8B 44 9F 04"); // mov eax, dword ptr 4[edi + ebx * 4]
|
|
|
|
EmitString("81 F8"); // cmp eax, vm->instructionCount
|
|
|
|
Emit4(vm->instructionCount);
|
|
|
|
#if idx64
|
|
|
|
EmitString("73 04"); // jae +4
|
|
|
|
EmitRexString(0x49, "FF 24 C0"); // jmp qword ptr [r8 + eax * 8]
|
|
|
|
#else
|
|
|
|
EmitString("73 07"); // jae +7
|
|
|
|
EmitString("FF 24 85"); // jmp dword ptr [instructionPointers + eax * 4]
|
|
|
|
Emit4((intptr_t) vm->instructionPointers);
|
|
|
|
#endif
|
|
|
|
EmitCallErrJump(vm, callDoSyscallOfs);
|
2011-02-18 14:31:32 +00:00
|
|
|
break;
|
|
|
|
default:
|
2011-07-26 08:52:24 +00:00
|
|
|
VMFREE_BUFFERS();
|
|
|
|
Com_Error(ERR_DROP, "VM_CompileX86: bad opcode %i at offset %i", op, pc);
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
pop0 = pop1;
|
|
|
|
pop1 = op;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
// copy to an exact sized buffer with the appropriate permission bits
|
2011-02-18 14:31:32 +00:00
|
|
|
vm->codeLength = compiledOfs;
|
|
|
|
#ifdef VM_X86_MMAP
|
|
|
|
vm->codeBase = mmap(NULL, compiledOfs, PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
|
2011-07-26 08:52:24 +00:00
|
|
|
if(vm->codeBase == MAP_FAILED)
|
|
|
|
Com_Error(ERR_FATAL, "VM_CompileX86: can't mmap memory");
|
2011-02-18 14:31:32 +00:00
|
|
|
#elif _WIN32
|
|
|
|
// allocate memory with EXECUTE permissions under windows.
|
|
|
|
vm->codeBase = VirtualAlloc(NULL, compiledOfs, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
|
|
|
|
if(!vm->codeBase)
|
2011-07-26 08:52:24 +00:00
|
|
|
Com_Error(ERR_FATAL, "VM_CompileX86: VirtualAlloc failed");
|
2011-02-18 14:31:32 +00:00
|
|
|
#else
|
|
|
|
vm->codeBase = malloc(compiledOfs);
|
2011-07-26 08:52:24 +00:00
|
|
|
if(!vm->codeBase)
|
|
|
|
Com_Error(ERR_FATAL, "VM_CompileX86: malloc failed");
|
2011-02-18 14:31:32 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
Com_Memcpy( vm->codeBase, buf, compiledOfs );
|
|
|
|
|
|
|
|
#ifdef VM_X86_MMAP
|
|
|
|
if(mprotect(vm->codeBase, compiledOfs, PROT_READ|PROT_EXEC))
|
2011-07-26 08:52:24 +00:00
|
|
|
Com_Error(ERR_FATAL, "VM_CompileX86: mprotect failed");
|
2011-02-18 14:31:32 +00:00
|
|
|
#elif _WIN32
|
|
|
|
{
|
|
|
|
DWORD oldProtect = 0;
|
|
|
|
|
|
|
|
// remove write permissions.
|
|
|
|
if(!VirtualProtect(vm->codeBase, compiledOfs, PAGE_EXECUTE_READ, &oldProtect))
|
2011-07-26 08:52:24 +00:00
|
|
|
Com_Error(ERR_FATAL, "VM_CompileX86: VirtualProtect failed");
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
Z_Free( code );
|
2011-02-18 14:31:32 +00:00
|
|
|
Z_Free( buf );
|
|
|
|
Z_Free( jused );
|
|
|
|
Com_Printf( "VM file %s compiled to %i bytes of code\n", vm->name, compiledOfs );
|
|
|
|
|
|
|
|
vm->destroy = VM_Destroy_Compiled;
|
|
|
|
|
|
|
|
// offset all the instruction pointers for the new location
|
|
|
|
for ( i = 0 ; i < header->instructionCount ; i++ ) {
|
2011-07-26 08:52:24 +00:00
|
|
|
vm->instructionPointers[i] += (intptr_t) vm->codeBase;
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VM_Destroy_Compiled(vm_t* self)
|
|
|
|
{
|
|
|
|
#ifdef VM_X86_MMAP
|
|
|
|
munmap(self->codeBase, self->codeLength);
|
|
|
|
#elif _WIN32
|
|
|
|
VirtualFree(self->codeBase, 0, MEM_RELEASE);
|
|
|
|
#else
|
|
|
|
free(self->codeBase);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
==============
|
|
|
|
VM_CallCompiled
|
|
|
|
|
|
|
|
This function is called directly by the generated code
|
|
|
|
==============
|
|
|
|
*/
|
2011-07-26 08:52:24 +00:00
|
|
|
|
2012-09-15 03:03:44 +00:00
|
|
|
#if defined(_MSC_VER) && defined(idx64)
|
|
|
|
extern uint8_t qvmcall64(int *programStack, int *opStack, intptr_t *instructionPointers, byte *dataBase);
|
|
|
|
#endif
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
int VM_CallCompiled(vm_t *vm, int *args)
|
|
|
|
{
|
|
|
|
byte stack[OPSTACK_SIZE + 15];
|
|
|
|
void *entryPoint;
|
|
|
|
int programStack, stackOnEntry;
|
2011-02-18 14:31:32 +00:00
|
|
|
byte *image;
|
2011-07-26 08:52:24 +00:00
|
|
|
int *opStack;
|
|
|
|
int opStackOfs;
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
int arg;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
currentVM = vm;
|
|
|
|
|
|
|
|
// interpret the code
|
|
|
|
vm->currentlyInterpreting = qtrue;
|
|
|
|
|
|
|
|
// we might be called recursively, so this might not be the very top
|
2011-07-26 08:52:24 +00:00
|
|
|
programStack = stackOnEntry = vm->programStack;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
// set up the stack frame
|
|
|
|
image = vm->dataBase;
|
|
|
|
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
programStack -= ( 8 + 4 * MAX_VMMAIN_ARGS );
|
|
|
|
|
|
|
|
for ( arg = 0; arg < MAX_VMMAIN_ARGS; arg++ )
|
|
|
|
*(int *)&image[ programStack + 8 + arg * 4 ] = args[ arg ];
|
|
|
|
|
2011-02-18 14:31:32 +00:00
|
|
|
*(int *)&image[ programStack + 4 ] = 0; // return stack
|
|
|
|
*(int *)&image[ programStack ] = -1; // will terminate the loop on return
|
|
|
|
|
|
|
|
// off we go into generated code...
|
2011-07-26 08:52:24 +00:00
|
|
|
entryPoint = vm->codeBase + vm->entryOfs;
|
|
|
|
opStack = PADP(stack, 16);
|
|
|
|
*opStack = 0xDEADBEEF;
|
|
|
|
opStackOfs = 0;
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
2011-07-26 08:52:24 +00:00
|
|
|
#if idx64
|
|
|
|
opStackOfs = qvmcall64(&programStack, opStack, vm->instructionPointers, vm->dataBase);
|
|
|
|
#else
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
pushad
|
|
|
|
|
|
|
|
mov esi, dword ptr programStack
|
|
|
|
mov edi, dword ptr opStack
|
|
|
|
mov ebx, dword ptr opStackOfs
|
|
|
|
|
|
|
|
call entryPoint
|
|
|
|
|
|
|
|
mov dword ptr opStackOfs, ebx
|
|
|
|
mov dword ptr opStack, edi
|
|
|
|
mov dword ptr programStack, esi
|
|
|
|
|
|
|
|
popad
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#elif idx64
|
|
|
|
__asm__ volatile(
|
2011-08-10 22:10:14 +00:00
|
|
|
"movq %5, %%rax\n"
|
|
|
|
"movq %3, %%r8\n"
|
|
|
|
"movq %4, %%r9\n"
|
|
|
|
"push %%r15\n"
|
|
|
|
"push %%r14\n"
|
|
|
|
"push %%r13\n"
|
|
|
|
"push %%r12\n"
|
|
|
|
"callq *%%rax\n"
|
|
|
|
"pop %%r12\n"
|
|
|
|
"pop %%r13\n"
|
|
|
|
"pop %%r14\n"
|
|
|
|
"pop %%r15\n"
|
2011-07-26 08:52:24 +00:00
|
|
|
: "+S" (programStack), "+D" (opStack), "+b" (opStackOfs)
|
|
|
|
: "g" (vm->instructionPointers), "g" (vm->dataBase), "g" (entryPoint)
|
|
|
|
: "cc", "memory", "%rax", "%rcx", "%rdx", "%r8", "%r9", "%r10", "%r11"
|
|
|
|
);
|
2011-02-18 14:31:32 +00:00
|
|
|
#else
|
2011-07-26 08:52:24 +00:00
|
|
|
__asm__ volatile(
|
2011-08-10 22:10:14 +00:00
|
|
|
"calll *%3\n"
|
2011-07-26 08:52:24 +00:00
|
|
|
: "+S" (programStack), "+D" (opStack), "+b" (opStackOfs)
|
|
|
|
: "g" (entryPoint)
|
|
|
|
: "cc", "memory", "%eax", "%ecx", "%edx"
|
|
|
|
);
|
2011-02-18 14:31:32 +00:00
|
|
|
#endif
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
if(opStackOfs != 1 || *opStack != 0xDEADBEEF)
|
|
|
|
{
|
|
|
|
Com_Error(ERR_DROP, "opStack corrupted in compiled code");
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
ioquake3 resync to revision 2398 from 2369.
This is the last ioquake3 revision before ioquake3 changed from subversion to git at the beginning of 2013.
#5808 - Include and use .glsl in source (rend2)
#5812 - Use refdef's coordinates when drawing to screen shadow fbo, and separate depth texture and screen texture coordinates in glsl shaders.
Include Rend2 renderer in MacOSX bundle
Include OpenGL1 and Rend2 renderers in MacOSX UB
Include Rend2 renderer in NSIS installer.
Include OpenGL1 and Rend2 renderers in Loki Setup Installer.
Have NSIS uninstaller delete rend2.
Split light sample into direct and ambient parts when using deluxemaps or per-vertex light vectors. Fixes #5813.
Fix writting voip data in demos (broke in r2102).
Fix server ignoring client move commands if voip data is included.
Allow changing cl_voip without restarting.
Fix assert failing in CL_ParseVoip() while flipping cl_voip off and on.
Only declare var_SampleToView in lightall shader when it is actually used.
Fix a couple files not ending with a newline.
Fix clients being able to reset their player state and respawn using donedl.
Fix passing arg9 (qvm only), arg10, and arg11 to vmMain for native libs and non-i386 compiled or interpated qvms. (Currently they aren't use in vmMain in game, cgame, or ui.)
Fix passing args[11] to args[15] from vm to engine on ppc64 and sparc64. Some of the args are used by game bot prediction syscalls. May have been causing bugs. Note: This was fixed for x86_64 in r2163.
Fix reconnect command to work after leaving server. (#5794)
Fix dedicated server crashing when using MSG_ReadDelta*, though it only happens if someone modifies the engine. (#5449)
Makefile fixes for OpenBSD by Jonathan Gray. (#5728)
Save all arguments from connect for reconnect command.
Remove unnecessary localhost check from reconnect command.
Support r_srgb even without hardware support. Also tweak default autoexposure/tonemap settings to look good on both r_srgb 0 and 1.
Changed the MacOS-X build system to make UB's containing i386 and x86_64 arches and made make-macosx.sh not build UB's but only standard binaries
Fix spectator client being switched from follow to free after map_restart if following a client with a higher client number.
Fix client unlinking issue caused by ent->s.number being set to followed client's ps->clientNum after map_restart. Reported by Ensiform.
Changes from Ensiform:
- In G_AddBot, try to allocate clientNum before doing anything else.
- In G_AddBot, don't set SVF_BOT and inuse. It's done in ClientConnect, plus inuse causes ClientDisconnect to be run for no reason.
- In G_AddBot, only set skill in bot useinfo once.
- Avoid using cl->ps.clientNum to check if cl is a bot.
Fix bot skill format so it doesn't always have a space at the beginning of it.
More fixes to the macosx buildsystem. This removes the SDL Framework and makes use of a SDL library that is position independant. This also brings back PPC builds into the UB and also as a standa alone build choice.
Have make-macosx.sh require the user to specify which architecture she/he wants to build for and suggest building UB's if the user is unaware of what architectures are
Lets list all the valid options.
2017-07-09 21:21:12 +00:00
|
|
|
if(programStack != stackOnEntry - (8 + 4 * MAX_VMMAIN_ARGS))
|
2011-07-26 08:52:24 +00:00
|
|
|
Com_Error(ERR_DROP, "programStack corrupted in compiled code");
|
2011-02-18 14:31:32 +00:00
|
|
|
|
|
|
|
vm->programStack = stackOnEntry;
|
|
|
|
|
2011-07-26 08:52:24 +00:00
|
|
|
return opStack[opStackOfs];
|
2011-02-18 14:31:32 +00:00
|
|
|
}
|
2023-03-03 04:26:22 +00:00
|
|
|
#endif
|