3 * ================================================================
4 * SourcePawn (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ================================================================
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
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"
25 #define WIN32_LEAN_AND_MEAN
27 #elif defined __GNUC__
31 #define INVALID_CIP 0xFFFFFFFF
33 using namespace SourcePawn;
35 #define ERROR_MESSAGE_MAX 25
36 static const char *g_ErrorMsgTable[] =
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",
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",
66 SourcePawnEngine::SourcePawnEngine()
77 SourcePawnEngine::~SourcePawnEngine()
82 pTemp = m_FreedCalls->next;
91 pNext = m_pFreeFuncs->m_pNext;
98 void *SourcePawnEngine::ExecAlloc(size_t size)
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)
113 void SourcePawnEngine::ExecFree(void *address)
116 VirtualFree(address, 0, MEM_RELEASE);
117 #elif defined __GNUC__
122 void *SourcePawnEngine::BaseAlloc(size_t size)
127 void SourcePawnEngine::BaseFree(void *memory)
132 IPluginContext *SourcePawnEngine::CreateBaseContext(sp_context_t *ctx)
134 return new BaseContext(ctx);
137 void SourcePawnEngine::FreeBaseContext(IPluginContext *ctx)
142 sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, int *err)
146 sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
148 memset(plugin, 0, sizeof(sp_plugin_t));
152 while (sectnum < hdr->sections)
154 nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
156 if (!(plugin->pcode) && !strcmp(nameptr, ".code"))
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;
163 else if (!(plugin->data) && !strcmp(nameptr, ".data"))
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;
170 else if (!(plugin->info.publics) && !strcmp(nameptr, ".publics"))
172 plugin->info.publics_num = secptr->size / sizeof(sp_file_publics_t);
173 plugin->info.publics = (sp_file_publics_t *)(base + secptr->dataoffs);
175 else if (!(plugin->info.pubvars) && !strcmp(nameptr, ".pubvars"))
177 plugin->info.pubvars_num = secptr->size / sizeof(sp_file_pubvars_t);
178 plugin->info.pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
180 else if (!(plugin->info.natives) && !strcmp(nameptr, ".natives"))
182 plugin->info.natives_num = secptr->size / sizeof(sp_file_natives_t);
183 plugin->info.natives = (sp_file_natives_t *)(base + secptr->dataoffs);
185 else if (!(plugin->info.stringbase) && !strcmp(nameptr, ".names"))
187 plugin->info.stringbase = (const char *)(base + secptr->dataoffs);
189 else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files"))
191 plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
193 else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines"))
195 plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
197 else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols"))
199 plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
201 else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info"))
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;
208 else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings"))
210 plugin->debug.stringbase = (const char *)(base + secptr->dataoffs);
217 if (!(plugin->pcode) || !(plugin->data) || !(plugin->info.stringbase))
222 if ((plugin->flags & SP_FLAG_DEBUG) && (!(plugin->debug.files) || !(plugin->debug.lines) || !(plugin->debug.symbols)))
229 *err = SP_ERROR_NONE;
237 *err = SP_ERROR_FILE_FORMAT;
243 sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
253 error = SP_ERROR_NOT_FOUND;
257 /* Rewind for safety */
259 fread(&hdr, sizeof(sp_file_hdr_t), 1, fp);
261 if (hdr.magic != SPFILE_MAGIC)
263 error = SP_ERROR_FILE_FORMAT;
267 switch (hdr.compression)
269 case SPFILE_COMPRESSION_GZ:
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;
276 char *tempbuf = (char *)malloc(compsize);
277 void *uncompdata = malloc(uncompsize);
278 void *sectheader = malloc(sectsize);
280 fread(sectheader, sectsize, 1, fp);
281 fread(tempbuf, compsize, 1, fp);
283 z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize);
285 if (z_result != Z_OK)
289 error = SP_ERROR_DECOMPRESSOR;
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);
297 memcpy(base + hdr.dataoffs, uncompdata, uncompsize);
301 case SPFILE_COMPRESSION_NONE:
303 base = (uint8_t *)malloc(hdr.imagesize);
305 fread(base, hdr.imagesize, 1, fp);
310 error = SP_ERROR_DECOMPRESSOR;
315 plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
316 if (!_ReadPlugin(&hdr, base, plugin, err))
323 plugin->allocflags = 0;
335 sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
340 memcpy(&hdr, base, sizeof(sp_file_hdr_t));
344 plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
348 if (!_ReadPlugin(&hdr, (uint8_t *)base, plugin, err))
359 plugin->allocflags |= SP_FA_SELF_EXTERNAL;
361 plugin->allocflags |= SP_FA_BASE_EXTERNAL;
366 int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
368 if (!(plugin->allocflags & SP_FA_BASE_EXTERNAL))
373 if (!(plugin->allocflags & SP_FA_SELF_EXTERNAL))
378 return SP_ERROR_NONE;
382 void SourcePawnEngine::ReleaseFunctionToPool(CFunction *func)
389 func->m_pNext = m_pFreeFuncs;
393 CFunction *SourcePawnEngine::GetFunctionFromPool(funcid_t f, IPluginContext *plugin)
397 return new CFunction(f, plugin);
399 CFunction *pFunc = m_pFreeFuncs;
400 m_pFreeFuncs = m_pFreeFuncs->m_pNext;
401 pFunc->Set(f, plugin);
407 IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
409 IDebugListener *old = m_pDebugHook;
411 m_pDebugHook = pListener;
416 unsigned int SourcePawnEngine::GetContextCallCount()
423 return m_CallStack->chain;
426 TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
432 pCall = new TracedCall;
434 /* Unlink the head node from the free list */
435 pCall = m_FreedCalls;
436 m_FreedCalls = m_FreedCalls->next;
439 /* Link as the head node into the call stack */
440 pCall->next = m_CallStack;
444 pCall->chain = ++m_CurChain;
446 pCall->chain = m_CurChain;
454 void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
456 /* Check if this is the top of the call stack */
457 if (pCall == m_CallStack)
459 m_CallStack = m_CallStack->next;
462 /* Add this to our linked list of freed calls */
465 m_FreedCalls = pCall;
466 m_FreedCalls->next = NULL;
468 pCall->next = m_FreedCalls;
469 m_FreedCalls = pCall;
473 void SourcePawnEngine::PushTracer(sp_context_t *ctx)
475 TracedCall *pCall = MakeTracedCall(true);
477 pCall->cip = INVALID_CIP;
479 pCall->frm = INVALID_CIP;
482 void SourcePawnEngine::RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip)
484 assert(m_CallStack != NULL);
485 assert(m_CallStack->ctx == ctx);
486 assert(m_CallStack->chain == m_CurChain);
488 if (m_CallStack->cip == INVALID_CIP)
490 /* We aren't logging anything yet, so begin the trace */
491 m_CallStack->cip = codeip;
492 m_CallStack->frm = frame;
494 if (m_CallStack->frm > frame)
496 /* The last frame has moved down the stack,
497 * so we have to push a new call onto our list.
499 TracedCall *pCall = MakeTracedCall(false);
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.
506 FreeTracedCall(m_CallStack);
508 /* no matter where we are, update the cip */
509 m_CallStack->cip = codeip;
513 void SourcePawnEngine::PopTracer(int error, const char *msg)
515 assert(m_CallStack != NULL);
517 if (error != SP_ERROR_NONE && m_pDebugHook)
519 uint32_t native = INVALID_CIP;
521 if (m_CallStack->ctx->n_err)
523 native = m_CallStack->ctx->n_idx;
526 CContextTrace trace(m_CallStack, error, msg, native);
527 m_pDebugHook->OnContextExecuteError(m_CallStack->ctx->context, &trace);
530 /* Now pop the error chain */
531 while (m_CallStack && m_CallStack->chain == m_CurChain)
533 FreeTracedCall(m_CallStack);
539 unsigned int SourcePawnEngine::GetEngineAPIVersion()
541 return SOURCEPAWN_ENGINE_API_VERSION;
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)
549 bool CContextTrace::DebugInfoAvailable()
551 return ((m_pStart->ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
554 const char *CContextTrace::GetCustomErrorString()
559 int CContextTrace::GetErrorCode()
564 const char *CContextTrace::GetErrorString()
566 if (m_Error > ERROR_MESSAGE_MAX ||
569 return "Invalid error code";
571 return g_ErrorMsgTable[m_Error];
575 void CContextTrace::ResetTrace()
577 m_pIterator = m_pStart;
580 bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
582 if (!m_pIterator || (m_pIterator->chain != m_pStart->chain))
587 if (m_pIterator->cip == INVALID_CIP)
592 IPluginContext *pContext = m_pIterator->ctx->context;
593 IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
602 m_pIterator = m_pIterator->next;
606 if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
608 trace->filename = NULL;
611 if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
613 trace->function = NULL;
616 if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
621 m_pIterator = m_pIterator->next;
626 const char *CContextTrace::GetLastNative(uint32_t *index)
628 if (m_Native == INVALID_CIP)
634 if (m_pIterator->ctx->context->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE)