4 #include "sp_file_headers.h"
5 #include "sp_vm_types.h"
6 #include "sp_vm_engine.h"
8 #include "sp_vm_basecontext.h"
11 #define WIN32_LEAN_AND_MEAN
13 #else if defined __GNUC__
17 #define INVALID_CIP 0xFFFFFFFF
19 using namespace SourcePawn;
21 #define ERROR_MESSAGE_MAX 23
22 static const char *g_ErrorMsgTable[] =
25 "Unrecognizable file format",
26 "Decompressor was not found",
27 "Not enough space on the heap",
28 "Invalid parameter or parameter type",
29 "Invalid plugin address",
30 "Object or index not found",
31 "Invalid index or index not found",
32 "Not enough space on the stack",
33 "Debug section not found or debug not enabled",
34 "Invalid instruction",
35 "Invalid memory access",
36 "Stack went below stack boundary",
37 "Heap went below heap boundary",
39 "Array index is out of bounds",
40 "Instruction contained invalid parameter",
41 "Stack memory leaked by native",
42 "Heap memory leaked by native",
43 "Dynamic array is too big",
44 "Tracker stack is out of bounds",
45 "Native was never bound",
46 "Maximum number of parameters reached",
47 "Native detected error",
50 SourcePawnEngine::SourcePawnEngine()
58 SourcePawnEngine::~SourcePawnEngine()
60 assert(m_CallStack == NULL);
65 pTemp = m_FreedCalls->next;
71 void *SourcePawnEngine::ExecAlloc(size_t size)
74 return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
75 #else if defined __GNUC__
76 void *base = memalign(sysconf(_SC_PAGESIZE), size);
77 if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
86 void SourcePawnEngine::ExecFree(void *address)
89 VirtualFree(address, 0, MEM_RELEASE);
90 #else if defined __GNUC__
95 void *SourcePawnEngine::BaseAlloc(size_t size)
100 void SourcePawnEngine::BaseFree(void *memory)
105 IPluginContext *SourcePawnEngine::CreateBaseContext(sp_context_t *ctx)
107 return new BaseContext(ctx);
110 void SourcePawnEngine::FreeBaseContext(IPluginContext *ctx)
115 sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, int *err)
119 sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
121 memset(plugin, 0, sizeof(sp_plugin_t));
125 while (sectnum < hdr->sections)
127 nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
129 if (!(plugin->pcode) && !strcmp(nameptr, ".code"))
131 sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
132 plugin->pcode = base + secptr->dataoffs + cod->code;
133 plugin->pcode_size = cod->codesize;
134 plugin->flags = cod->flags;
136 else if (!(plugin->data) && !strcmp(nameptr, ".data"))
138 sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs);
139 plugin->data = base + secptr->dataoffs + dat->data;
140 plugin->data_size = dat->datasize;
141 plugin->memory = dat->memsize;
143 else if (!(plugin->info.publics) && !strcmp(nameptr, ".publics"))
145 plugin->info.publics_num = secptr->size / sizeof(sp_file_publics_t);
146 plugin->info.publics = (sp_file_publics_t *)(base + secptr->dataoffs);
148 else if (!(plugin->info.pubvars) && !strcmp(nameptr, ".pubvars"))
150 plugin->info.pubvars_num = secptr->size / sizeof(sp_file_pubvars_t);
151 plugin->info.pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
153 else if (!(plugin->info.natives) && !strcmp(nameptr, ".natives"))
155 plugin->info.natives_num = secptr->size / sizeof(sp_file_natives_t);
156 plugin->info.natives = (sp_file_natives_t *)(base + secptr->dataoffs);
158 else if (!(plugin->info.lib) && !strcmp(nameptr, ".libraries"))
160 plugin->info.libraries_num = secptr->size / sizeof(sp_file_libraries_t);
161 plugin->info.lib = (sp_file_libraries_t *)(base + secptr->dataoffs);
163 else if (!(plugin->info.stringbase) && !strcmp(nameptr, ".names"))
165 plugin->info.stringbase = (const char *)(base + secptr->dataoffs);
167 else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files"))
169 plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
171 else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines"))
173 plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
175 else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols"))
177 plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
179 else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info"))
181 sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs);
182 plugin->debug.files_num = inf->num_files;
183 plugin->debug.lines_num = inf->num_lines;
184 plugin->debug.syms_num = inf->num_syms;
186 else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings"))
188 plugin->debug.stringbase = (const char *)(base + secptr->dataoffs);
195 if (!(plugin->pcode) || !(plugin->data) || !(plugin->info.stringbase))
200 if ((plugin->flags & SP_FLAG_DEBUG) && (!(plugin->debug.files) || !(plugin->debug.lines) || !(plugin->debug.symbols)))
207 *err = SP_ERROR_NONE;
215 *err = SP_ERROR_FILE_FORMAT;
221 sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
231 error = SP_ERROR_NOT_FOUND;
235 /* Rewind for safety */
237 fread(&hdr, sizeof(sp_file_hdr_t), 1, fp);
239 if (hdr.magic != SPFILE_MAGIC)
241 error = SP_ERROR_FILE_FORMAT;
245 switch (hdr.compression)
247 case SPFILE_COMPRESSION_GZ:
249 uint32_t uncompsize = hdr.imagesize - hdr.dataoffs;
250 uint32_t compsize = hdr.disksize - hdr.dataoffs;
251 uint32_t sectsize = hdr.dataoffs - sizeof(sp_file_hdr_t);
252 uLongf destlen = uncompsize;
254 char *tempbuf = (char *)malloc(compsize);
255 void *uncompdata = malloc(uncompsize);
256 void *sectheader = malloc(sectsize);
258 fread(sectheader, sectsize, 1, fp);
259 fread(tempbuf, compsize, 1, fp);
261 z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize);
263 if (z_result != Z_OK)
267 error = SP_ERROR_DECOMPRESSOR;
271 base = (uint8_t *)malloc(hdr.imagesize);
272 memcpy(base, &hdr, sizeof(sp_file_hdr_t));
273 memcpy(base + sizeof(sp_file_hdr_t), sectheader, sectsize);
275 memcpy(base + hdr.dataoffs, uncompdata, uncompsize);
279 case SPFILE_COMPRESSION_NONE:
281 base = (uint8_t *)malloc(hdr.imagesize);
283 fread(base, hdr.imagesize, 1, fp);
288 error = SP_ERROR_DECOMPRESSOR;
293 plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
294 if (!_ReadPlugin(&hdr, base, plugin, err))
301 plugin->allocflags = 0;
313 sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
318 memcpy(&hdr, base, sizeof(sp_file_hdr_t));
322 plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
326 if (!_ReadPlugin(&hdr, (uint8_t *)base, plugin, err))
337 plugin->allocflags |= SP_FA_SELF_EXTERNAL;
339 plugin->allocflags |= SP_FA_BASE_EXTERNAL;
344 int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
346 if (!(plugin->allocflags & SP_FA_BASE_EXTERNAL))
351 if (!(plugin->allocflags & SP_FA_SELF_EXTERNAL))
356 return SP_ERROR_NONE;
359 IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
361 IDebugListener *old = m_pDebugHook;
363 m_pDebugHook = pListener;
368 unsigned int SourcePawnEngine::GetContextCallCount()
375 return m_CallStack->chain;
378 TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
384 pCall = new TracedCall;
386 /* Unlink the head node from the free list */
387 pCall = m_FreedCalls;
388 m_FreedCalls = m_FreedCalls->next;
391 m_FreedCalls->prev = NULL;
395 /* Link as the head node into the call stack */
396 pCall->next = m_CallStack;
401 pCall->chain = ++m_CurChain;
403 pCall->chain = m_CurChain;
408 m_CallStack->prev = pCall;
415 void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
417 /* Check if this is the top of the call stack */
418 if (pCall == m_CallStack)
420 m_CallStack = m_CallStack->next;
423 m_CallStack->prev = NULL;
427 /* Add this to our linked list of freed calls */
430 m_FreedCalls = pCall;
431 m_FreedCalls->next = NULL;
432 m_FreedCalls->prev = NULL;
434 pCall->next = m_FreedCalls;
436 m_FreedCalls->prev = pCall;
437 m_FreedCalls = pCall;
441 void SourcePawnEngine::PushTracer(sp_context_t *ctx)
443 TracedCall *pCall = MakeTracedCall(true);
445 pCall->cip = INVALID_CIP;
447 pCall->frm = INVALID_CIP;
450 void SourcePawnEngine::RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip)
452 assert(m_CallStack != NULL);
453 assert(m_CallStack->ctx == ctx);
454 assert(m_CallStack->chain == m_CurChain);
456 if (m_CallStack->cip == INVALID_CIP)
458 /* We aren't logging anything yet, so begin the trace */
459 m_CallStack->cip = codeip;
460 m_CallStack->frm = frame;
462 if (m_CallStack->frm > frame)
464 /* The last frame has moved down the stack,
465 * so we have to push a new call onto our list.
467 TracedCall *pCall = MakeTracedCall(false);
469 } else if (m_CallStack->frm < frame) {
470 /* The last frame has moved up the stack,
471 * so we have to pop the call from our list.
473 FreeTracedCall(m_CallStack);
475 /* no matter where we are, update the cip */
476 m_CallStack->cip = codeip;
480 void SourcePawnEngine::PopTracer(int error, const char *msg)
482 assert(m_CallStack != NULL);
484 if (error != SP_ERROR_NONE && m_pDebugHook)
486 CContextTrace trace(m_CallStack, error, msg);
487 m_pDebugHook->OnContextExecuteError(m_CallStack->ctx->context, &trace);
490 /* Now pop the error chain */
491 while (m_CallStack && m_CallStack->chain == m_CurChain)
493 FreeTracedCall(m_CallStack);
499 CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg) :
500 m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart)
504 bool CContextTrace::DebugInfoAvailable()
506 return ((m_pStart->ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
509 const char *CContextTrace::GetCustomErrorString()
514 int CContextTrace::GetErrorCode()
519 const char *CContextTrace::GetErrorString()
521 if (m_Error > ERROR_MESSAGE_MAX ||
524 return "Invalid error code";
526 return g_ErrorMsgTable[m_Error];
530 void CContextTrace::ResetTrace()
532 m_pIterator = m_pStart;
535 bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
537 if (m_pIterator->chain != m_pStart->chain)
542 if (m_pIterator->cip == INVALID_CIP)
547 IPluginContext *pContext = m_pIterator->ctx->context;
548 IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
550 m_pIterator = m_pIterator->next;
562 if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
564 trace->filename = NULL;
567 if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
569 trace->function = NULL;
572 if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)