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