dvander@2193: /**
dvander@2193: * vim: set ts=4 :
dvander@2193: * =============================================================================
dvander@2193: * SourcePawn
dvander@2193: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
dvander@2193: * =============================================================================
dvander@2193: *
dvander@2193: * This program is free software; you can redistribute it and/or modify it under
dvander@2193: * the terms of the GNU General Public License, version 3.0, as published by the
dvander@2193: * Free Software Foundation.
dvander@2193: *
dvander@2193: * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@2193: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@2193: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
dvander@2193: * details.
dvander@2193: *
dvander@2193: * You should have received a copy of the GNU General Public License along with
dvander@2193: * this program. If not, see .
dvander@2193: *
dvander@2193: * As a special exception, AlliedModders LLC gives you permission to link the
dvander@2193: * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@2193: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@2193: * by the Valve Corporation. You must obey the GNU General Public License in
dvander@2193: * all respects for all other code used. Additionally, AlliedModders LLC grants
dvander@2193: * this exception to all derivative works. AlliedModders LLC defines further
dvander@2193: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@2193: * or .
dvander@2193: *
dvander@2193: * Version: $Id$
dvander@2193: */
dvander@2193:
dvander@2471: #include
dvander@2193: #include
dvander@2193: #include
dvander@2228: #include "sp_vm_types.h"
dvander@2226: #include
dvander@2193: #include "sp_file_headers.h"
dvander@2193: #include "sp_vm_engine.h"
dvander@2193: #include "zlib/zlib.h"
dvander@2193: #include "sp_vm_basecontext.h"
dvander@2226: #include "jit_x86.h"
dvander@2226: #if defined __GNUC__
dvander@2226: #include
dvander@2226: #endif
dvander@2193:
dvander@2193: SourcePawnEngine g_engine1;
dvander@2193:
dvander@2193: #if defined WIN32
dvander@2193: #define WIN32_LEAN_AND_MEAN
dvander@2193: #include
dvander@2193: #elif defined __GNUC__
dvander@2193: #include
dvander@2193: #endif
dvander@2193:
dvander@2473: #if defined __linux__
dvander@2472: #include
dvander@2472: #endif
dvander@2472:
dvander@2193: using namespace SourcePawn;
dvander@2193:
danderson@3977: #define ERROR_MESSAGE_MAX 29
dvander@2193: static const char *g_ErrorMsgTable[] =
dvander@2193: {
dvander@2193: NULL,
dvander@2193: "Unrecognizable file format",
dvander@2193: "Decompressor was not found",
dvander@2193: "Not enough space on the heap",
dvander@2193: "Invalid parameter or parameter type",
dvander@2193: "Invalid plugin address",
dvander@2193: "Object or index not found",
dvander@2193: "Invalid index or index not found",
dvander@2193: "Not enough space on the stack",
dvander@2193: "Debug section not found or debug not enabled",
dvander@2193: "Invalid instruction",
dvander@2193: "Invalid memory access",
dvander@2193: "Stack went below stack boundary",
dvander@2193: "Heap went below heap boundary",
dvander@2193: "Divide by zero",
dvander@2193: "Array index is out of bounds",
dvander@2193: "Instruction contained invalid parameter",
dvander@2193: "Stack memory leaked by native",
dvander@2193: "Heap memory leaked by native",
dvander@2193: "Dynamic array is too big",
dvander@2193: "Tracker stack is out of bounds",
dvander@2193: "Native is not bound",
dvander@2193: "Maximum number of parameters reached",
dvander@2193: "Native detected error",
dvander@2193: "Plugin not runnable",
dvander@2193: "Call was aborted",
dvander@2226: "Plugin format is too old",
dvander@2226: "Plugin format is too new",
danderson@3977: "Out of memory",
danderson@3977: "Integer overflow"
dvander@2193: };
dvander@2193:
dvander@2193: const char *SourcePawnEngine::GetErrorString(int error)
dvander@2193: {
dvander@2193: if (error < 1 || error > ERROR_MESSAGE_MAX)
dvander@2193: {
dvander@2193: return NULL;
dvander@2193: }
dvander@2193:
dvander@2193: return g_ErrorMsgTable[error];
dvander@2193: }
dvander@2193:
dvander@2193: SourcePawnEngine::SourcePawnEngine()
dvander@2193: {
dvander@2193: m_pDebugHook = NULL;
dvander@2193: }
dvander@2193:
dvander@2193: SourcePawnEngine::~SourcePawnEngine()
dvander@2193: {
dvander@2193: }
dvander@2193:
dvander@2193: void *SourcePawnEngine::ExecAlloc(size_t size)
dvander@2193: {
dvander@2193: #if defined WIN32
dvander@2193: return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
dvander@2193: #elif defined __GNUC__
dvander@2471: # if defined __APPLE__
dvander@2471: void *base = valloc(size);
dvander@2471: # else
dvander@2193: void *base = memalign(sysconf(_SC_PAGESIZE), size);
dvander@2471: # endif
dvander@2193: if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
dvander@2193: {
dvander@2193: free(base);
dvander@2193: return NULL;
dvander@2193: }
dvander@2193: return base;
dvander@2193: #endif
dvander@2193: }
dvander@2193:
dvander@2193: void *SourcePawnEngine::AllocatePageMemory(size_t size)
dvander@2193: {
dvander@2226: return g_Jit.AllocCode(size);
dvander@2193: }
dvander@2193:
dvander@2193: void SourcePawnEngine::SetReadExecute(void *ptr)
dvander@2193: {
dvander@2226: /* already re */
dvander@2193: }
dvander@2193:
dvander@2193: void SourcePawnEngine::SetReadWrite(void *ptr)
dvander@2193: {
dvander@2226: /* already rw */
dvander@2193: }
dvander@2193:
dvander@2193: void SourcePawnEngine::FreePageMemory(void *ptr)
dvander@2193: {
dvander@2226: g_Jit.FreeCode(ptr);
dvander@2193: }
dvander@2193:
dvander@2193: void SourcePawnEngine::ExecFree(void *address)
dvander@2193: {
dvander@2193: #if defined WIN32
dvander@2193: VirtualFree(address, 0, MEM_RELEASE);
dvander@2193: #elif defined __GNUC__
dvander@2193: free(address);
dvander@2193: #endif
dvander@2193: }
dvander@2193:
dvander@2226: void SourcePawnEngine::SetReadWriteExecute(void *ptr)
dvander@2226: {
dvander@2226: //:TODO: g_ExeMemory.SetRWE(ptr);
dvander@2226: SetReadExecute(ptr);
dvander@2226: }
dvander@2226:
dvander@2193: void *SourcePawnEngine::BaseAlloc(size_t size)
dvander@2193: {
dvander@2193: return malloc(size);
dvander@2193: }
dvander@2193:
dvander@2193: void SourcePawnEngine::BaseFree(void *memory)
dvander@2193: {
dvander@2193: free(memory);
dvander@2193: }
dvander@2193:
dvander@2193: sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
dvander@2193: {
dvander@2193: if (err != NULL)
dvander@2193: {
dvander@2193: *err = SP_ERROR_ABORTED;
dvander@2193: }
dvander@2193:
dvander@2193: return NULL;
dvander@2193: }
dvander@2193:
dvander@2193: sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
dvander@2193: {
dvander@2193: if (err != NULL)
dvander@2193: {
dvander@2193: *err = SP_ERROR_ABORTED;
dvander@2193: }
dvander@2193:
dvander@2193: return NULL;
dvander@2193: }
dvander@2193:
dvander@2193: int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
dvander@2193: {
dvander@2193: return SP_ERROR_ABORTED;
dvander@2193: }
dvander@2193:
dvander@2193: IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
dvander@2193: {
dvander@2193: IDebugListener *old = m_pDebugHook;
dvander@2193:
dvander@2193: m_pDebugHook = pListener;
dvander@2193:
dvander@2193: return old;
dvander@2193: }
dvander@2193:
dvander@2193: unsigned int SourcePawnEngine::GetEngineAPIVersion()
dvander@2193: {
dvander@2193: return SOURCEPAWN_ENGINE_API_VERSION;
dvander@2193: }
dvander@2193:
dvander@2226: unsigned int SourcePawnEngine::GetContextCallCount()
dvander@2226: {
dvander@2226: return 0;
dvander@2226: }
dvander@2226:
dvander@2226: void SourcePawnEngine::ReportError(BaseRuntime *runtime, int err, const char *errstr, cell_t rp_start)
dvander@2193: {
dvander@2226: if (m_pDebugHook == NULL)
dvander@2226: {
dvander@2226: return;
dvander@2226: }
dvander@2226:
dvander@2226: CContextTrace trace(runtime, err, errstr, rp_start);
dvander@2226:
dvander@2226: m_pDebugHook->OnContextExecuteError(runtime->GetDefaultContext(), &trace);
dvander@2226: }
dvander@2226:
dvander@2226: CContextTrace::CContextTrace(BaseRuntime *pRuntime, int err, const char *errstr, cell_t start_rp)
dvander@2226: : m_pRuntime(pRuntime), m_Error(err), m_pMsg(errstr), m_StartRp(start_rp), m_Level(0)
dvander@2226: {
dvander@2226: m_ctx = pRuntime->m_pCtx->GetCtx();
dvander@2226: m_pDebug = m_pRuntime->GetDebugInfo();
dvander@2193: }
dvander@2193:
dvander@2193: bool CContextTrace::DebugInfoAvailable()
dvander@2193: {
dvander@2226: return (m_pDebug != NULL);
dvander@2193: }
dvander@2193:
dvander@2193: const char *CContextTrace::GetCustomErrorString()
dvander@2193: {
dvander@2193: return m_pMsg;
dvander@2193: }
dvander@2193:
dvander@2193: int CContextTrace::GetErrorCode()
dvander@2193: {
dvander@2193: return m_Error;
dvander@2193: }
dvander@2193:
dvander@2193: const char *CContextTrace::GetErrorString()
dvander@2193: {
dvander@2226: if (m_Error > ERROR_MESSAGE_MAX || m_Error < 1)
dvander@2193: {
dvander@2193: return "Invalid error code";
dvander@2226: }
dvander@2226: else
dvander@2226: {
dvander@2193: return g_ErrorMsgTable[m_Error];
dvander@2193: }
dvander@2193: }
dvander@2193:
dvander@2193: void CContextTrace::ResetTrace()
dvander@2193: {
dvander@2226: m_Level = 0;
dvander@2193: }
dvander@2193:
dvander@2193: bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
dvander@2193: {
dvander@2226: cell_t cip;
dvander@2226:
dvander@2226: if (m_Level == 0)
dvander@2193: {
dvander@3990: cip = m_ctx->cip;
dvander@2193: }
dvander@2226: else if (m_ctx->rp > 0)
dvander@2193: {
dvander@2226: /* Entries go from ctx.rp - 1 to m_StartRp */
dvander@2226: cell_t offs, start, end;
dvander@2226:
dvander@2226: offs = m_Level - 1;
dvander@2226: start = m_ctx->rp - 1;
dvander@2226: end = m_StartRp;
dvander@2193:
dvander@2226: if (start - offs < end)
dvander@2226: {
dvander@2226: return false;
dvander@2226: }
dvander@2193:
dvander@2226: cip = m_ctx->rstk_cips[start - offs];
dvander@2226: }
dvander@2226: else
dvander@2193: {
dvander@2193: return false;
dvander@2193: }
dvander@2193:
dvander@2226: if (trace == NULL)
dvander@2193: {
dvander@2226: m_Level++;
dvander@2193: return true;
dvander@2193: }
dvander@2193:
dvander@2226: if (m_pDebug->LookupFile(cip, &(trace->filename)) != SP_ERROR_NONE)
dvander@2193: {
dvander@2193: trace->filename = NULL;
dvander@2193: }
dvander@2193:
dvander@2226: if (m_pDebug->LookupFunction(cip, &(trace->function)) != SP_ERROR_NONE)
dvander@2193: {
dvander@2193: trace->function = NULL;
dvander@2193: }
dvander@2193:
dvander@2226: if (m_pDebug->LookupLine(cip, &(trace->line)) != SP_ERROR_NONE)
dvander@2193: {
dvander@2193: trace->line = 0;
dvander@2193: }
dvander@2193:
dvander@2226: m_Level++;
dvander@2193:
dvander@2193: return true;
dvander@2193: }
dvander@2193:
dvander@2193: const char *CContextTrace::GetLastNative(uint32_t *index)
dvander@2193: {
dvander@2226: if (m_ctx->n_err == SP_ERROR_NONE)
dvander@2193: {
dvander@2193: return NULL;
dvander@2193: }
dvander@2193:
dvander@2193: sp_native_t *native;
dvander@2226: if (m_pRuntime->GetNativeByIndex(m_ctx->n_idx, &native) != SP_ERROR_NONE)
dvander@2193: {
dvander@2193: return NULL;
dvander@2193: }
dvander@2193:
dvander@2193: if (index)
dvander@2193: {
dvander@2226: *index = m_ctx->n_idx;
dvander@2193: }
dvander@2193:
dvander@2193: return native->name;
dvander@2193: }
dvander@2226:
dvander@2226: IDebugListener *SourcePawnEngine::GetDebugHook()
dvander@2226: {
dvander@2226: return m_pDebugHook;
dvander@2226: }
dvander@2226: