3 * ================================================================
5 * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved.
6 * ================================================================
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License,
10 * version 3.0, as published by the Free Software Foundation.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to
21 * link the code of this program (as well as its derivative works) to
22 * "Half-Life 2," the "Source Engine," the "SourcePawn JIT," and any
23 * Game MODs that run on software by the Valve Corporation. You must
24 * obey the GNU General Public License in all respects for all other
25 * code used. Additionally, AlliedModders LLC grants this exception
26 * to all derivative works. AlliedModders LLC defines further
27 * exceptions, found in LICENSE.txt (as of this writing, version
28 * JULY-31-2007), or <http://www.sourcemod.net/license.php>.
36 #include "sp_file_headers.h"
37 #include "sp_vm_types.h"
38 #include "sp_vm_engine.h"
39 #include "zlib/zlib.h"
40 #include "sp_vm_basecontext.h"
43 #define WIN32_LEAN_AND_MEAN
45 #elif defined __GNUC__
49 #define INVALID_CIP 0xFFFFFFFF
51 using namespace SourcePawn;
53 #define ERROR_MESSAGE_MAX 25
54 static const char *g_ErrorMsgTable[] =
57 "Unrecognizable file format",
58 "Decompressor was not found",
59 "Not enough space on the heap",
60 "Invalid parameter or parameter type",
61 "Invalid plugin address",
62 "Object or index not found",
63 "Invalid index or index not found",
64 "Not enough space on the stack",
65 "Debug section not found or debug not enabled",
66 "Invalid instruction",
67 "Invalid memory access",
68 "Stack went below stack boundary",
69 "Heap went below heap boundary",
71 "Array index is out of bounds",
72 "Instruction contained invalid parameter",
73 "Stack memory leaked by native",
74 "Heap memory leaked by native",
75 "Dynamic array is too big",
76 "Tracker stack is out of bounds",
77 "Native is not bound",
78 "Maximum number of parameters reached",
79 "Native detected error",
80 "Plugin not runnable",
84 const char *GetSourcePawnErrorMessage(int error)
86 if (error < 1 || error > ERROR_MESSAGE_MAX)
91 return g_ErrorMsgTable[error];
94 SourcePawnEngine::SourcePawnEngine()
105 SourcePawnEngine::~SourcePawnEngine()
110 pTemp = m_FreedCalls->next;
112 m_FreedCalls = pTemp;
119 pNext = m_pFreeFuncs->m_pNext;
121 m_pFreeFuncs = pNext;
126 void *SourcePawnEngine::ExecAlloc(size_t size)
129 return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
130 #elif defined __GNUC__
131 void *base = memalign(sysconf(_SC_PAGESIZE), size);
132 if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
141 void SourcePawnEngine::ExecFree(void *address)
144 VirtualFree(address, 0, MEM_RELEASE);
145 #elif defined __GNUC__
150 void *SourcePawnEngine::BaseAlloc(size_t size)
155 void SourcePawnEngine::BaseFree(void *memory)
160 IPluginContext *SourcePawnEngine::CreateBaseContext(sp_context_t *ctx)
162 return new BaseContext(ctx);
165 void SourcePawnEngine::FreeBaseContext(IPluginContext *ctx)
170 sp_plugin_t *_ReadPlugin(sp_file_hdr_t *hdr, uint8_t *base, sp_plugin_t *plugin, int *err)
174 sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
176 memset(plugin, 0, sizeof(sp_plugin_t));
180 while (sectnum < hdr->sections)
182 nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
184 if (!(plugin->pcode) && !strcmp(nameptr, ".code"))
186 sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
187 plugin->pcode = base + secptr->dataoffs + cod->code;
188 plugin->pcode_size = cod->codesize;
189 plugin->flags = cod->flags;
191 else if (!(plugin->data) && !strcmp(nameptr, ".data"))
193 sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs);
194 plugin->data = base + secptr->dataoffs + dat->data;
195 plugin->data_size = dat->datasize;
196 plugin->memory = dat->memsize;
198 else if (!(plugin->info.publics) && !strcmp(nameptr, ".publics"))
200 plugin->info.publics_num = secptr->size / sizeof(sp_file_publics_t);
201 plugin->info.publics = (sp_file_publics_t *)(base + secptr->dataoffs);
203 else if (!(plugin->info.pubvars) && !strcmp(nameptr, ".pubvars"))
205 plugin->info.pubvars_num = secptr->size / sizeof(sp_file_pubvars_t);
206 plugin->info.pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
208 else if (!(plugin->info.natives) && !strcmp(nameptr, ".natives"))
210 plugin->info.natives_num = secptr->size / sizeof(sp_file_natives_t);
211 plugin->info.natives = (sp_file_natives_t *)(base + secptr->dataoffs);
213 else if (!(plugin->info.stringbase) && !strcmp(nameptr, ".names"))
215 plugin->info.stringbase = (const char *)(base + secptr->dataoffs);
217 else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files"))
219 plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
221 else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines"))
223 plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
225 else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols"))
227 plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
229 else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info"))
231 sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs);
232 plugin->debug.files_num = inf->num_files;
233 plugin->debug.lines_num = inf->num_lines;
234 plugin->debug.syms_num = inf->num_syms;
236 else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings"))
238 plugin->debug.stringbase = (const char *)(base + secptr->dataoffs);
245 if (!(plugin->pcode) || !(plugin->data) || !(plugin->info.stringbase))
250 if ((plugin->flags & SP_FLAG_DEBUG) && (!(plugin->debug.files) || !(plugin->debug.lines) || !(plugin->debug.symbols)))
257 *err = SP_ERROR_NONE;
265 *err = SP_ERROR_FILE_FORMAT;
271 sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
281 error = SP_ERROR_NOT_FOUND;
285 /* Rewind for safety */
287 fread(&hdr, sizeof(sp_file_hdr_t), 1, fp);
289 if (hdr.magic != SPFILE_MAGIC)
291 error = SP_ERROR_FILE_FORMAT;
295 switch (hdr.compression)
297 case SPFILE_COMPRESSION_GZ:
299 uint32_t uncompsize = hdr.imagesize - hdr.dataoffs;
300 uint32_t compsize = hdr.disksize - hdr.dataoffs;
301 uint32_t sectsize = hdr.dataoffs - sizeof(sp_file_hdr_t);
302 uLongf destlen = uncompsize;
304 char *tempbuf = (char *)malloc(compsize);
305 void *uncompdata = malloc(uncompsize);
306 void *sectheader = malloc(sectsize);
308 fread(sectheader, sectsize, 1, fp);
309 fread(tempbuf, compsize, 1, fp);
311 z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize);
313 if (z_result != Z_OK)
317 error = SP_ERROR_DECOMPRESSOR;
321 base = (uint8_t *)malloc(hdr.imagesize);
322 memcpy(base, &hdr, sizeof(sp_file_hdr_t));
323 memcpy(base + sizeof(sp_file_hdr_t), sectheader, sectsize);
325 memcpy(base + hdr.dataoffs, uncompdata, uncompsize);
329 case SPFILE_COMPRESSION_NONE:
331 base = (uint8_t *)malloc(hdr.imagesize);
333 fread(base, hdr.imagesize, 1, fp);
338 error = SP_ERROR_DECOMPRESSOR;
343 plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
344 if (!_ReadPlugin(&hdr, base, plugin, err))
351 plugin->allocflags = 0;
363 sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
368 memcpy(&hdr, base, sizeof(sp_file_hdr_t));
372 plugin = (sp_plugin_t *)malloc(sizeof(sp_plugin_t));
376 if (!_ReadPlugin(&hdr, (uint8_t *)base, plugin, err))
387 plugin->allocflags |= SP_FA_SELF_EXTERNAL;
389 plugin->allocflags |= SP_FA_BASE_EXTERNAL;
394 int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
396 if (!(plugin->allocflags & SP_FA_BASE_EXTERNAL))
401 if (!(plugin->allocflags & SP_FA_SELF_EXTERNAL))
406 return SP_ERROR_NONE;
410 void SourcePawnEngine::ReleaseFunctionToPool(CFunction *func)
417 func->m_pNext = m_pFreeFuncs;
421 CFunction *SourcePawnEngine::GetFunctionFromPool(funcid_t f, IPluginContext *plugin)
425 return new CFunction(f, plugin);
427 CFunction *pFunc = m_pFreeFuncs;
428 m_pFreeFuncs = m_pFreeFuncs->m_pNext;
429 pFunc->Set(f, plugin);
435 IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
437 IDebugListener *old = m_pDebugHook;
439 m_pDebugHook = pListener;
444 unsigned int SourcePawnEngine::GetContextCallCount()
451 return m_CallStack->chain;
454 TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
460 pCall = new TracedCall;
462 /* Unlink the head node from the free list */
463 pCall = m_FreedCalls;
464 m_FreedCalls = m_FreedCalls->next;
467 /* Link as the head node into the call stack */
468 pCall->next = m_CallStack;
472 pCall->chain = ++m_CurChain;
474 pCall->chain = m_CurChain;
482 void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
484 /* Check if this is the top of the call stack */
485 if (pCall == m_CallStack)
487 m_CallStack = m_CallStack->next;
490 /* Add this to our linked list of freed calls */
493 m_FreedCalls = pCall;
494 m_FreedCalls->next = NULL;
496 pCall->next = m_FreedCalls;
497 m_FreedCalls = pCall;
501 void SourcePawnEngine::PushTracer(sp_context_t *ctx)
503 TracedCall *pCall = MakeTracedCall(true);
505 pCall->cip = INVALID_CIP;
507 pCall->frm = INVALID_CIP;
510 void SourcePawnEngine::RunTracer(sp_context_t *ctx, uint32_t frame, uint32_t codeip)
512 assert(m_CallStack != NULL);
513 assert(m_CallStack->ctx == ctx);
514 assert(m_CallStack->chain == m_CurChain);
516 if (m_CallStack->cip == INVALID_CIP)
518 /* We aren't logging anything yet, so begin the trace */
519 m_CallStack->cip = codeip;
520 m_CallStack->frm = frame;
522 if (m_CallStack->frm > frame)
524 /* The last frame has moved down the stack,
525 * so we have to push a new call onto our list.
527 TracedCall *pCall = MakeTracedCall(false);
530 } else if (m_CallStack->frm < frame) {
531 /* The last frame has moved up the stack,
532 * so we have to pop the call from our list.
534 FreeTracedCall(m_CallStack);
536 /* no matter where we are, update the cip */
537 m_CallStack->cip = codeip;
541 void SourcePawnEngine::PopTracer(int error, const char *msg)
543 assert(m_CallStack != NULL);
545 if (error != SP_ERROR_NONE && m_pDebugHook)
547 uint32_t native = INVALID_CIP;
549 if (m_CallStack->ctx->n_err)
551 native = m_CallStack->ctx->n_idx;
554 CContextTrace trace(m_CallStack, error, msg, native);
555 m_pDebugHook->OnContextExecuteError(m_CallStack->ctx->context, &trace);
558 /* Now pop the error chain */
559 while (m_CallStack && m_CallStack->chain == m_CurChain)
561 FreeTracedCall(m_CallStack);
567 unsigned int SourcePawnEngine::GetEngineAPIVersion()
569 return SOURCEPAWN_ENGINE_API_VERSION;
572 CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native) :
573 m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart), m_Native(native)
577 bool CContextTrace::DebugInfoAvailable()
579 return ((m_pStart->ctx->flags & SP_FLAG_DEBUG) == SP_FLAG_DEBUG);
582 const char *CContextTrace::GetCustomErrorString()
587 int CContextTrace::GetErrorCode()
592 const char *CContextTrace::GetErrorString()
594 if (m_Error > ERROR_MESSAGE_MAX ||
597 return "Invalid error code";
599 return g_ErrorMsgTable[m_Error];
603 void CContextTrace::ResetTrace()
605 m_pIterator = m_pStart;
608 bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
610 if (!m_pIterator || (m_pIterator->chain != m_pStart->chain))
615 if (m_pIterator->cip == INVALID_CIP)
620 IPluginContext *pContext = m_pIterator->ctx->context;
621 IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
630 m_pIterator = m_pIterator->next;
634 if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
636 trace->filename = NULL;
639 if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
641 trace->function = NULL;
644 if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
649 m_pIterator = m_pIterator->next;
654 const char *CContextTrace::GetLastNative(uint32_t *index)
656 if (m_Native == INVALID_CIP)
662 if (m_pIterator->ctx->context->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE)