* More robust x86 vm entrypoint/callback assembly (Tron)

This commit is contained in:
Tim Angus 2008-08-17 23:22:06 +00:00
parent d781a25157
commit 1af6eb9592

View file

@ -53,7 +53,6 @@ static void VM_Destroy_Compiled(vm_t* self);
*/ */
// TTimo: initialised the statics, this fixes a crash when entering a compiled VM
static byte *buf = NULL; static byte *buf = NULL;
static byte *jused = NULL; static byte *jused = NULL;
static int compiledOfs = 0; static int compiledOfs = 0;
@ -71,9 +70,6 @@ int _ftol( float );
static int ftolPtr = (int)_ftol; static int ftolPtr = (int)_ftol;
#endif #endif
void AsmCall( void );
static int asmCallPtr = (int)AsmCall;
#else // _MSC_VER #else // _MSC_VER
#if defined( FTOL_PTR ) #if defined( FTOL_PTR )
@ -88,10 +84,11 @@ int qftol0F7F( void );
static int ftolPtr = (int)qftol0F7F; static int ftolPtr = (int)qftol0F7F;
#endif // FTOL_PTR #endif // FTOL_PTR
void doAsmCall( void );
static int asmCallPtr = (int)doAsmCall;
#endif #endif
void AsmCall(void);
static void (*const asmCallPtr)(void) = AsmCall;
static int callMask = 0; static int callMask = 0;
@ -124,7 +121,7 @@ vm_t* savedVM;
__asm { __asm {
mov eax, dword ptr [edi] mov eax, dword ptr [edi]
sub edi, 4 sub edi, 4
or eax,eax test eax,eax
jl systemCall jl systemCall
// calling another vm function // calling another vm function
shl eax,2 shl eax,2
@ -137,8 +134,7 @@ systemCall:
// convert negative num to system call number // convert negative num to system call number
// and store right before the first arg // and store right before the first arg
neg eax not eax
dec eax
push ebp push ebp
mov ebp, esp mov ebp, esp
@ -180,68 +176,58 @@ _asm {
#else //!_MSC_VER #else //!_MSC_VER
#if defined(__MINGW32__) || defined(MACOS_X) // _ is prepended to compiled symbols #if defined(__MINGW32__) || defined(MACOS_X) // _ is prepended to compiled symbols
#define CMANG(sym) "_"#sym # define CMANG(sym) "_"#sym
#else #else
#define CMANG(sym) #sym # define CMANG(sym) #sym
#endif #endif
static int callProgramStack; static void __attribute__((cdecl, used)) CallAsmCall(int const syscallNum,
static int *callOpStack; int const programStack, int* const opStack)
static int callSyscallNum;
void callAsmCall(void)
{ {
vm_t *savedVM; vm_t *const vm = currentVM;
int *callOpStack2; intptr_t *const data = (intptr_t*)(vm->dataBase + programStack + 4);
savedVM = currentVM;
callOpStack2 = callOpStack;
// save the stack to allow recursive VM entry // save the stack to allow recursive VM entry
currentVM->programStack = callProgramStack - 4; vm->programStack = programStack - 4;
*(int *)((byte *)currentVM->dataBase + callProgramStack + 4) = callSyscallNum; *data = syscallNum;
//VM_LogSyscalls((int *)((byte *)currentVM->dataBase + callProgramStack + 4) ); opStack[1] = vm->systemCall(data);
*(callOpStack2+1) = currentVM->systemCall( (intptr_t *)((byte *)currentVM->dataBase + callProgramStack + 4) );
currentVM = savedVM; currentVM = vm;
} }
// Note the C space function AsmCall is never actually called, and is in fact __asm__(
// arbitrarily named (though this is not true for the MSC version). When a vm ".text\n\t"
// makes a system call, control jumps straight to the doAsmCall label. ".p2align 4,,15\n\t"
void AsmCall( void ) { #if defined __ELF__
__asm__( CMANG(doAsmCall) ": \n\t" \ ".type " CMANG(AsmCall) ", @function\n"
" movl (%%edi),%%eax \n\t" \ #endif
" subl $4,%%edi \n\t" \ CMANG(AsmCall) ":\n\t"
" orl %%eax,%%eax \n\t" \ "movl (%edi), %eax\n\t"
" jl systemCall \n\t" \ "subl $4, %edi\n\t"
" shll $2,%%eax \n\t" \ "testl %eax, %eax\n\t"
" addl %3,%%eax \n\t" \ "jl 0f\n\t"
" call *(%%eax) \n\t" \ "shll $2, %eax\n\t"
" movl (%%edi),%%eax \n\t" \ "addl " CMANG(instructionPointers) ", %eax\n\t"
" andl " CMANG(callMask) ", %%eax \n\t" \ "call *(%eax)\n\t"
" jmp doret \n\t" \ "movl (%edi), %eax\n\t"
"systemCall: \n\t" \ "andl " CMANG(callMask) ", %eax\n\t"
" negl %%eax \n\t" \ "ret\n"
" decl %%eax \n\t" \ "0:\n\t" // system call
" movl %%eax,%0 \n\t" \ "notl %eax\n\t"
" movl %%esi,%1 \n\t" \ "pushl %ecx\n\t"
" movl %%edi,%2 \n\t" \ "pushl %edi\n\t" // opStack
" pushl %%ecx \n\t" \ "pushl %esi\n\t" // programStack
" pushl %%esi \n\t" \ "pushl %eax\n\t" // syscallNum
" pushl %%edi \n\t" \ "call " CMANG(CallAsmCall) "\n\t"
" call " CMANG(callAsmCall) " \n\t" \ "addl $12, %esp\n\t"
" popl %%edi \n\t" \ "popl %ecx\n\t"
" popl %%esi \n\t" \ "addl $4, %edi\n\t"
" popl %%ecx \n\t" \ "ret\n\t"
" addl $4,%%edi \n\t" \ #if defined __ELF__
"doret: \n\t" \ ".size " CMANG(AsmCall)", .-" CMANG(AsmCall)
" ret \n\t" \ #endif
: "=rm" (callSyscallNum), "=rm" (callProgramStack), "=rm" (callOpStack) \ );
: "m" (instructionPointers) \
: "ax", "di", "si", "cx" \
);
}
#endif #endif
static int Constant4( void ) { static int Constant4( void ) {
@ -1142,7 +1128,6 @@ int VM_CallCompiled( vm_t *vm, int *args ) {
int programStack; int programStack;
int stackOnEntry; int stackOnEntry;
byte *image; byte *image;
void *entryPoint;
void *opStack; void *opStack;
int *oldInstructionPointers; int *oldInstructionPointers;
@ -1181,45 +1166,38 @@ int VM_CallCompiled( vm_t *vm, int *args ) {
*(int *)&image[ programStack ] = -1; // will terminate the loop on return *(int *)&image[ programStack ] = -1; // will terminate the loop on return
// off we go into generated code... // off we go into generated code...
entryPoint = vm->codeBase;
opStack = &stack; opStack = &stack;
#ifdef _MSC_VER
__asm {
pushad
mov esi, programStack;
mov edi, opStack
call entryPoint
mov programStack, esi
mov opStack, edi
popad
}
#else
{ {
static int memProgramStack; #ifdef _MSC_VER
static void *memOpStack; void *entryPoint = vm->codeBase;
static void *memEntryPoint;
memProgramStack = programStack; __asm {
memOpStack = opStack; pushad
memEntryPoint = entryPoint; mov esi, programStack
mov edi, opStack
__asm__(" pushal \n" \ call entryPoint
" movl %0,%%esi \n" \ mov programStack, esi
" movl %1,%%edi \n" \ mov opStack, edi
" call *%2 \n" \ popad
" movl %%esi,%0 \n" \ }
" movl %%edi,%1 \n" \ #else
" popal \n" \ /* These registers are used as scratch registers and are destroyed after the
: "=m" (memProgramStack), "=m" (memOpStack) \ * call. Do not use clobber, so they can be used as input for the asm. */
: "m" (memEntryPoint), "m" (memProgramStack), "m" (memOpStack) \ unsigned eax;
: "si", "di" \ unsigned ebx;
unsigned ecx;
unsigned edx;
__asm__ volatile(
"call *%6"
: "+S" (programStack), "+D" (opStack),
"=a" (eax), "=b" (ebx), "=c" (ecx), "=d" (edx)
: "mr" (vm->codeBase)
: "cc", "memory"
); );
programStack = memProgramStack;
opStack = memOpStack;
}
#endif #endif
}
if ( opStack != &stack[1] ) { if ( opStack != &stack[1] ) {
Com_Error( ERR_DROP, "opStack corrupted in compiled code" ); Com_Error( ERR_DROP, "opStack corrupted in compiled code" );