IPluginFunction implementation is re-entrant across native calls, as heap allocations are delayed until execution
removed ICallable::GetAddressOfPushedParam
removed phys_addr from ICallable::PushArray
fixed a bug where sp_context_t::n_idx was overwritten upon re-entrant calls
2 * ================================================================
3 * SourcePawn (C)2004-2007 AlliedModders LLC. All rights reserved.
4 * ================================================================
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
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"
24 #define WIN32_LEAN_AND_MEAN
26 #elif defined __GNUC__
30 #define INVALID_CIP 0xFFFFFFFF
32 using namespace SourcePawn;
34 #define ERROR_MESSAGE_MAX 25
35 static const char *g_ErrorMsgTable[] =
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",
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",
65 SourcePawnEngine::SourcePawnEngine()
76 SourcePawnEngine::~SourcePawnEngine()
81 pTemp = m_FreedCalls->next;
90 pNext = m_pFreeFuncs->m_pNext;
97 void *SourcePawnEngine::ExecAlloc(size_t size)
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)
112 void SourcePawnEngine::ExecFree(void *address)
115 VirtualFree(address, 0, MEM_RELEASE);
116 #elif defined __GNUC__
121 void *SourcePawnEngine::BaseAlloc(size_t size)
126 void SourcePawnEngine::BaseFree(void *memory)
131 IPluginContext *SourcePawnEngine::CreateBaseContext(sp_context_t *ctx)
133 return new BaseContext(ctx);
136 void SourcePawnEngine::FreeBaseContext(IPluginContext *ctx)
141 sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, int *err)
145 sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
147 memset(plugin, 0, sizeof(sp_plugin_t));
151 while (sectnum < hdr->sections)
153 nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
155 if (!(plugin->pcode) && !strcmp(nameptr, ".code"))
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;
162 else if (!(plugin->data) && !strcmp(nameptr, ".data"))
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;
169 else if (!(plugin->info.publics) && !strcmp(nameptr, ".publics"))
171 plugin->info.publics_num = secptr->size / sizeof(sp_file_publics_t);
172 plugin->info.publics = (sp_file_publics_t *)(base + secptr->dataoffs);
174 else if (!(plugin->info.pubvars) && !strcmp(nameptr, ".pubvars"))
176 plugin->info.pubvars_num = secptr->size / sizeof(sp_file_pubvars_t);
177 plugin->info.pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
179 else if (!(plugin->info.natives) && !strcmp(nameptr, ".natives"))
181 plugin->info.natives_num = secptr->size / sizeof(sp_file_natives_t);
182 plugin->info.natives = (sp_file_natives_t *)(base + secptr->dataoffs);
184 else if (!(plugin->info.stringbase) && !strcmp(nameptr, ".names"))
186 plugin->info.stringbase = (const char *)(base + secptr->dataoffs);
188 else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files"))
190 plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
192 else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines"))
194 plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
196 else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols"))
198 plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
200 else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info"))
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;
207 else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings"))
209 plugin->debug.stringbase = (const char *)(base + secptr->dataoffs);
216 if (!(plugin->pcode) || !(plugin->data) || !(plugin->info.stringbase))
221 if ((plugin->flags & SP_FLAG_DEBUG) && (!(plugin->debug.files) || !(plugin->debug.lines) || !(plugin->debug.symbols)))
228 *err = SP_ERROR_NONE;
236 *err = SP_ERROR_FILE_FORMAT;
242 sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
252 error = SP_ERROR_NOT_FOUND;
256 /* Rewind for safety */
258 fread(&hdr, sizeof(sp_file_hdr_t), 1, fp);
260 if (hdr.magic != SPFILE_MAGIC)
262 error = SP_ERROR_FILE_FORMAT;
266 switch (hdr.compression)
268 case SPFILE_COMPRESSION_GZ:
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;
275 char *tempbuf = (char *)malloc(compsize);
276 void *uncompdata = malloc(uncompsize);
277 void *sectheader = malloc(sectsize);
279 fread(sectheader, sectsize, 1, fp);
280 fread(tempbuf, compsize, 1, fp);
282 z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize);
284 if (z_result != Z_OK)
288 error = SP_ERROR_DECOMPRESSOR;
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);
296 memcpy(base + hdr.dataoffs, uncompdata, uncompsize);
300 case SPFILE_COMPRESSION_NONE:
302 base = (uint8_t *)malloc(hdr.imagesize);
304 fread(base, hdr.imagesize, 1, fp);
309 error = SP_ERROR_DECOMPRESSOR;
314 plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
315 if (!_ReadPlugin(&hdr, base, plugin, err))
322 plugin->allocflags = 0;
334 sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
339 memcpy(&hdr, base, sizeof(sp_file_hdr_t));
343 plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
347 if (!_ReadPlugin(&hdr, (uint8_t *)base, plugin, err))
358 plugin->allocflags |= SP_FA_SELF_EXTERNAL;
360 plugin->allocflags |= SP_FA_BASE_EXTERNAL;
365 int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
367 if (!(plugin->allocflags & SP_FA_BASE_EXTERNAL))
372 if (!(plugin->allocflags & SP_FA_SELF_EXTERNAL))
377 return SP_ERROR_NONE;
381 void SourcePawnEngine::ReleaseFunctionToPool(CFunction *func)
388 func->m_pNext = m_pFreeFuncs;
392 CFunction *SourcePawnEngine::GetFunctionFromPool(funcid_t f, IPluginContext *plugin)
396 return new CFunction(f, plugin);
398 CFunction *pFunc = m_pFreeFuncs;
399 m_pFreeFuncs = m_pFreeFuncs->m_pNext;
400 pFunc->Set(f, plugin);
406 IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
408 IDebugListener *old = m_pDebugHook;
410 m_pDebugHook = pListener;
415 unsigned int SourcePawnEngine::GetContextCallCount()
422 return m_CallStack->chain;
425 TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
431 pCall = new TracedCall;
433 /* Unlink the head node from the free list */
434 pCall = m_FreedCalls;
435 m_FreedCalls = m_FreedCalls->next;
438 /* Link as the head node into the call stack */
439 pCall->next = m_CallStack;
443 pCall->chain = ++m_CurChain;
445 pCall->chain = m_CurChain;
453 void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
455 /* Check if this is the top of the call stack */
456 if (pCall == m_CallStack)
458 m_CallStack = m_CallStack->next;
461 /* Add this to our linked list of freed calls */
464 m_FreedCalls = pCall;
465 m_FreedCalls->next = NULL;
467 pCall->next = m_FreedCalls;
468 m_FreedCalls = pCall;
472 void SourcePawnEngine::PushTracer(sp_context_t *ctx)
474 TracedCall *pCall = MakeTracedCall(true);
476 pCall->cip = INVALID_CIP;
478 pCall->frm = INVALID_CIP;
481 void SourcePawnEngine::RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip)
483 assert(m_CallStack != NULL);
484 assert(m_CallStack->ctx == ctx);
485 assert(m_CallStack->chain == m_CurChain);
487 if (m_CallStack->cip == INVALID_CIP)
489 /* We aren't logging anything yet, so begin the trace */
490 m_CallStack->cip = codeip;
491 m_CallStack->frm = frame;
493 if (m_CallStack->frm > frame)
495 /* The last frame has moved down the stack,
496 * so we have to push a new call onto our list.
498 TracedCall *pCall = MakeTracedCall(false);
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.
505 FreeTracedCall(m_CallStack);
507 /* no matter where we are, update the cip */
508 m_CallStack->cip = codeip;
512 void SourcePawnEngine::PopTracer(int error, const char *msg)
514 assert(m_CallStack != NULL);
516 if (error != SP_ERROR_NONE && m_pDebugHook)
518 uint32_t native = INVALID_CIP;
520 if (m_CallStack->ctx->n_err)
522 native = m_CallStack->ctx->n_idx;
525 CContextTrace trace(m_CallStack, error, msg, native);
526 m_pDebugHook->OnContextExecuteError(m_CallStack->ctx->context, &trace);
529 /* Now pop the error chain */
530 while (m_CallStack && m_CallStack->chain == m_CurChain)
532 FreeTracedCall(m_CallStack);
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)
543 bool CContextTrace::DebugInfoAvailable()
545 return ((m_pStart->ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
548 const char *CContextTrace::GetCustomErrorString()
553 int CContextTrace::GetErrorCode()
558 const char *CContextTrace::GetErrorString()
560 if (m_Error > ERROR_MESSAGE_MAX ||
563 return "Invalid error code";
565 return g_ErrorMsgTable[m_Error];
569 void CContextTrace::ResetTrace()
571 m_pIterator = m_pStart;
574 bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
576 if (!m_pIterator || (m_pIterator->chain != m_pStart->chain))
581 if (m_pIterator->cip == INVALID_CIP)
586 IPluginContext *pContext = m_pIterator->ctx->context;
587 IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
596 m_pIterator = m_pIterator->next;
600 if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
602 trace->filename = NULL;
605 if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
607 trace->function = NULL;
610 if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
615 m_pIterator = m_pIterator->next;
620 const char *CContextTrace::GetLastNative(uint32_t *index)
622 if (m_Native == INVALID_CIP)
628 if (m_pIterator->ctx->context->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE)