3 * =============================================================================
5 * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
6 * =============================================================================
8 * This program is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, version 3.0, as published by the
10 * Free Software Foundation.
12 * This program is distributed in the hope that it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 * You should have received a copy of the GNU General Public License along with
18 * this program. If not, see <http://www.gnu.org/licenses/>.
20 * As a special exception, AlliedModders LLC gives you permission to link the
21 * code of this program (as well as its derivative works) to "Half-Life 2," the
22 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
23 * by the Valve Corporation. You must obey the GNU General Public License in
24 * all respects for all other code used. Additionally, AlliedModders LLC grants
25 * this exception to all derivative works. AlliedModders LLC defines further
26 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
27 * or <http://www.sourcemod.net/license.php>.
32 #include "sp_vm_types.h"
33 #include <sh_memory.h>
34 /* HACK to avoid including sourcehook.h for just the SH_ASSERT definition */
35 #if !defined SH_ASSERT
36 #define SH_ASSERT(x, info)
37 #include <sh_pagealloc.h>
40 #include <sh_pagealloc.h>
46 #include "sp_file_headers.h"
47 #include "sp_vm_engine.h"
48 #include "zlib/zlib.h"
49 #include "sp_vm_basecontext.h"
51 SourcePawnEngine g_engine1;
54 #define WIN32_LEAN_AND_MEAN
56 #elif defined __GNUC__
60 #define INVALID_CIP 0xFFFFFFFF
62 using namespace SourcePawn;
64 SourceHook::CPageAlloc g_ExeMemory(16);
66 #define ERROR_MESSAGE_MAX 25
67 static const char *g_ErrorMsgTable[] =
70 "Unrecognizable file format",
71 "Decompressor was not found",
72 "Not enough space on the heap",
73 "Invalid parameter or parameter type",
74 "Invalid plugin address",
75 "Object or index not found",
76 "Invalid index or index not found",
77 "Not enough space on the stack",
78 "Debug section not found or debug not enabled",
79 "Invalid instruction",
80 "Invalid memory access",
81 "Stack went below stack boundary",
82 "Heap went below heap boundary",
84 "Array index is out of bounds",
85 "Instruction contained invalid parameter",
86 "Stack memory leaked by native",
87 "Heap memory leaked by native",
88 "Dynamic array is too big",
89 "Tracker stack is out of bounds",
90 "Native is not bound",
91 "Maximum number of parameters reached",
92 "Native detected error",
93 "Plugin not runnable",
97 const char *SourcePawnEngine::GetErrorString(int error)
99 if (error < 1 || error > ERROR_MESSAGE_MAX)
104 return g_ErrorMsgTable[error];
107 SourcePawnEngine::SourcePawnEngine()
118 SourcePawnEngine::~SourcePawnEngine()
123 pTemp = m_FreedCalls->next;
125 m_FreedCalls = pTemp;
132 pNext = m_pFreeFuncs->m_pNext;
134 m_pFreeFuncs = pNext;
139 void *SourcePawnEngine::ExecAlloc(size_t size)
142 return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
143 #elif defined __GNUC__
144 void *base = memalign(sysconf(_SC_PAGESIZE), size);
145 if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
154 void *SourcePawnEngine::AllocatePageMemory(size_t size)
156 return g_ExeMemory.Alloc(size);
159 void SourcePawnEngine::SetReadExecute(void *ptr)
161 g_ExeMemory.SetRE(ptr);
164 void SourcePawnEngine::SetReadWrite(void *ptr)
166 g_ExeMemory.SetRW(ptr);
169 void SourcePawnEngine::FreePageMemory(void *ptr)
171 g_ExeMemory.Free(ptr);
174 void SourcePawnEngine::ExecFree(void *address)
177 VirtualFree(address, 0, MEM_RELEASE);
178 #elif defined __GNUC__
183 void *SourcePawnEngine::BaseAlloc(size_t size)
188 void SourcePawnEngine::BaseFree(void *memory)
193 sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
197 *err = SP_ERROR_ABORTED;
203 sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
207 *err = SP_ERROR_ABORTED;
213 int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
215 return SP_ERROR_ABORTED;
218 IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
220 IDebugListener *old = m_pDebugHook;
222 m_pDebugHook = pListener;
227 unsigned int SourcePawnEngine::GetContextCallCount()
234 return m_CallStack->chain;
237 TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
243 pCall = new TracedCall;
245 /* Unlink the head node from the free list */
246 pCall = m_FreedCalls;
247 m_FreedCalls = m_FreedCalls->next;
250 /* Link as the head node into the call stack */
251 pCall->next = m_CallStack;
255 pCall->chain = ++m_CurChain;
257 pCall->chain = m_CurChain;
265 void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
267 /* Check if this is the top of the call stack */
268 if (pCall == m_CallStack)
270 m_CallStack = m_CallStack->next;
273 /* Add this to our linked list of freed calls */
276 m_FreedCalls = pCall;
277 m_FreedCalls->next = NULL;
279 pCall->next = m_FreedCalls;
280 m_FreedCalls = pCall;
284 void SourcePawnEngine::PushTracer(BaseContext *ctx)
286 TracedCall *pCall = MakeTracedCall(true);
288 pCall->cip = INVALID_CIP;
290 pCall->frm = INVALID_CIP;
293 void SourcePawnEngine::RunTracer(BaseContext *ctx, uint32_t frame, uint32_t codeip)
295 assert(m_CallStack != NULL);
296 assert(m_CallStack->ctx == ctx);
297 assert(m_CallStack->chain == m_CurChain);
299 if (m_CallStack->cip == INVALID_CIP)
301 /* We aren't logging anything yet, so begin the trace */
302 m_CallStack->cip = codeip;
303 m_CallStack->frm = frame;
305 if (m_CallStack->frm > frame)
307 /* The last frame has moved down the stack,
308 * so we have to push a new call onto our list.
310 TracedCall *pCall = MakeTracedCall(false);
313 } else if (m_CallStack->frm < frame) {
314 /* The last frame has moved up the stack,
315 * so we have to pop the call from our list.
317 FreeTracedCall(m_CallStack);
319 /* no matter where we are, update the cip */
320 m_CallStack->cip = codeip;
324 void SourcePawnEngine::PopTracer(int error, const char *msg)
326 assert(m_CallStack != NULL);
328 if (error != SP_ERROR_NONE && m_pDebugHook)
330 uint32_t native = INVALID_CIP;
332 if (m_CallStack->ctx->GetCtx()->n_err)
334 native = m_CallStack->ctx->GetCtx()->n_idx;
337 CContextTrace trace(m_CallStack, error, msg, native);
338 m_pDebugHook->OnContextExecuteError(m_CallStack->ctx, &trace);
341 /* Now pop the error chain */
342 while (m_CallStack && m_CallStack->chain == m_CurChain)
344 FreeTracedCall(m_CallStack);
350 unsigned int SourcePawnEngine::GetEngineAPIVersion()
352 return SOURCEPAWN_ENGINE_API_VERSION;
355 CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native) :
356 m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart), m_Native(native)
360 bool CContextTrace::DebugInfoAvailable()
362 return m_pStart->ctx->IsDebugging();
365 const char *CContextTrace::GetCustomErrorString()
370 int CContextTrace::GetErrorCode()
375 const char *CContextTrace::GetErrorString()
377 if (m_Error > ERROR_MESSAGE_MAX ||
380 return "Invalid error code";
382 return g_ErrorMsgTable[m_Error];
386 void CContextTrace::ResetTrace()
388 m_pIterator = m_pStart;
391 bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
393 if (!m_pIterator || (m_pIterator->chain != m_pStart->chain))
398 if (m_pIterator->cip == INVALID_CIP)
403 IPluginContext *pContext = m_pIterator->ctx;
404 IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
413 m_pIterator = m_pIterator->next;
417 if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
419 trace->filename = NULL;
422 if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
424 trace->function = NULL;
427 if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
432 m_pIterator = m_pIterator->next;
437 const char *CContextTrace::GetLastNative(uint32_t *index)
439 if (m_Native == INVALID_CIP)
445 if (m_pIterator->ctx->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE)