core/vm/sp_vm_engine.cpp
author David Anderson <dvander@alliedmods.net>
Mon Jul 23 19:12:54 2007 +0000 (2007-07-23)
changeset 1147 437e1f00a590
parent 1119 9e3486386c04
child 1231 17badaf66ce4
permissions -rw-r--r--
new jit api versioning scheme for amb398
     1 /**
     2  * vim: set ts=4 :
     3  * ================================================================
     4  * SourcePawn (C)2004-2007 AlliedModders LLC.  All rights reserved.
     5  * ================================================================
     6  *
     7  * This file is not open source and may not be copied without explicit
     8  * written permission of AlliedModders LLC.  This file may not be redistributed 
     9  * in whole or significant part.
    10  * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
    11  *
    12  * Version: $Id$
    13  */
    14 
    15 #include <malloc.h>
    16 #include <string.h>
    17 #include <assert.h>
    18 #include "sp_file_headers.h"
    19 #include "sp_vm_types.h"
    20 #include "sp_vm_engine.h"
    21 #include "zlib/zlib.h"
    22 #include "sp_vm_basecontext.h"
    23 
    24 #if defined WIN32
    25  #define WIN32_LEAN_AND_MEAN
    26  #include <windows.h>
    27 #elif defined __GNUC__
    28  #include <sys/mman.h>
    29 #endif
    30 
    31 #define INVALID_CIP			0xFFFFFFFF
    32 
    33 using namespace SourcePawn;
    34 
    35 #define ERROR_MESSAGE_MAX		25
    36 static const char *g_ErrorMsgTable[] = 
    37 {
    38 	NULL,
    39 	"Unrecognizable file format",
    40 	"Decompressor was not found",
    41 	"Not enough space on the heap",
    42 	"Invalid parameter or parameter type",
    43 	"Invalid plugin address",
    44 	"Object or index not found",
    45 	"Invalid index or index not found",
    46 	"Not enough space on the stack",
    47 	"Debug section not found or debug not enabled",
    48 	"Invalid instruction",
    49 	"Invalid memory access",
    50 	"Stack went below stack boundary",
    51 	"Heap went below heap boundary",
    52 	"Divide by zero",
    53 	"Array index is out of bounds",
    54 	"Instruction contained invalid parameter",
    55 	"Stack memory leaked by native",
    56 	"Heap memory leaked by native",
    57 	"Dynamic array is too big",
    58 	"Tracker stack is out of bounds",
    59 	"Native is not bound",
    60 	"Maximum number of parameters reached",
    61 	"Native detected error",
    62 	"Plugin not runnable",
    63 	"Call was aborted",
    64 };
    65 
    66 SourcePawnEngine::SourcePawnEngine()
    67 {
    68 	m_pDebugHook = NULL;
    69 	m_CallStack = NULL;
    70 	m_FreedCalls = NULL;
    71 	m_CurChain = 0;
    72 #if 0
    73 	m_pFreeFuncs = NULL;
    74 #endif
    75 }
    76 
    77 SourcePawnEngine::~SourcePawnEngine()
    78 {
    79 	TracedCall *pTemp;
    80 	while (m_FreedCalls)
    81 	{
    82 		pTemp = m_FreedCalls->next;
    83 		delete m_FreedCalls;
    84 		m_FreedCalls = pTemp;
    85 	}
    86 
    87 #if 0
    88 	CFunction *pNext;
    89 	while (m_pFreeFuncs)
    90 	{
    91 		pNext = m_pFreeFuncs->m_pNext;
    92 		delete m_pFreeFuncs;
    93 		m_pFreeFuncs = pNext;
    94 	}
    95 #endif
    96 }
    97 
    98 void *SourcePawnEngine::ExecAlloc(size_t size)
    99 {
   100 #if defined WIN32
   101 	return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
   102 #elif defined __GNUC__
   103 	void *base = memalign(sysconf(_SC_PAGESIZE), size);
   104 	if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
   105 	{
   106 		free(base);
   107 		return NULL;
   108 	}
   109 	return base;
   110 #endif
   111 }
   112 
   113 void SourcePawnEngine::ExecFree(void *address)
   114 {
   115 #if defined WIN32
   116 	VirtualFree(address, 0, MEM_RELEASE);
   117 #elif defined __GNUC__
   118 	free(address);
   119 #endif
   120 }
   121 
   122 void *SourcePawnEngine::BaseAlloc(size_t size)
   123 {
   124 	return malloc(size);
   125 }
   126 
   127 void SourcePawnEngine::BaseFree(void *memory)
   128 {
   129 	free(memory);
   130 }
   131 
   132 IPluginContext *SourcePawnEngine::CreateBaseContext(sp_context_t *ctx)
   133 {
   134 	return new BaseContext(ctx);
   135 }
   136 
   137 void SourcePawnEngine::FreeBaseContext(IPluginContext *ctx)
   138 {
   139 	delete ctx;
   140 }
   141 
   142 sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, int *err)
   143 {
   144 	char *nameptr;
   145 	uint8_t sectnum = 0;
   146 	sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
   147 
   148 	memset(plugin, 0, sizeof(sp_plugin_t));
   149 
   150 	plugin->base = base;
   151 
   152 	while (sectnum < hdr->sections)
   153 	{
   154 		nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
   155 
   156 		if (!(plugin->pcode) && !strcmp(nameptr, ".code"))
   157 		{
   158 			sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
   159 			plugin->pcode = base + secptr->dataoffs + cod->code;
   160 			plugin->pcode_size = cod->codesize;
   161 			plugin->flags = cod->flags;
   162 		}
   163 		else if (!(plugin->data) && !strcmp(nameptr, ".data"))
   164 		{
   165 			sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs);
   166 			plugin->data = base + secptr->dataoffs + dat->data;
   167 			plugin->data_size = dat->datasize;
   168 			plugin->memory = dat->memsize;
   169 		}
   170 		else if (!(plugin->info.publics) && !strcmp(nameptr, ".publics"))
   171 		{
   172 			plugin->info.publics_num = secptr->size / sizeof(sp_file_publics_t);
   173 			plugin->info.publics = (sp_file_publics_t *)(base + secptr->dataoffs);
   174 		}
   175 		else if (!(plugin->info.pubvars) && !strcmp(nameptr, ".pubvars"))
   176 		{
   177 			plugin->info.pubvars_num = secptr->size / sizeof(sp_file_pubvars_t);
   178 			plugin->info.pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
   179 		}
   180 		else if (!(plugin->info.natives) && !strcmp(nameptr, ".natives"))
   181 		{
   182 			plugin->info.natives_num = secptr->size / sizeof(sp_file_natives_t);
   183 			plugin->info.natives = (sp_file_natives_t *)(base + secptr->dataoffs);
   184 		}
   185 		else if (!(plugin->info.stringbase) && !strcmp(nameptr, ".names"))
   186 		{
   187 			plugin->info.stringbase = (const char *)(base + secptr->dataoffs);
   188 		}
   189 		else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files"))
   190 		{
   191 			plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
   192 		}
   193 		else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines"))
   194 		{
   195 			plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
   196 		}
   197 		else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols"))
   198 		{
   199 			plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
   200 		}
   201 		else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info"))
   202 		{
   203 			sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs);
   204 			plugin->debug.files_num = inf->num_files;
   205 			plugin->debug.lines_num = inf->num_lines;
   206 			plugin->debug.syms_num = inf->num_syms;
   207 		}
   208 		else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings"))
   209 		{
   210 			plugin->debug.stringbase = (const char *)(base + secptr->dataoffs);
   211 		}
   212 
   213 		secptr++;
   214 		sectnum++;
   215 	}
   216 
   217 	if (!(plugin->pcode) || !(plugin->data) || !(plugin->info.stringbase))
   218 	{
   219 		goto return_error;
   220 	}
   221 
   222 	if ((plugin->flags & SP_FLAG_DEBUG) && (!(plugin->debug.files) || !(plugin->debug.lines) || !(plugin->debug.symbols)))
   223 	{
   224 		goto return_error;
   225 	}
   226 
   227 	if (err)
   228 	{
   229 		*err = SP_ERROR_NONE;
   230 	}
   231 
   232 	return plugin;
   233 
   234 return_error:
   235 	if (err)
   236 	{
   237 		*err = SP_ERROR_FILE_FORMAT;
   238 	}
   239 
   240 	return NULL;
   241 }
   242 
   243 sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
   244 {
   245 	sp_file_hdr_t hdr;
   246 	sp_plugin_t *plugin;
   247 	uint8_t *base;
   248 	int z_result;
   249 	int error;
   250 
   251 	if (!fp)
   252 	{
   253 		error = SP_ERROR_NOT_FOUND;
   254 		goto return_error;
   255 	}
   256 
   257 	/* Rewind for safety */
   258 	rewind(fp);
   259 	fread(&hdr, sizeof(sp_file_hdr_t), 1, fp);
   260 
   261 	if (hdr.magic != SPFILE_MAGIC)
   262 	{
   263 		error = SP_ERROR_FILE_FORMAT;
   264 		goto return_error;
   265 	}
   266 
   267 	switch (hdr.compression)
   268 	{
   269 	case SPFILE_COMPRESSION_GZ:
   270 		{
   271 			uint32_t uncompsize = hdr.imagesize - hdr.dataoffs;
   272 			uint32_t compsize = hdr.disksize - hdr.dataoffs;
   273 			uint32_t sectsize = hdr.dataoffs - sizeof(sp_file_hdr_t);
   274 			uLongf destlen = uncompsize;
   275 
   276 			char *tempbuf = (char *)malloc(compsize);
   277 			void *uncompdata = malloc(uncompsize);
   278 			void *sectheader = malloc(sectsize);
   279 
   280 			fread(sectheader, sectsize, 1, fp);
   281 			fread(tempbuf, compsize, 1, fp);
   282 
   283 			z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize);
   284 			free(tempbuf);
   285 			if (z_result != Z_OK)
   286 			{
   287 				free(sectheader);
   288 				free(uncompdata);
   289 				error = SP_ERROR_DECOMPRESSOR;
   290 				goto return_error;
   291 			}
   292 
   293 			base = (uint8_t *)malloc(hdr.imagesize);
   294 			memcpy(base, &hdr, sizeof(sp_file_hdr_t));
   295 			memcpy(base + sizeof(sp_file_hdr_t), sectheader, sectsize);
   296 			free(sectheader);
   297 			memcpy(base + hdr.dataoffs, uncompdata, uncompsize);
   298 			free(uncompdata);
   299 			break;
   300 		}
   301 	case SPFILE_COMPRESSION_NONE:
   302 		{
   303 			base = (uint8_t *)malloc(hdr.imagesize);
   304 			rewind(fp);
   305 			fread(base, hdr.imagesize, 1, fp);
   306 			break;
   307 		}
   308 	default:
   309 		{
   310 			error = SP_ERROR_DECOMPRESSOR;
   311 			goto return_error;
   312 		}
   313 	}
   314 
   315 	plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
   316 	if (!_ReadPlugin(&hdr, base, plugin, err))
   317 	{
   318 		free(plugin);
   319 		free(base);
   320 		return NULL;
   321 	}
   322 
   323 	plugin->allocflags = 0;
   324 
   325 	return plugin;
   326 
   327 return_error:
   328 	if (err)
   329 	{
   330 		*err = error;
   331 	}
   332 	return NULL;
   333 }
   334 
   335 sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
   336 {
   337 	sp_file_hdr_t hdr;
   338 	uint8_t noptr = 0;
   339 
   340 	memcpy(&hdr, base, sizeof(sp_file_hdr_t));
   341 
   342 	if (!plugin)
   343 	{
   344 		plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
   345 		noptr = 1;
   346 	}
   347 
   348 	if (!_ReadPlugin(&hdr, (uint8_t *)base, plugin, err))
   349 	{
   350 		if (noptr)
   351 		{
   352 			free(plugin);
   353 		}
   354 		return NULL;
   355 	}
   356 
   357 	if (!noptr)
   358 	{
   359 		plugin->allocflags |= SP_FA_SELF_EXTERNAL;
   360 	}
   361 	plugin->allocflags |= SP_FA_BASE_EXTERNAL;
   362 
   363 	return plugin;
   364 }
   365 
   366 int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
   367 {
   368 	if (!(plugin->allocflags & SP_FA_BASE_EXTERNAL))
   369 	{
   370 		free(plugin->base);
   371 		plugin->base = NULL;
   372 	}
   373 	if (!(plugin->allocflags & SP_FA_SELF_EXTERNAL))
   374 	{
   375 		free(plugin);
   376 	}
   377 
   378 	return SP_ERROR_NONE;
   379 }
   380 
   381 #if 0
   382 void SourcePawnEngine::ReleaseFunctionToPool(CFunction *func)
   383 {
   384 	if (!func)
   385 	{
   386 		return;
   387 	}
   388 	func->Cancel();
   389 	func->m_pNext = m_pFreeFuncs;
   390 	m_pFreeFuncs = func;
   391 }
   392 
   393 CFunction *SourcePawnEngine::GetFunctionFromPool(funcid_t f, IPluginContext *plugin)
   394 {
   395 	if (!m_pFreeFuncs)
   396 	{
   397 		return new CFunction(f, plugin);
   398 	} else {
   399 		CFunction *pFunc = m_pFreeFuncs;
   400 		m_pFreeFuncs = m_pFreeFuncs->m_pNext;
   401 		pFunc->Set(f, plugin);
   402 		return pFunc;
   403 	}
   404 }
   405 #endif
   406 
   407 IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
   408 {
   409 	IDebugListener *old = m_pDebugHook;
   410 
   411 	m_pDebugHook = pListener;
   412 
   413 	return old;
   414 }
   415 
   416 unsigned int SourcePawnEngine::GetContextCallCount()
   417 {
   418 	if (!m_CallStack)
   419 	{
   420 		return 0;
   421 	}
   422 
   423 	return m_CallStack->chain;
   424 }
   425 
   426 TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
   427 {
   428 	TracedCall *pCall;
   429 
   430 	if (!m_FreedCalls)
   431 	{
   432 		pCall = new TracedCall;
   433 	} else {
   434 		/* Unlink the head node from the free list */
   435 		pCall = m_FreedCalls;
   436 		m_FreedCalls = m_FreedCalls->next;
   437 	}
   438 
   439 	/* Link as the head node into the call stack */
   440 	pCall->next = m_CallStack;
   441 
   442 	if (new_chain)
   443 	{
   444 		pCall->chain = ++m_CurChain;
   445 	} else {
   446 		pCall->chain = m_CurChain;
   447 	}
   448 
   449 	m_CallStack = pCall;
   450 
   451 	return pCall;
   452 }
   453 
   454 void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
   455 {
   456 	/* Check if this is the top of the call stack */
   457 	if (pCall == m_CallStack)
   458 	{
   459 		m_CallStack = m_CallStack->next;
   460 	}
   461 
   462 	/* Add this to our linked list of freed calls */
   463 	if (!m_FreedCalls)
   464 	{
   465 		m_FreedCalls = pCall;
   466 		m_FreedCalls->next = NULL;
   467 	} else {
   468 		pCall->next = m_FreedCalls;
   469 		m_FreedCalls = pCall;
   470 	}
   471 }
   472 
   473 void SourcePawnEngine::PushTracer(sp_context_t *ctx)
   474 {
   475 	TracedCall *pCall = MakeTracedCall(true);
   476 
   477 	pCall->cip = INVALID_CIP;
   478 	pCall->ctx = ctx;
   479 	pCall->frm = INVALID_CIP;
   480 }
   481 
   482 void SourcePawnEngine::RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip)
   483 {
   484 	assert(m_CallStack != NULL);
   485 	assert(m_CallStack->ctx == ctx);
   486 	assert(m_CallStack->chain == m_CurChain);
   487 
   488 	if (m_CallStack->cip == INVALID_CIP)
   489 	{
   490 		/* We aren't logging anything yet, so begin the trace */
   491 		m_CallStack->cip = codeip;
   492 		m_CallStack->frm = frame;
   493 	} else {
   494 		if (m_CallStack->frm > frame)
   495 		{
   496 			/* The last frame has moved down the stack, 
   497 			 * so we have to push a new call onto our list.
   498 			 */
   499 			TracedCall *pCall = MakeTracedCall(false);
   500 			pCall->ctx = ctx;
   501 			pCall->frm = frame;
   502 		} else if (m_CallStack->frm < frame) {
   503 			/* The last frame has moved up the stack,
   504 			 * so we have to pop the call from our list.
   505 			 */
   506 			FreeTracedCall(m_CallStack);
   507 		}
   508 		/* no matter where we are, update the cip */
   509 		m_CallStack->cip = codeip;
   510 	}
   511 }
   512 
   513 void SourcePawnEngine::PopTracer(int error, const char *msg)
   514 {
   515 	assert(m_CallStack != NULL);
   516 
   517 	if (error != SP_ERROR_NONE && m_pDebugHook)
   518 	{
   519 		uint32_t native = INVALID_CIP;
   520 
   521 		if (m_CallStack->ctx->n_err)
   522 		{
   523 			native = m_CallStack->ctx->n_idx;
   524 		}
   525 
   526 		CContextTrace trace(m_CallStack, error, msg, native);
   527 		m_pDebugHook->OnContextExecuteError(m_CallStack->ctx->context, &trace);
   528 	}
   529 
   530 	/* Now pop the error chain  */
   531 	while (m_CallStack && m_CallStack->chain == m_CurChain)
   532 	{
   533 		FreeTracedCall(m_CallStack);
   534 	}
   535 
   536 	m_CurChain--;
   537 }
   538 
   539 unsigned int SourcePawnEngine::GetEngineAPIVersion()
   540 {
   541 	return SOURCEPAWN_ENGINE_API_VERSION;
   542 }
   543 
   544 CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native) : 
   545  m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart), m_Native(native)
   546 {
   547 }
   548 
   549 bool CContextTrace::DebugInfoAvailable()
   550 {
   551 	return ((m_pStart->ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
   552 }
   553 
   554 const char *CContextTrace::GetCustomErrorString()
   555 {
   556 	return m_pMsg;
   557 }
   558 
   559 int CContextTrace::GetErrorCode()
   560 {
   561 	return m_Error;
   562 }
   563 
   564 const char *CContextTrace::GetErrorString()
   565 {
   566 	if (m_Error > ERROR_MESSAGE_MAX || 
   567 		m_Error < 1)
   568 	{
   569 		return "Invalid error code";
   570 	} else {
   571 		return g_ErrorMsgTable[m_Error];
   572 	}
   573 }
   574 
   575 void CContextTrace::ResetTrace()
   576 {
   577 	m_pIterator = m_pStart;
   578 }
   579 
   580 bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
   581 {
   582 	if (!m_pIterator || (m_pIterator->chain != m_pStart->chain))
   583 	{
   584 		return false;
   585 	}
   586 
   587 	if (m_pIterator->cip == INVALID_CIP)
   588 	{
   589 		return false;
   590 	}
   591 
   592 	IPluginContext *pContext = m_pIterator->ctx->context;
   593 	IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
   594 
   595 	if (!pInfo)
   596 	{
   597 		return false;
   598 	}
   599 
   600 	if (!trace)
   601 	{
   602 		m_pIterator = m_pIterator->next;
   603 		return true;
   604 	}
   605 
   606 	if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
   607 	{
   608 		trace->filename = NULL;
   609 	}
   610 
   611 	if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
   612 	{
   613 		trace->function = NULL;
   614 	}
   615 
   616 	if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
   617 	{
   618 		trace->line = 0;
   619 	}
   620 
   621 	m_pIterator = m_pIterator->next;
   622 
   623 	return true;
   624 }
   625 
   626 const char *CContextTrace::GetLastNative(uint32_t *index)
   627 {
   628 	if (m_Native == INVALID_CIP)
   629 	{
   630 		return NULL;
   631 	}
   632 
   633 	sp_native_t *native;
   634 	if (m_pIterator->ctx->context->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE)
   635 	{
   636 		return NULL;
   637 	}
   638 
   639 	if (index)
   640 	{
   641 		*index = m_Native;
   642 	}
   643 
   644 	return native->name;
   645 }