sourcepawn/jit/sp_vm_engine.cpp
author David Anderson <dvander@alliedmods.net>
Fri Jul 11 08:18:43 2008 +0000 (2008-07-11)
changeset 2193 db7c6362f3f2
child 2195 5b70179559f9
permissions -rw-r--r--
landed massive refactoring of the JIT. contexts are actually contexts now, and a higher level structure wraps sp_plugin_t info. on that note, both sp_plugin_t and sp_context_t are entirely opaque, and not even core has access to them. amazingly, i managed to keep binary compatibility here although a large number of functions are deprecated (and core should eventually stop calling them).

NOTE: the JIT is now embeddable out-of-box and usable by other projects which is pretty cool. I will commit a shell app demonstrating this soon
dvander@2193
     1
/**
dvander@2193
     2
 * vim: set ts=4 :
dvander@2193
     3
 * =============================================================================
dvander@2193
     4
 * SourcePawn
dvander@2193
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
dvander@2193
     6
 * =============================================================================
dvander@2193
     7
 *
dvander@2193
     8
 * This program is free software; you can redistribute it and/or modify it under
dvander@2193
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
dvander@2193
    10
 * Free Software Foundation.
dvander@2193
    11
 * 
dvander@2193
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@2193
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@2193
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
dvander@2193
    15
 * details.
dvander@2193
    16
 *
dvander@2193
    17
 * You should have received a copy of the GNU General Public License along with
dvander@2193
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@2193
    19
 *
dvander@2193
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
dvander@2193
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@2193
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@2193
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
dvander@2193
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
dvander@2193
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
dvander@2193
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@2193
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@2193
    28
 *
dvander@2193
    29
 * Version: $Id$
dvander@2193
    30
 */
dvander@2193
    31
dvander@2193
    32
#include "sp_vm_types.h"
dvander@2193
    33
#include <sh_memory.h>
dvander@2193
    34
/* HACK to avoid including sourcehook.h for just the SH_ASSERT definition */
dvander@2193
    35
#if !defined  SH_ASSERT
dvander@2193
    36
	#define SH_ASSERT(x, info)
dvander@2193
    37
	#include <sh_pagealloc.h>
dvander@2193
    38
	#undef SH_ASSERT
dvander@2193
    39
#else
dvander@2193
    40
	#include <sh_pagealloc.h>
dvander@2193
    41
#endif
dvander@2193
    42
dvander@2193
    43
#include <malloc.h>
dvander@2193
    44
#include <string.h>
dvander@2193
    45
#include <assert.h>
dvander@2193
    46
#include "sp_file_headers.h"
dvander@2193
    47
#include "sp_vm_engine.h"
dvander@2193
    48
#include "zlib/zlib.h"
dvander@2193
    49
#include "sp_vm_basecontext.h"
dvander@2193
    50
dvander@2193
    51
SourcePawnEngine g_engine1;
dvander@2193
    52
dvander@2193
    53
#if defined WIN32
dvander@2193
    54
 #define WIN32_LEAN_AND_MEAN
dvander@2193
    55
 #include <windows.h>
dvander@2193
    56
#elif defined __GNUC__
dvander@2193
    57
 #include <sys/mman.h>
dvander@2193
    58
#endif
dvander@2193
    59
dvander@2193
    60
#define INVALID_CIP			0xFFFFFFFF
dvander@2193
    61
dvander@2193
    62
using namespace SourcePawn;
dvander@2193
    63
dvander@2193
    64
SourceHook::CPageAlloc g_ExeMemory(16);
dvander@2193
    65
dvander@2193
    66
#define ERROR_MESSAGE_MAX		25
dvander@2193
    67
static const char *g_ErrorMsgTable[] = 
dvander@2193
    68
{
dvander@2193
    69
	NULL,
dvander@2193
    70
	"Unrecognizable file format",
dvander@2193
    71
	"Decompressor was not found",
dvander@2193
    72
	"Not enough space on the heap",
dvander@2193
    73
	"Invalid parameter or parameter type",
dvander@2193
    74
	"Invalid plugin address",
dvander@2193
    75
	"Object or index not found",
dvander@2193
    76
	"Invalid index or index not found",
dvander@2193
    77
	"Not enough space on the stack",
dvander@2193
    78
	"Debug section not found or debug not enabled",
dvander@2193
    79
	"Invalid instruction",
dvander@2193
    80
	"Invalid memory access",
dvander@2193
    81
	"Stack went below stack boundary",
dvander@2193
    82
	"Heap went below heap boundary",
dvander@2193
    83
	"Divide by zero",
dvander@2193
    84
	"Array index is out of bounds",
dvander@2193
    85
	"Instruction contained invalid parameter",
dvander@2193
    86
	"Stack memory leaked by native",
dvander@2193
    87
	"Heap memory leaked by native",
dvander@2193
    88
	"Dynamic array is too big",
dvander@2193
    89
	"Tracker stack is out of bounds",
dvander@2193
    90
	"Native is not bound",
dvander@2193
    91
	"Maximum number of parameters reached",
dvander@2193
    92
	"Native detected error",
dvander@2193
    93
	"Plugin not runnable",
dvander@2193
    94
	"Call was aborted",
dvander@2193
    95
};
dvander@2193
    96
dvander@2193
    97
const char *SourcePawnEngine::GetErrorString(int error)
dvander@2193
    98
{
dvander@2193
    99
	if (error < 1 || error > ERROR_MESSAGE_MAX)
dvander@2193
   100
	{
dvander@2193
   101
		return NULL;
dvander@2193
   102
	}
dvander@2193
   103
dvander@2193
   104
	return g_ErrorMsgTable[error];
dvander@2193
   105
}
dvander@2193
   106
dvander@2193
   107
SourcePawnEngine::SourcePawnEngine()
dvander@2193
   108
{
dvander@2193
   109
	m_pDebugHook = NULL;
dvander@2193
   110
	m_CallStack = NULL;
dvander@2193
   111
	m_FreedCalls = NULL;
dvander@2193
   112
	m_CurChain = 0;
dvander@2193
   113
#if 0
dvander@2193
   114
	m_pFreeFuncs = NULL;
dvander@2193
   115
#endif
dvander@2193
   116
}
dvander@2193
   117
dvander@2193
   118
SourcePawnEngine::~SourcePawnEngine()
dvander@2193
   119
{
dvander@2193
   120
	TracedCall *pTemp;
dvander@2193
   121
	while (m_FreedCalls)
dvander@2193
   122
	{
dvander@2193
   123
		pTemp = m_FreedCalls->next;
dvander@2193
   124
		delete m_FreedCalls;
dvander@2193
   125
		m_FreedCalls = pTemp;
dvander@2193
   126
	}
dvander@2193
   127
dvander@2193
   128
#if 0
dvander@2193
   129
	CFunction *pNext;
dvander@2193
   130
	while (m_pFreeFuncs)
dvander@2193
   131
	{
dvander@2193
   132
		pNext = m_pFreeFuncs->m_pNext;
dvander@2193
   133
		delete m_pFreeFuncs;
dvander@2193
   134
		m_pFreeFuncs = pNext;
dvander@2193
   135
	}
dvander@2193
   136
#endif
dvander@2193
   137
}
dvander@2193
   138
dvander@2193
   139
void *SourcePawnEngine::ExecAlloc(size_t size)
dvander@2193
   140
{
dvander@2193
   141
#if defined WIN32
dvander@2193
   142
	return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
dvander@2193
   143
#elif defined __GNUC__
dvander@2193
   144
	void *base = memalign(sysconf(_SC_PAGESIZE), size);
dvander@2193
   145
	if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
dvander@2193
   146
	{
dvander@2193
   147
		free(base);
dvander@2193
   148
		return NULL;
dvander@2193
   149
	}
dvander@2193
   150
	return base;
dvander@2193
   151
#endif
dvander@2193
   152
}
dvander@2193
   153
dvander@2193
   154
void *SourcePawnEngine::AllocatePageMemory(size_t size)
dvander@2193
   155
{
dvander@2193
   156
	return g_ExeMemory.Alloc(size);
dvander@2193
   157
}
dvander@2193
   158
dvander@2193
   159
void SourcePawnEngine::SetReadExecute(void *ptr)
dvander@2193
   160
{
dvander@2193
   161
	g_ExeMemory.SetRE(ptr);
dvander@2193
   162
}
dvander@2193
   163
dvander@2193
   164
void SourcePawnEngine::SetReadWrite(void *ptr)
dvander@2193
   165
{
dvander@2193
   166
	g_ExeMemory.SetRW(ptr);
dvander@2193
   167
}
dvander@2193
   168
dvander@2193
   169
void SourcePawnEngine::FreePageMemory(void *ptr)
dvander@2193
   170
{
dvander@2193
   171
	g_ExeMemory.Free(ptr);
dvander@2193
   172
}
dvander@2193
   173
dvander@2193
   174
void SourcePawnEngine::ExecFree(void *address)
dvander@2193
   175
{
dvander@2193
   176
#if defined WIN32
dvander@2193
   177
	VirtualFree(address, 0, MEM_RELEASE);
dvander@2193
   178
#elif defined __GNUC__
dvander@2193
   179
	free(address);
dvander@2193
   180
#endif
dvander@2193
   181
}
dvander@2193
   182
dvander@2193
   183
void *SourcePawnEngine::BaseAlloc(size_t size)
dvander@2193
   184
{
dvander@2193
   185
	return malloc(size);
dvander@2193
   186
}
dvander@2193
   187
dvander@2193
   188
void SourcePawnEngine::BaseFree(void *memory)
dvander@2193
   189
{
dvander@2193
   190
	free(memory);
dvander@2193
   191
}
dvander@2193
   192
dvander@2193
   193
sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
dvander@2193
   194
{
dvander@2193
   195
	if (err != NULL)
dvander@2193
   196
	{
dvander@2193
   197
		*err = SP_ERROR_ABORTED;
dvander@2193
   198
	}
dvander@2193
   199
dvander@2193
   200
	return NULL;
dvander@2193
   201
}
dvander@2193
   202
dvander@2193
   203
sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
dvander@2193
   204
{
dvander@2193
   205
	if (err != NULL)
dvander@2193
   206
	{
dvander@2193
   207
		*err = SP_ERROR_ABORTED;
dvander@2193
   208
	}
dvander@2193
   209
dvander@2193
   210
	return NULL;
dvander@2193
   211
}
dvander@2193
   212
dvander@2193
   213
int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
dvander@2193
   214
{
dvander@2193
   215
	return SP_ERROR_ABORTED;
dvander@2193
   216
}
dvander@2193
   217
dvander@2193
   218
IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
dvander@2193
   219
{
dvander@2193
   220
	IDebugListener *old = m_pDebugHook;
dvander@2193
   221
dvander@2193
   222
	m_pDebugHook = pListener;
dvander@2193
   223
dvander@2193
   224
	return old;
dvander@2193
   225
}
dvander@2193
   226
dvander@2193
   227
unsigned int SourcePawnEngine::GetContextCallCount()
dvander@2193
   228
{
dvander@2193
   229
	if (!m_CallStack)
dvander@2193
   230
	{
dvander@2193
   231
		return 0;
dvander@2193
   232
	}
dvander@2193
   233
dvander@2193
   234
	return m_CallStack->chain;
dvander@2193
   235
}
dvander@2193
   236
dvander@2193
   237
TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
dvander@2193
   238
{
dvander@2193
   239
	TracedCall *pCall;
dvander@2193
   240
dvander@2193
   241
	if (!m_FreedCalls)
dvander@2193
   242
	{
dvander@2193
   243
		pCall = new TracedCall;
dvander@2193
   244
	} else {
dvander@2193
   245
		/* Unlink the head node from the free list */
dvander@2193
   246
		pCall = m_FreedCalls;
dvander@2193
   247
		m_FreedCalls = m_FreedCalls->next;
dvander@2193
   248
	}
dvander@2193
   249
dvander@2193
   250
	/* Link as the head node into the call stack */
dvander@2193
   251
	pCall->next = m_CallStack;
dvander@2193
   252
dvander@2193
   253
	if (new_chain)
dvander@2193
   254
	{
dvander@2193
   255
		pCall->chain = ++m_CurChain;
dvander@2193
   256
	} else {
dvander@2193
   257
		pCall->chain = m_CurChain;
dvander@2193
   258
	}
dvander@2193
   259
dvander@2193
   260
	m_CallStack = pCall;
dvander@2193
   261
dvander@2193
   262
	return pCall;
dvander@2193
   263
}
dvander@2193
   264
dvander@2193
   265
void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
dvander@2193
   266
{
dvander@2193
   267
	/* Check if this is the top of the call stack */
dvander@2193
   268
	if (pCall == m_CallStack)
dvander@2193
   269
	{
dvander@2193
   270
		m_CallStack = m_CallStack->next;
dvander@2193
   271
	}
dvander@2193
   272
dvander@2193
   273
	/* Add this to our linked list of freed calls */
dvander@2193
   274
	if (!m_FreedCalls)
dvander@2193
   275
	{
dvander@2193
   276
		m_FreedCalls = pCall;
dvander@2193
   277
		m_FreedCalls->next = NULL;
dvander@2193
   278
	} else {
dvander@2193
   279
		pCall->next = m_FreedCalls;
dvander@2193
   280
		m_FreedCalls = pCall;
dvander@2193
   281
	}
dvander@2193
   282
}
dvander@2193
   283
dvander@2193
   284
void SourcePawnEngine::PushTracer(BaseContext *ctx)
dvander@2193
   285
{
dvander@2193
   286
	TracedCall *pCall = MakeTracedCall(true);
dvander@2193
   287
dvander@2193
   288
	pCall->cip = INVALID_CIP;
dvander@2193
   289
	pCall->ctx = ctx;
dvander@2193
   290
	pCall->frm = INVALID_CIP;
dvander@2193
   291
}
dvander@2193
   292
dvander@2193
   293
void SourcePawnEngine::RunTracer(BaseContext *ctx, uint32_t frame, uint32_t codeip)
dvander@2193
   294
{
dvander@2193
   295
	assert(m_CallStack != NULL);
dvander@2193
   296
	assert(m_CallStack->ctx == ctx);
dvander@2193
   297
	assert(m_CallStack->chain == m_CurChain);
dvander@2193
   298
dvander@2193
   299
	if (m_CallStack->cip == INVALID_CIP)
dvander@2193
   300
	{
dvander@2193
   301
		/* We aren't logging anything yet, so begin the trace */
dvander@2193
   302
		m_CallStack->cip = codeip;
dvander@2193
   303
		m_CallStack->frm = frame;
dvander@2193
   304
	} else {
dvander@2193
   305
		if (m_CallStack->frm > frame)
dvander@2193
   306
		{
dvander@2193
   307
			/* The last frame has moved down the stack, 
dvander@2193
   308
			 * so we have to push a new call onto our list.
dvander@2193
   309
			 */
dvander@2193
   310
			TracedCall *pCall = MakeTracedCall(false);
dvander@2193
   311
			pCall->ctx = ctx;
dvander@2193
   312
			pCall->frm = frame;
dvander@2193
   313
		} else if (m_CallStack->frm < frame) {
dvander@2193
   314
			/* The last frame has moved up the stack,
dvander@2193
   315
			 * so we have to pop the call from our list.
dvander@2193
   316
			 */
dvander@2193
   317
			FreeTracedCall(m_CallStack);
dvander@2193
   318
		}
dvander@2193
   319
		/* no matter where we are, update the cip */
dvander@2193
   320
		m_CallStack->cip = codeip;
dvander@2193
   321
	}
dvander@2193
   322
}
dvander@2193
   323
dvander@2193
   324
void SourcePawnEngine::PopTracer(int error, const char *msg)
dvander@2193
   325
{
dvander@2193
   326
	assert(m_CallStack != NULL);
dvander@2193
   327
dvander@2193
   328
	if (error != SP_ERROR_NONE && m_pDebugHook)
dvander@2193
   329
	{
dvander@2193
   330
		uint32_t native = INVALID_CIP;
dvander@2193
   331
dvander@2193
   332
		if (m_CallStack->ctx->GetCtx()->n_err)
dvander@2193
   333
		{
dvander@2193
   334
			native = m_CallStack->ctx->GetCtx()->n_idx;
dvander@2193
   335
		}
dvander@2193
   336
dvander@2193
   337
		CContextTrace trace(m_CallStack, error, msg, native);
dvander@2193
   338
		m_pDebugHook->OnContextExecuteError(m_CallStack->ctx, &trace);
dvander@2193
   339
	}
dvander@2193
   340
dvander@2193
   341
	/* Now pop the error chain  */
dvander@2193
   342
	while (m_CallStack && m_CallStack->chain == m_CurChain)
dvander@2193
   343
	{
dvander@2193
   344
		FreeTracedCall(m_CallStack);
dvander@2193
   345
	}
dvander@2193
   346
dvander@2193
   347
	m_CurChain--;
dvander@2193
   348
}
dvander@2193
   349
dvander@2193
   350
unsigned int SourcePawnEngine::GetEngineAPIVersion()
dvander@2193
   351
{
dvander@2193
   352
	return SOURCEPAWN_ENGINE_API_VERSION;
dvander@2193
   353
}
dvander@2193
   354
dvander@2193
   355
CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native) : 
dvander@2193
   356
 m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart), m_Native(native)
dvander@2193
   357
{
dvander@2193
   358
}
dvander@2193
   359
dvander@2193
   360
bool CContextTrace::DebugInfoAvailable()
dvander@2193
   361
{
dvander@2193
   362
	return m_pStart->ctx->IsDebugging();
dvander@2193
   363
}
dvander@2193
   364
dvander@2193
   365
const char *CContextTrace::GetCustomErrorString()
dvander@2193
   366
{
dvander@2193
   367
	return m_pMsg;
dvander@2193
   368
}
dvander@2193
   369
dvander@2193
   370
int CContextTrace::GetErrorCode()
dvander@2193
   371
{
dvander@2193
   372
	return m_Error;
dvander@2193
   373
}
dvander@2193
   374
dvander@2193
   375
const char *CContextTrace::GetErrorString()
dvander@2193
   376
{
dvander@2193
   377
	if (m_Error > ERROR_MESSAGE_MAX || 
dvander@2193
   378
		m_Error < 1)
dvander@2193
   379
	{
dvander@2193
   380
		return "Invalid error code";
dvander@2193
   381
	} else {
dvander@2193
   382
		return g_ErrorMsgTable[m_Error];
dvander@2193
   383
	}
dvander@2193
   384
}
dvander@2193
   385
dvander@2193
   386
void CContextTrace::ResetTrace()
dvander@2193
   387
{
dvander@2193
   388
	m_pIterator = m_pStart;
dvander@2193
   389
}
dvander@2193
   390
dvander@2193
   391
bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
dvander@2193
   392
{
dvander@2193
   393
	if (!m_pIterator || (m_pIterator->chain != m_pStart->chain))
dvander@2193
   394
	{
dvander@2193
   395
		return false;
dvander@2193
   396
	}
dvander@2193
   397
dvander@2193
   398
	if (m_pIterator->cip == INVALID_CIP)
dvander@2193
   399
	{
dvander@2193
   400
		return false;
dvander@2193
   401
	}
dvander@2193
   402
dvander@2193
   403
	IPluginContext *pContext = m_pIterator->ctx;
dvander@2193
   404
	IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
dvander@2193
   405
dvander@2193
   406
	if (!pInfo)
dvander@2193
   407
	{
dvander@2193
   408
		return false;
dvander@2193
   409
	}
dvander@2193
   410
dvander@2193
   411
	if (!trace)
dvander@2193
   412
	{
dvander@2193
   413
		m_pIterator = m_pIterator->next;
dvander@2193
   414
		return true;
dvander@2193
   415
	}
dvander@2193
   416
dvander@2193
   417
	if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
dvander@2193
   418
	{
dvander@2193
   419
		trace->filename = NULL;
dvander@2193
   420
	}
dvander@2193
   421
dvander@2193
   422
	if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
dvander@2193
   423
	{
dvander@2193
   424
		trace->function = NULL;
dvander@2193
   425
	}
dvander@2193
   426
dvander@2193
   427
	if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
dvander@2193
   428
	{
dvander@2193
   429
		trace->line = 0;
dvander@2193
   430
	}
dvander@2193
   431
dvander@2193
   432
	m_pIterator = m_pIterator->next;
dvander@2193
   433
dvander@2193
   434
	return true;
dvander@2193
   435
}
dvander@2193
   436
dvander@2193
   437
const char *CContextTrace::GetLastNative(uint32_t *index)
dvander@2193
   438
{
dvander@2193
   439
	if (m_Native == INVALID_CIP)
dvander@2193
   440
	{
dvander@2193
   441
		return NULL;
dvander@2193
   442
	}
dvander@2193
   443
dvander@2193
   444
	sp_native_t *native;
dvander@2193
   445
	if (m_pIterator->ctx->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE)
dvander@2193
   446
	{
dvander@2193
   447
		return NULL;
dvander@2193
   448
	}
dvander@2193
   449
dvander@2193
   450
	if (index)
dvander@2193
   451
	{
dvander@2193
   452
		*index = m_Native;
dvander@2193
   453
	}
dvander@2193
   454
dvander@2193
   455
	return native->name;
dvander@2193
   456
}