1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/sourcepawn/jit/sp_vm_engine.cpp Fri Jul 11 08:18:43 2008 +0000
1.3 @@ -0,0 +1,456 @@
1.4 +/**
1.5 + * vim: set ts=4 :
1.6 + * =============================================================================
1.7 + * SourcePawn
1.8 + * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 + * =============================================================================
1.10 + *
1.11 + * This program is free software; you can redistribute it and/or modify it under
1.12 + * the terms of the GNU General Public License, version 3.0, as published by the
1.13 + * Free Software Foundation.
1.14 + *
1.15 + * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 + * details.
1.19 + *
1.20 + * You should have received a copy of the GNU General Public License along with
1.21 + * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 + *
1.23 + * As a special exception, AlliedModders LLC gives you permission to link the
1.24 + * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 + * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 + * by the Valve Corporation. You must obey the GNU General Public License in
1.27 + * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 + * this exception to all derivative works. AlliedModders LLC defines further
1.29 + * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 + * or <http://www.sourcemod.net/license.php>.
1.31 + *
1.32 + * Version: $Id$
1.33 + */
1.34 +
1.35 +#include "sp_vm_types.h"
1.36 +#include <sh_memory.h>
1.37 +/* HACK to avoid including sourcehook.h for just the SH_ASSERT definition */
1.38 +#if !defined SH_ASSERT
1.39 + #define SH_ASSERT(x, info)
1.40 + #include <sh_pagealloc.h>
1.41 + #undef SH_ASSERT
1.42 +#else
1.43 + #include <sh_pagealloc.h>
1.44 +#endif
1.45 +
1.46 +#include <malloc.h>
1.47 +#include <string.h>
1.48 +#include <assert.h>
1.49 +#include "sp_file_headers.h"
1.50 +#include "sp_vm_engine.h"
1.51 +#include "zlib/zlib.h"
1.52 +#include "sp_vm_basecontext.h"
1.53 +
1.54 +SourcePawnEngine g_engine1;
1.55 +
1.56 +#if defined WIN32
1.57 + #define WIN32_LEAN_AND_MEAN
1.58 + #include <windows.h>
1.59 +#elif defined __GNUC__
1.60 + #include <sys/mman.h>
1.61 +#endif
1.62 +
1.63 +#define INVALID_CIP 0xFFFFFFFF
1.64 +
1.65 +using namespace SourcePawn;
1.66 +
1.67 +SourceHook::CPageAlloc g_ExeMemory(16);
1.68 +
1.69 +#define ERROR_MESSAGE_MAX 25
1.70 +static const char *g_ErrorMsgTable[] =
1.71 +{
1.72 + NULL,
1.73 + "Unrecognizable file format",
1.74 + "Decompressor was not found",
1.75 + "Not enough space on the heap",
1.76 + "Invalid parameter or parameter type",
1.77 + "Invalid plugin address",
1.78 + "Object or index not found",
1.79 + "Invalid index or index not found",
1.80 + "Not enough space on the stack",
1.81 + "Debug section not found or debug not enabled",
1.82 + "Invalid instruction",
1.83 + "Invalid memory access",
1.84 + "Stack went below stack boundary",
1.85 + "Heap went below heap boundary",
1.86 + "Divide by zero",
1.87 + "Array index is out of bounds",
1.88 + "Instruction contained invalid parameter",
1.89 + "Stack memory leaked by native",
1.90 + "Heap memory leaked by native",
1.91 + "Dynamic array is too big",
1.92 + "Tracker stack is out of bounds",
1.93 + "Native is not bound",
1.94 + "Maximum number of parameters reached",
1.95 + "Native detected error",
1.96 + "Plugin not runnable",
1.97 + "Call was aborted",
1.98 +};
1.99 +
1.100 +const char *SourcePawnEngine::GetErrorString(int error)
1.101 +{
1.102 + if (error < 1 || error > ERROR_MESSAGE_MAX)
1.103 + {
1.104 + return NULL;
1.105 + }
1.106 +
1.107 + return g_ErrorMsgTable[error];
1.108 +}
1.109 +
1.110 +SourcePawnEngine::SourcePawnEngine()
1.111 +{
1.112 + m_pDebugHook = NULL;
1.113 + m_CallStack = NULL;
1.114 + m_FreedCalls = NULL;
1.115 + m_CurChain = 0;
1.116 +#if 0
1.117 + m_pFreeFuncs = NULL;
1.118 +#endif
1.119 +}
1.120 +
1.121 +SourcePawnEngine::~SourcePawnEngine()
1.122 +{
1.123 + TracedCall *pTemp;
1.124 + while (m_FreedCalls)
1.125 + {
1.126 + pTemp = m_FreedCalls->next;
1.127 + delete m_FreedCalls;
1.128 + m_FreedCalls = pTemp;
1.129 + }
1.130 +
1.131 +#if 0
1.132 + CFunction *pNext;
1.133 + while (m_pFreeFuncs)
1.134 + {
1.135 + pNext = m_pFreeFuncs->m_pNext;
1.136 + delete m_pFreeFuncs;
1.137 + m_pFreeFuncs = pNext;
1.138 + }
1.139 +#endif
1.140 +}
1.141 +
1.142 +void *SourcePawnEngine::ExecAlloc(size_t size)
1.143 +{
1.144 +#if defined WIN32
1.145 + return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
1.146 +#elif defined __GNUC__
1.147 + void *base = memalign(sysconf(_SC_PAGESIZE), size);
1.148 + if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
1.149 + {
1.150 + free(base);
1.151 + return NULL;
1.152 + }
1.153 + return base;
1.154 +#endif
1.155 +}
1.156 +
1.157 +void *SourcePawnEngine::AllocatePageMemory(size_t size)
1.158 +{
1.159 + return g_ExeMemory.Alloc(size);
1.160 +}
1.161 +
1.162 +void SourcePawnEngine::SetReadExecute(void *ptr)
1.163 +{
1.164 + g_ExeMemory.SetRE(ptr);
1.165 +}
1.166 +
1.167 +void SourcePawnEngine::SetReadWrite(void *ptr)
1.168 +{
1.169 + g_ExeMemory.SetRW(ptr);
1.170 +}
1.171 +
1.172 +void SourcePawnEngine::FreePageMemory(void *ptr)
1.173 +{
1.174 + g_ExeMemory.Free(ptr);
1.175 +}
1.176 +
1.177 +void SourcePawnEngine::ExecFree(void *address)
1.178 +{
1.179 +#if defined WIN32
1.180 + VirtualFree(address, 0, MEM_RELEASE);
1.181 +#elif defined __GNUC__
1.182 + free(address);
1.183 +#endif
1.184 +}
1.185 +
1.186 +void *SourcePawnEngine::BaseAlloc(size_t size)
1.187 +{
1.188 + return malloc(size);
1.189 +}
1.190 +
1.191 +void SourcePawnEngine::BaseFree(void *memory)
1.192 +{
1.193 + free(memory);
1.194 +}
1.195 +
1.196 +sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
1.197 +{
1.198 + if (err != NULL)
1.199 + {
1.200 + *err = SP_ERROR_ABORTED;
1.201 + }
1.202 +
1.203 + return NULL;
1.204 +}
1.205 +
1.206 +sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
1.207 +{
1.208 + if (err != NULL)
1.209 + {
1.210 + *err = SP_ERROR_ABORTED;
1.211 + }
1.212 +
1.213 + return NULL;
1.214 +}
1.215 +
1.216 +int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
1.217 +{
1.218 + return SP_ERROR_ABORTED;
1.219 +}
1.220 +
1.221 +IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
1.222 +{
1.223 + IDebugListener *old = m_pDebugHook;
1.224 +
1.225 + m_pDebugHook = pListener;
1.226 +
1.227 + return old;
1.228 +}
1.229 +
1.230 +unsigned int SourcePawnEngine::GetContextCallCount()
1.231 +{
1.232 + if (!m_CallStack)
1.233 + {
1.234 + return 0;
1.235 + }
1.236 +
1.237 + return m_CallStack->chain;
1.238 +}
1.239 +
1.240 +TracedCall *SourcePawnEngine::MakeTracedCall(bool new_chain)
1.241 +{
1.242 + TracedCall *pCall;
1.243 +
1.244 + if (!m_FreedCalls)
1.245 + {
1.246 + pCall = new TracedCall;
1.247 + } else {
1.248 + /* Unlink the head node from the free list */
1.249 + pCall = m_FreedCalls;
1.250 + m_FreedCalls = m_FreedCalls->next;
1.251 + }
1.252 +
1.253 + /* Link as the head node into the call stack */
1.254 + pCall->next = m_CallStack;
1.255 +
1.256 + if (new_chain)
1.257 + {
1.258 + pCall->chain = ++m_CurChain;
1.259 + } else {
1.260 + pCall->chain = m_CurChain;
1.261 + }
1.262 +
1.263 + m_CallStack = pCall;
1.264 +
1.265 + return pCall;
1.266 +}
1.267 +
1.268 +void SourcePawnEngine::FreeTracedCall(TracedCall *pCall)
1.269 +{
1.270 + /* Check if this is the top of the call stack */
1.271 + if (pCall == m_CallStack)
1.272 + {
1.273 + m_CallStack = m_CallStack->next;
1.274 + }
1.275 +
1.276 + /* Add this to our linked list of freed calls */
1.277 + if (!m_FreedCalls)
1.278 + {
1.279 + m_FreedCalls = pCall;
1.280 + m_FreedCalls->next = NULL;
1.281 + } else {
1.282 + pCall->next = m_FreedCalls;
1.283 + m_FreedCalls = pCall;
1.284 + }
1.285 +}
1.286 +
1.287 +void SourcePawnEngine::PushTracer(BaseContext *ctx)
1.288 +{
1.289 + TracedCall *pCall = MakeTracedCall(true);
1.290 +
1.291 + pCall->cip = INVALID_CIP;
1.292 + pCall->ctx = ctx;
1.293 + pCall->frm = INVALID_CIP;
1.294 +}
1.295 +
1.296 +void SourcePawnEngine::RunTracer(BaseContext *ctx, uint32_t frame, uint32_t codeip)
1.297 +{
1.298 + assert(m_CallStack != NULL);
1.299 + assert(m_CallStack->ctx == ctx);
1.300 + assert(m_CallStack->chain == m_CurChain);
1.301 +
1.302 + if (m_CallStack->cip == INVALID_CIP)
1.303 + {
1.304 + /* We aren't logging anything yet, so begin the trace */
1.305 + m_CallStack->cip = codeip;
1.306 + m_CallStack->frm = frame;
1.307 + } else {
1.308 + if (m_CallStack->frm > frame)
1.309 + {
1.310 + /* The last frame has moved down the stack,
1.311 + * so we have to push a new call onto our list.
1.312 + */
1.313 + TracedCall *pCall = MakeTracedCall(false);
1.314 + pCall->ctx = ctx;
1.315 + pCall->frm = frame;
1.316 + } else if (m_CallStack->frm < frame) {
1.317 + /* The last frame has moved up the stack,
1.318 + * so we have to pop the call from our list.
1.319 + */
1.320 + FreeTracedCall(m_CallStack);
1.321 + }
1.322 + /* no matter where we are, update the cip */
1.323 + m_CallStack->cip = codeip;
1.324 + }
1.325 +}
1.326 +
1.327 +void SourcePawnEngine::PopTracer(int error, const char *msg)
1.328 +{
1.329 + assert(m_CallStack != NULL);
1.330 +
1.331 + if (error != SP_ERROR_NONE && m_pDebugHook)
1.332 + {
1.333 + uint32_t native = INVALID_CIP;
1.334 +
1.335 + if (m_CallStack->ctx->GetCtx()->n_err)
1.336 + {
1.337 + native = m_CallStack->ctx->GetCtx()->n_idx;
1.338 + }
1.339 +
1.340 + CContextTrace trace(m_CallStack, error, msg, native);
1.341 + m_pDebugHook->OnContextExecuteError(m_CallStack->ctx, &trace);
1.342 + }
1.343 +
1.344 + /* Now pop the error chain */
1.345 + while (m_CallStack && m_CallStack->chain == m_CurChain)
1.346 + {
1.347 + FreeTracedCall(m_CallStack);
1.348 + }
1.349 +
1.350 + m_CurChain--;
1.351 +}
1.352 +
1.353 +unsigned int SourcePawnEngine::GetEngineAPIVersion()
1.354 +{
1.355 + return SOURCEPAWN_ENGINE_API_VERSION;
1.356 +}
1.357 +
1.358 +CContextTrace::CContextTrace(TracedCall *pStart, int error, const char *msg, uint32_t native) :
1.359 + m_Error(error), m_pMsg(msg), m_pStart(pStart), m_pIterator(pStart), m_Native(native)
1.360 +{
1.361 +}
1.362 +
1.363 +bool CContextTrace::DebugInfoAvailable()
1.364 +{
1.365 + return m_pStart->ctx->IsDebugging();
1.366 +}
1.367 +
1.368 +const char *CContextTrace::GetCustomErrorString()
1.369 +{
1.370 + return m_pMsg;
1.371 +}
1.372 +
1.373 +int CContextTrace::GetErrorCode()
1.374 +{
1.375 + return m_Error;
1.376 +}
1.377 +
1.378 +const char *CContextTrace::GetErrorString()
1.379 +{
1.380 + if (m_Error > ERROR_MESSAGE_MAX ||
1.381 + m_Error < 1)
1.382 + {
1.383 + return "Invalid error code";
1.384 + } else {
1.385 + return g_ErrorMsgTable[m_Error];
1.386 + }
1.387 +}
1.388 +
1.389 +void CContextTrace::ResetTrace()
1.390 +{
1.391 + m_pIterator = m_pStart;
1.392 +}
1.393 +
1.394 +bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
1.395 +{
1.396 + if (!m_pIterator || (m_pIterator->chain != m_pStart->chain))
1.397 + {
1.398 + return false;
1.399 + }
1.400 +
1.401 + if (m_pIterator->cip == INVALID_CIP)
1.402 + {
1.403 + return false;
1.404 + }
1.405 +
1.406 + IPluginContext *pContext = m_pIterator->ctx;
1.407 + IPluginDebugInfo *pInfo = pContext->GetDebugInfo();
1.408 +
1.409 + if (!pInfo)
1.410 + {
1.411 + return false;
1.412 + }
1.413 +
1.414 + if (!trace)
1.415 + {
1.416 + m_pIterator = m_pIterator->next;
1.417 + return true;
1.418 + }
1.419 +
1.420 + if (pInfo->LookupFile(m_pIterator->cip, &(trace->filename)) != SP_ERROR_NONE)
1.421 + {
1.422 + trace->filename = NULL;
1.423 + }
1.424 +
1.425 + if (pInfo->LookupFunction(m_pIterator->cip, &(trace->function)) != SP_ERROR_NONE)
1.426 + {
1.427 + trace->function = NULL;
1.428 + }
1.429 +
1.430 + if (pInfo->LookupLine(m_pIterator->cip, &(trace->line)) != SP_ERROR_NONE)
1.431 + {
1.432 + trace->line = 0;
1.433 + }
1.434 +
1.435 + m_pIterator = m_pIterator->next;
1.436 +
1.437 + return true;
1.438 +}
1.439 +
1.440 +const char *CContextTrace::GetLastNative(uint32_t *index)
1.441 +{
1.442 + if (m_Native == INVALID_CIP)
1.443 + {
1.444 + return NULL;
1.445 + }
1.446 +
1.447 + sp_native_t *native;
1.448 + if (m_pIterator->ctx->GetNativeByIndex(m_Native, &native) != SP_ERROR_NONE)
1.449 + {
1.450 + return NULL;
1.451 + }
1.452 +
1.453 + if (index)
1.454 + {
1.455 + *index = m_Native;
1.456 + }
1.457 +
1.458 + return native->name;
1.459 +}