dvander@1231: /** dvander@1231: * vim: set ts=4 : ds@1330: * ============================================================================= dvander@1231: * SourcePawn dvander@1231: * Copyright (C) 2004-2007 AlliedModders LLC. All rights reserved. ds@1330: * ============================================================================= dvander@1231: * ds@1330: * This program is free software; you can redistribute it and/or modify it under ds@1330: * the terms of the GNU General Public License, version 3.0, as published by the ds@1330: * Free Software Foundation. dvander@1231: * ds@1330: * This program is distributed in the hope that it will be useful, but WITHOUT ds@1330: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ds@1330: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ds@1330: * details. dvander@1231: * ds@1330: * You should have received a copy of the GNU General Public License along with ds@1330: * this program. If not, see . dvander@1231: * ds@1330: * As a special exception, AlliedModders LLC gives you permission to link the ds@1330: * code of this program (as well as its derivative works) to "Half-Life 2," the ds@1330: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software ds@1330: * by the Valve Corporation. You must obey the GNU General Public License in ds@1330: * all respects for all other code used. Additionally, AlliedModders LLC grants ds@1330: * this exception to all derivative works. AlliedModders LLC defines further ds@1330: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), ds@1330: * or . dvander@1231: * dvander@1231: * Version: $Id$ dvander@1269: */ dvander@1269: dvander@1269: #include dvander@1269: #include dvander@1269: #include dvander@1269: #include dvander@1269: #include "sp_vm_api.h" dvander@1269: #include "sp_vm_basecontext.h" dvander@1269: #include "sp_vm_engine.h" dvander@1269: dvander@1269: #ifdef SOURCEMOD_BUILD dvander@1269: #include "Logger.h" dvander@1339: #include "DebugReporter.h" dvander@1269: #endif dvander@1269: dvander@1269: using namespace SourcePawn; dvander@1269: dvander@1269: extern SourcePawnEngine g_SourcePawn; dvander@1269: dvander@1269: #define CELLBOUNDMAX (INT_MAX/sizeof(cell_t)) dvander@1269: #define STACKMARGIN ((cell_t)(16*sizeof(cell_t))) dvander@1269: dvander@1269: int GlobalDebugBreak(sp_context_t *ctx, uint32_t frm, uint32_t cip) dvander@1269: { dvander@1269: g_SourcePawn.RunTracer(ctx, frm, cip); dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: BaseContext::BaseContext(sp_context_t *_ctx) dvander@1269: { dvander@1269: ctx = _ctx; dvander@1269: ctx->context = this; dvander@1269: ctx->dbreak = GlobalDebugBreak; dvander@1269: m_InExec = false; dvander@1269: m_CustomMsg = false; dvander@1269: m_funcsnum = ctx->vmbase->FunctionCount(ctx); dvander@1269: #if 0 dvander@1269: m_priv_funcs = NULL; dvander@1269: #endif dvander@1269: m_pub_funcs = NULL; dvander@1269: dvander@1269: #if 0 dvander@1269: /** dvander@1269: * Note: Since the m_plugin member will never change, dvander@1269: * it is safe to assume the function count will never change dvander@1269: */ dvander@1269: if (m_funcsnum && m_priv_funcs == NULL) dvander@1269: { dvander@1269: m_priv_funcs = new CFunction *[m_funcsnum]; dvander@1269: memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum); dvander@1269: } else { dvander@1269: m_priv_funcs = NULL; dvander@1269: } dvander@1269: #endif dvander@1269: dvander@1269: if (ctx->plugin->info.publics_num && m_pub_funcs == NULL) dvander@1269: { dvander@1269: m_pub_funcs = new CFunction *[ctx->plugin->info.publics_num]; dvander@1269: memset(m_pub_funcs, 0, sizeof(CFunction *) * ctx->plugin->info.publics_num); dvander@1269: } else { dvander@1269: m_pub_funcs = NULL; dvander@1269: } dvander@1269: dvander@1269: /* Initialize the null references */ dvander@1269: uint32_t index; dvander@1269: if (FindPubvarByName("NULL_VECTOR", &index) == SP_ERROR_NONE) dvander@1269: { dvander@1269: sp_pubvar_t *pubvar; dvander@1269: GetPubvarByIndex(index, &pubvar); dvander@1269: m_pNullVec = pubvar->offs; dvander@1269: } else { dvander@1269: m_pNullVec = NULL; dvander@1269: } dvander@1269: dvander@1269: if (FindPubvarByName("NULL_STRING", &index) == SP_ERROR_NONE) dvander@1269: { dvander@1269: sp_pubvar_t *pubvar; dvander@1269: GetPubvarByIndex(index, &pubvar); dvander@1269: m_pNullString = pubvar->offs; dvander@1269: } else { dvander@1269: m_pNullString = NULL; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: void BaseContext::FlushFunctionCache() dvander@1269: { dvander@1269: if (m_pub_funcs) dvander@1269: { dvander@1269: for (uint32_t i=0; iplugin->info.publics_num; i++) dvander@1269: { dvander@1269: delete m_pub_funcs[i]; dvander@1269: m_pub_funcs[i] = NULL; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: #if 0 dvander@1269: if (m_priv_funcs) dvander@1269: { dvander@1269: for (unsigned int i=0; iplugin->info.publics_num; i++) dvander@1269: { dvander@1269: if (!m_pub_funcs[i]) dvander@1269: { dvander@1269: continue; dvander@1269: } dvander@1269: if (GetPublicByIndex(i, &pub) != SP_ERROR_NONE) dvander@1269: { dvander@1269: continue; dvander@1269: } dvander@1339: m_pub_funcs[i]->Set(pub->code_offs, this, pub->funcid); dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: #if 0 dvander@1269: if (m_priv_funcs) dvander@1269: { dvander@1269: for (unsigned int i=0; i dvander@1269: } dvander@1269: } dvander@1269: #endif dvander@1269: } dvander@1269: dvander@1269: BaseContext::~BaseContext() dvander@1269: { dvander@1269: FlushFunctionCache(); dvander@1269: delete [] m_pub_funcs; dvander@1269: m_pub_funcs = NULL; dvander@1269: #if 0 dvander@1269: delete [] m_priv_funcs; dvander@1269: m_priv_funcs = NULL; dvander@1269: #endif dvander@1269: } dvander@1269: dvander@1269: void BaseContext::SetContext(sp_context_t *_ctx) dvander@1269: { dvander@1269: if (!_ctx) dvander@1269: { dvander@1269: return; dvander@1269: } dvander@1269: ctx = _ctx; dvander@1269: ctx->context = this; dvander@1269: ctx->dbreak = GlobalDebugBreak; dvander@1269: RefreshFunctionCache(); dvander@1269: } dvander@1269: dvander@1269: IVirtualMachine *BaseContext::GetVirtualMachine() dvander@1269: { dvander@1269: return (IVirtualMachine *)ctx->vmbase; dvander@1269: } dvander@1269: dvander@1269: sp_context_t *BaseContext::GetContext() dvander@1269: { dvander@1269: return ctx; dvander@1269: } dvander@1269: dvander@1269: bool BaseContext::IsDebugging() dvander@1269: { dvander@1269: return (ctx->flags & SPFLAG_PLUGIN_DEBUG); dvander@1269: } dvander@1269: dvander@1269: int BaseContext::SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn) dvander@1269: { dvander@1269: if (!IsDebugging()) dvander@1269: { dvander@1269: return SP_ERROR_NOTDEBUGGING; dvander@1269: } dvander@1269: dvander@1269: *oldpfn = ctx->dbreak; dvander@1269: ctx->dbreak = newpfn; dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: IPluginDebugInfo *BaseContext::GetDebugInfo() dvander@1269: { dvander@1339: if (!IsDebugging()) dvander@1339: { dvander@1339: return NULL; dvander@1339: } dvander@1269: return this; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::Execute(uint32_t code_addr, cell_t *result) dvander@1269: { dvander@1269: if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED) dvander@1269: { dvander@1269: return SP_ERROR_NOT_RUNNABLE; dvander@1269: } dvander@1269: dvander@1269: /* tada, prevent a crash */ dvander@1269: cell_t _ignore_result; dvander@1269: if (!result) dvander@1269: { dvander@1269: result = &_ignore_result; dvander@1269: } dvander@1269: dvander@1269: IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase; dvander@1269: dvander@1269: uint32_t pushcount = ctx->pushcount; dvander@1269: int err; dvander@1269: dvander@1339: if ((err = PushCell(pushcount++)) != SP_ERROR_NONE) dvander@1339: { dvander@1339: #if defined SOURCEMOD_BUILD dvander@1339: g_DbgReporter.GenerateCodeError(this, code_addr, err, "Stack error; cannot complete execution!"); dvander@1339: #endif dvander@1339: return SP_ERROR_NOT_RUNNABLE; dvander@1339: } dvander@1269: ctx->pushcount = 0; dvander@1269: dvander@1367: cell_t save_sp = ctx->sp + (pushcount * sizeof(cell_t)); dvander@1269: cell_t save_hp = ctx->hp; dvander@1269: uint32_t n_idx = ctx->n_idx; dvander@1269: dvander@1269: bool wasExec = m_InExec; dvander@1269: dvander@1269: /* Clear the error state, if any */ dvander@1269: ctx->n_err = SP_ERROR_NONE; dvander@1269: ctx->n_idx = 0; dvander@1269: m_InExec = true; dvander@1269: m_MsgCache[0] = '\0'; dvander@1269: m_CustomMsg = false; dvander@1269: dvander@1269: g_SourcePawn.PushTracer(ctx); dvander@1269: dvander@1269: err = vm->ContextExecute(ctx, code_addr, result); dvander@1269: dvander@1269: m_InExec = wasExec; dvander@1269: dvander@1269: /** dvander@1269: * :TODO: Calling from a plugin in here will erase the cached message... dvander@1269: * Should that be documented? dvander@1269: */ dvander@1269: g_SourcePawn.PopTracer(err, m_CustomMsg ? m_MsgCache : NULL); dvander@1269: dvander@1339: #if defined SOURCEMOD_BUILD dvander@1269: if (err == SP_ERROR_NONE) dvander@1269: { dvander@1367: if (ctx->sp != save_sp) dvander@1269: { dvander@1339: g_DbgReporter.GenerateCodeError(this, dvander@1339: code_addr, dvander@1339: SP_ERROR_STACKLEAK, dvander@1339: "Stack leak detected: sp:%d should be %d!", dvander@1339: ctx->sp, dvander@1339: save_sp); dvander@1269: } dvander@1269: if (ctx->hp != save_hp) dvander@1269: { dvander@1339: g_DbgReporter.GenerateCodeError(this, dvander@1339: code_addr, dvander@1339: SP_ERROR_HEAPLEAK, dvander@1339: "Heap leak detected: sp:%d should be %d!", dvander@1339: ctx->hp, dvander@1339: save_hp); dvander@1269: } dvander@1269: } dvander@1269: #endif dvander@1339: dvander@1269: if (err != SP_ERROR_NONE) dvander@1269: { dvander@1269: ctx->sp = save_sp; dvander@1269: ctx->hp = save_hp; dvander@1269: } dvander@1269: dvander@1269: ctx->n_idx = n_idx; dvander@1367: ctx->n_err = SP_ERROR_NONE; dvander@1367: m_MsgCache[0] = '\0'; dvander@1367: m_CustomMsg = false; dvander@1269: dvander@1269: return err; dvander@1269: } dvander@1269: dvander@1269: void BaseContext::SetErrorMessage(const char *msg, va_list ap) dvander@1269: { dvander@1269: m_CustomMsg = true; dvander@1269: dvander@1269: vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap); dvander@1269: } dvander@1269: dvander@1269: cell_t BaseContext::ThrowNativeErrorEx(int error, const char *msg, ...) dvander@1269: { dvander@1269: if (!m_InExec) dvander@1269: { dvander@1269: return 0; dvander@1269: } dvander@1269: dvander@1269: ctx->n_err = error; dvander@1269: dvander@1269: if (msg) dvander@1269: { dvander@1269: va_list ap; dvander@1269: va_start(ap, msg); dvander@1269: SetErrorMessage(msg, ap); dvander@1269: va_end(ap); dvander@1269: } dvander@1269: dvander@1269: return 0; dvander@1269: } dvander@1269: dvander@1269: cell_t BaseContext::ThrowNativeError(const char *msg, ...) dvander@1269: { dvander@1269: if (!m_InExec) dvander@1269: { dvander@1269: return 0; dvander@1269: } dvander@1269: dvander@1269: ctx->n_err = SP_ERROR_NATIVE; dvander@1269: dvander@1269: if (msg) dvander@1269: { dvander@1269: va_list ap; dvander@1269: va_start(ap, msg); dvander@1269: SetErrorMessage(msg, ap); dvander@1269: va_end(ap); dvander@1269: } dvander@1269: dvander@1269: return 0; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr) dvander@1269: { dvander@1269: cell_t *addr; dvander@1269: ucell_t realmem; dvander@1269: dvander@1269: #if 0 dvander@1269: if (cells > CELLBOUNDMAX) dvander@1269: { dvander@1269: return SP_ERROR_ARAM; dvander@1269: } dvander@1269: #else dvander@1269: assert(cells < CELLBOUNDMAX); dvander@1269: #endif dvander@1269: dvander@1269: realmem = cells * sizeof(cell_t); dvander@1269: dvander@1269: /** dvander@1269: * Check if the space between the heap and stack is sufficient. dvander@1269: */ dvander@1269: if ((cell_t)(ctx->sp - ctx->hp - realmem) < STACKMARGIN) dvander@1269: { dvander@1269: return SP_ERROR_HEAPLOW; dvander@1269: } dvander@1269: dvander@1269: addr = (cell_t *)(ctx->memory + ctx->hp); dvander@1269: /* store size of allocation in cells */ dvander@1269: *addr = (cell_t)cells; dvander@1269: addr++; dvander@1269: ctx->hp += sizeof(cell_t); dvander@1269: dvander@1269: *local_addr = ctx->hp; dvander@1269: dvander@1269: if (phys_addr) dvander@1269: { dvander@1269: *phys_addr = addr; dvander@1269: } dvander@1269: dvander@1269: ctx->hp += realmem; dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::HeapPop(cell_t local_addr) dvander@1269: { dvander@1269: cell_t cellcount; dvander@1269: cell_t *addr; dvander@1269: dvander@1269: /* check the bounds of this address */ dvander@1269: local_addr -= sizeof(cell_t); dvander@1269: if (local_addr < ctx->heap_base || local_addr >= ctx->sp) dvander@1269: { dvander@1269: return SP_ERROR_INVALID_ADDRESS; dvander@1269: } dvander@1269: dvander@1269: addr = (cell_t *)(ctx->memory + local_addr); dvander@1269: cellcount = (*addr) * sizeof(cell_t); dvander@1269: /* check if this memory count looks valid */ dvander@1269: if ((signed)(ctx->hp - cellcount - sizeof(cell_t)) != local_addr) dvander@1269: { dvander@1269: return SP_ERROR_INVALID_ADDRESS; dvander@1269: } dvander@1269: dvander@1269: ctx->hp = local_addr; dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: dvander@1269: int BaseContext::HeapRelease(cell_t local_addr) dvander@1269: { dvander@1269: if (local_addr < ctx->heap_base) dvander@1269: { dvander@1269: return SP_ERROR_INVALID_ADDRESS; dvander@1269: } dvander@1269: dvander@1269: ctx->hp = local_addr - sizeof(cell_t); dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::FindNativeByName(const char *name, uint32_t *index) dvander@1269: { dvander@1269: int high; dvander@1269: dvander@1269: high = ctx->plugin->info.natives_num - 1; dvander@1269: dvander@1269: for (uint32_t i=0; iplugin->info.natives_num; i++) dvander@1269: { dvander@1269: if (strcmp(ctx->natives[i].name, name) == 0) dvander@1269: { dvander@1269: if (index) dvander@1269: { dvander@1269: *index = i; dvander@1269: } dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NOT_FOUND; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::GetNativeByIndex(uint32_t index, sp_native_t **native) dvander@1269: { dvander@1269: if (index >= ctx->plugin->info.natives_num) dvander@1269: { dvander@1269: return SP_ERROR_INDEX; dvander@1269: } dvander@1269: dvander@1269: if (native) dvander@1269: { dvander@1269: *native = &(ctx->natives[index]); dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: dvander@1269: uint32_t BaseContext::GetNativesNum() dvander@1269: { dvander@1269: return ctx->plugin->info.natives_num; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::FindPublicByName(const char *name, uint32_t *index) dvander@1269: { dvander@1269: int diff, high, low; dvander@1269: uint32_t mid; dvander@1269: dvander@1269: high = ctx->plugin->info.publics_num - 1; dvander@1269: low = 0; dvander@1269: dvander@1269: while (low <= high) dvander@1269: { dvander@1269: mid = (low + high) / 2; dvander@1269: diff = strcmp(ctx->publics[mid].name, name); dvander@1269: if (diff == 0) dvander@1269: { dvander@1269: if (index) dvander@1269: { dvander@1269: *index = mid; dvander@1269: } dvander@1269: return SP_ERROR_NONE; dvander@1269: } else if (diff < 0) { dvander@1269: low = mid + 1; dvander@1269: } else { dvander@1269: high = mid - 1; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NOT_FOUND; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::GetPublicByIndex(uint32_t index, sp_public_t **pblic) dvander@1269: { dvander@1269: if (index >= ctx->plugin->info.publics_num) dvander@1269: { dvander@1269: return SP_ERROR_INDEX; dvander@1269: } dvander@1269: dvander@1269: if (pblic) dvander@1269: { dvander@1269: *pblic = &(ctx->publics[index]); dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: uint32_t BaseContext::GetPublicsNum() dvander@1269: { dvander@1269: return ctx->plugin->info.publics_num; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar) dvander@1269: { dvander@1269: if (index >= ctx->plugin->info.pubvars_num) dvander@1269: { dvander@1269: return SP_ERROR_INDEX; dvander@1269: } dvander@1269: dvander@1269: if (pubvar) dvander@1269: { dvander@1269: *pubvar = &(ctx->pubvars[index]); dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::FindPubvarByName(const char *name, uint32_t *index) dvander@1269: { dvander@1269: int diff, high, low; dvander@1269: uint32_t mid; dvander@1269: dvander@1269: high = ctx->plugin->info.pubvars_num - 1; dvander@1269: low = 0; dvander@1269: dvander@1269: while (low <= high) dvander@1269: { dvander@1269: mid = (low + high) / 2; dvander@1269: diff = strcmp(ctx->pubvars[mid].name, name); dvander@1269: if (diff == 0) dvander@1269: { dvander@1269: if (index) dvander@1269: { dvander@1269: *index = mid; dvander@1269: } dvander@1269: return SP_ERROR_NONE; dvander@1269: } else if (diff < 0) { dvander@1269: low = mid + 1; dvander@1269: } else { dvander@1269: high = mid - 1; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NOT_FOUND; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr) dvander@1269: { dvander@1269: if (index >= ctx->plugin->info.pubvars_num) dvander@1269: { dvander@1269: return SP_ERROR_INDEX; dvander@1269: } dvander@1269: dvander@1269: *local_addr = ctx->plugin->info.pubvars[index].address; dvander@1269: *phys_addr = ctx->pubvars[index].offs; dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: uint32_t BaseContext::GetPubVarsNum() dvander@1269: { dvander@1269: return ctx->plugin->info.pubvars_num; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::BindNatives(const sp_nativeinfo_t *natives, unsigned int num, int overwrite) dvander@1269: { dvander@1269: uint32_t i, j, max; dvander@1269: dvander@1269: max = ctx->plugin->info.natives_num; dvander@1269: dvander@1269: for (i=0; inatives[i].status == SP_NATIVE_BOUND) && !overwrite) dvander@1269: { dvander@1269: continue; dvander@1269: } dvander@1269: dvander@1269: for (j=0; (natives[j].name) && (!num || jnatives[i].name, natives[j].name)) dvander@1269: { dvander@1269: ctx->natives[i].pfn = natives[j].func; dvander@1269: ctx->natives[i].status = SP_NATIVE_BOUND; dvander@1269: } dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::BindNative(const sp_nativeinfo_t *native) dvander@1269: { dvander@1269: uint32_t index; dvander@1269: int err; dvander@1269: dvander@1269: if ((err = FindNativeByName(native->name, &index)) != SP_ERROR_NONE) dvander@1269: { dvander@1269: return err; dvander@1269: } dvander@1269: dvander@1269: ctx->natives[index].pfn = native->func; dvander@1269: ctx->natives[index].status = SP_NATIVE_BOUND; dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native) dvander@1269: { dvander@1269: uint32_t nativesnum, i; dvander@1269: dvander@1269: nativesnum = ctx->plugin->info.natives_num; dvander@1269: dvander@1269: for (i=0; inatives[i].status == SP_NATIVE_UNBOUND) dvander@1269: { dvander@1269: ctx->natives[i].pfn = native; dvander@1269: ctx->natives[i].status = SP_NATIVE_BOUND; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr) dvander@1269: { dvander@1269: if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size)) dvander@1269: { dvander@1269: return SP_ERROR_INVALID_ADDRESS; dvander@1269: } dvander@1269: dvander@1269: if (phys_addr) dvander@1269: { dvander@1269: *phys_addr = (cell_t *)(ctx->memory + local_addr); dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::PushCell(cell_t value) dvander@1269: { dvander@1269: if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t))) dvander@1269: { dvander@1269: return SP_ERROR_STACKLOW; dvander@1269: } dvander@1269: dvander@1269: ctx->sp -= sizeof(cell_t); dvander@1269: *(cell_t *)(ctx->memory + ctx->sp) = value; dvander@1269: ctx->pushcount++; dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells) dvander@1269: { dvander@1269: unsigned int i; dvander@1269: int err; dvander@1269: dvander@1269: for (i=0; isp += (cell_t)(i * sizeof(cell_t)); dvander@1269: ctx->pushcount -= i; dvander@1269: return err; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells) dvander@1269: { dvander@1269: cell_t *ph_addr; dvander@1269: int err; dvander@1269: dvander@1269: if ((err = HeapAlloc(numcells, local_addr, &ph_addr)) != SP_ERROR_NONE) dvander@1269: { dvander@1269: return err; dvander@1269: } dvander@1269: dvander@1269: memcpy(ph_addr, array, numcells * sizeof(cell_t)); dvander@1269: dvander@1269: if ((err = PushCell(*local_addr)) != SP_ERROR_NONE) dvander@1269: { dvander@1269: HeapRelease(*local_addr); dvander@1269: return err; dvander@1269: } dvander@1269: dvander@1269: if (phys_addr) dvander@1269: { dvander@1269: *phys_addr = ph_addr; dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::LocalToString(cell_t local_addr, char **addr) dvander@1269: { dvander@1269: if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size)) dvander@1269: { dvander@1269: return SP_ERROR_INVALID_ADDRESS; dvander@1269: } dvander@1269: *addr = (char *)(ctx->memory + local_addr); dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::PushString(cell_t *local_addr, char **phys_addr, const char *string) dvander@1269: { dvander@1269: char *ph_addr; dvander@1269: int err; dvander@1269: unsigned int len, numcells = ((len=strlen(string)) + sizeof(cell_t)) / sizeof(cell_t); dvander@1269: dvander@1269: if ((err = HeapAlloc(numcells, local_addr, (cell_t **)&ph_addr)) != SP_ERROR_NONE) dvander@1269: { dvander@1269: return err; dvander@1269: } dvander@1269: dvander@1269: memcpy(ph_addr, string, len); dvander@1269: ph_addr[len] = '\0'; dvander@1269: dvander@1269: if ((err = PushCell(*local_addr)) != SP_ERROR_NONE) dvander@1269: { dvander@1269: HeapRelease(*local_addr); dvander@1269: return err; dvander@1269: } dvander@1269: dvander@1269: if (phys_addr) dvander@1269: { dvander@1269: *phys_addr = ph_addr; dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source) dvander@1269: { dvander@1269: char *dest; dvander@1269: size_t len; dvander@1269: dvander@1269: if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size)) dvander@1269: { dvander@1269: return SP_ERROR_INVALID_ADDRESS; dvander@1269: } dvander@1269: dvander@1269: if (bytes == 0) dvander@1269: { dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: len = strlen(source); dvander@1269: dest = (char *)(ctx->memory + local_addr); dvander@1269: dvander@1269: if (len >= bytes) dvander@1269: { dvander@1269: len = bytes - 1; dvander@1269: } dvander@1269: dvander@1269: memcpy(dest, source, len); dvander@1269: dest[len] = '\0'; dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: inline int __CheckValidChar(char *c) dvander@1269: { dvander@1269: int count; dvander@1269: int bytecount = 0; dvander@1269: dvander@1269: for (count=1; (*c & 0xC0) == 0x80; count++) dvander@1269: { dvander@1269: c--; dvander@1269: } dvander@1269: dvander@1269: switch (*c & 0xF0) dvander@1269: { dvander@1269: case 0xC0: dvander@1269: case 0xD0: dvander@1269: { dvander@1269: bytecount = 2; dvander@1269: break; dvander@1269: } dvander@1269: case 0xE0: dvander@1269: { dvander@1269: bytecount = 3; dvander@1269: break; dvander@1269: } dvander@1269: case 0xF0: dvander@1269: { dvander@1269: bytecount = 4; dvander@1269: break; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: if (bytecount != count) dvander@1269: { dvander@1269: return count; dvander@1269: } dvander@1269: dvander@1269: return 0; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes) dvander@1269: { dvander@1269: char *dest; dvander@1269: size_t len; dvander@1269: bool needtocheck = false; dvander@1269: dvander@1269: if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size)) dvander@1269: { dvander@1269: return SP_ERROR_INVALID_ADDRESS; dvander@1269: } dvander@1269: dvander@1269: if (maxbytes == 0) dvander@1269: { dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: len = strlen(source); dvander@1269: dest = (char *)(ctx->memory + local_addr); dvander@1269: dvander@1269: if ((size_t)len >= maxbytes) dvander@1269: { dvander@1269: len = maxbytes - 1; dvander@1269: needtocheck = true; dvander@1269: } dvander@1269: dvander@1269: memcpy(dest, source, len); dvander@1269: if ((dest[len-1] & 1<<7) && needtocheck) dvander@1269: { dvander@1269: len -= __CheckValidChar(dest+len-1); dvander@1269: } dvander@1269: dest[len] = '\0'; dvander@1269: dvander@1269: if (wrtnbytes) dvander@1269: { dvander@1269: *wrtnbytes = len; dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: #define USHR(x) ((unsigned int)(x)>>1) dvander@1269: dvander@1269: int BaseContext::LookupFile(ucell_t addr, const char **filename) dvander@1269: { dvander@1269: int high, low, mid; dvander@1269: dvander@1269: high = ctx->plugin->debug.files_num; dvander@1269: low = -1; dvander@1269: dvander@1269: while (high - low > 1) dvander@1269: { dvander@1269: mid = USHR(low + high); dvander@1269: if (ctx->files[mid].addr <= addr) dvander@1269: { dvander@1269: low = mid; dvander@1269: } else { dvander@1269: high = mid; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: if (low == -1) dvander@1269: { dvander@1269: return SP_ERROR_NOT_FOUND; dvander@1269: } dvander@1269: dvander@1269: *filename = ctx->files[low].name; dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::LookupFunction(ucell_t addr, const char **name) dvander@1269: { dvander@1269: uint32_t iter, max = ctx->plugin->debug.syms_num; dvander@1269: dvander@1269: for (iter=0; itersymbols[iter].sym->ident == SP_SYM_FUNCTION) dvander@1269: && (ctx->symbols[iter].codestart <= addr) dvander@1269: && (ctx->symbols[iter].codeend > addr)) dvander@1269: { dvander@1269: break; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: if (iter >= max) dvander@1269: { dvander@1269: return SP_ERROR_NOT_FOUND; dvander@1269: } dvander@1269: dvander@1269: *name = ctx->symbols[iter].name; dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::LookupLine(ucell_t addr, uint32_t *line) dvander@1269: { dvander@1269: int high, low, mid; dvander@1269: dvander@1269: high = ctx->plugin->debug.lines_num; dvander@1269: low = -1; dvander@1269: dvander@1269: while (high - low > 1) dvander@1269: { dvander@1269: mid = USHR(low + high); dvander@1269: if (ctx->lines[mid].addr <= addr) dvander@1269: { dvander@1269: low = mid; dvander@1269: } else { dvander@1269: high = mid; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: if (low == -1) dvander@1269: { dvander@1269: return SP_ERROR_NOT_FOUND; dvander@1269: } dvander@1269: dvander@1269: /* Since the CIP occurs BEFORE the line, we have to add one */ dvander@1269: *line = ctx->lines[low].line + 1; dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: IPluginFunction *BaseContext::GetFunctionById(funcid_t func_id) dvander@1269: { dvander@1269: CFunction *pFunc = NULL; dvander@1269: dvander@1269: if (func_id & 1) dvander@1269: { dvander@1269: func_id >>= 1; dvander@1269: if (func_id >= ctx->plugin->info.publics_num) dvander@1269: { dvander@1269: return NULL; dvander@1269: } dvander@1269: pFunc = m_pub_funcs[func_id]; dvander@1269: if (!pFunc) dvander@1269: { dvander@1339: m_pub_funcs[func_id] = new CFunction(ctx->publics[func_id].code_offs, this, ctx->publics[func_id].funcid); dvander@1269: pFunc = m_pub_funcs[func_id]; dvander@1269: } else if (pFunc->IsInvalidated()) { dvander@1339: pFunc->Set(ctx->publics[func_id].code_offs, this, ctx->publics[func_id].funcid); dvander@1269: } dvander@1269: } else { dvander@1269: /* :TODO: currently not used */ dvander@1269: #if 0 dvander@1269: func_id >>= 1; dvander@1269: unsigned int index; dvander@1269: if (!g_pVM->FunctionLookup(ctx, func_id, &index)) dvander@1269: { dvander@1269: return NULL; dvander@1269: } dvander@1269: pFunc = m_priv_funcs[func_id]; dvander@1269: if (!pFunc) dvander@1269: { dvander@1269: m_priv_funcs[func_id] = new CFunction(save, this); dvander@1269: pFunc = m_priv_funcs[func_id]; dvander@1269: } dvander@1269: #endif dvander@1269: } dvander@1269: dvander@1269: return pFunc; dvander@1269: } dvander@1269: dvander@1269: IPluginFunction *BaseContext::GetFunctionByName(const char *public_name) dvander@1269: { dvander@1269: uint32_t index; dvander@1269: dvander@1269: if (FindPublicByName(public_name, &index) != SP_ERROR_NONE) dvander@1269: { dvander@1269: return NULL; dvander@1269: } dvander@1269: dvander@1269: CFunction *pFunc = m_pub_funcs[index]; dvander@1269: if (!pFunc) dvander@1269: { dvander@1269: sp_public_t *pub = NULL; dvander@1269: GetPublicByIndex(index, &pub); dvander@1269: if (pub) dvander@1269: { dvander@1339: m_pub_funcs[index] = new CFunction(pub->code_offs, this, pub->funcid); dvander@1269: } dvander@1269: pFunc = m_pub_funcs[index]; dvander@1269: } else if (pFunc->IsInvalidated()) { dvander@1269: sp_public_t *pub = NULL; dvander@1269: GetPublicByIndex(index, &pub); dvander@1269: if (pub) dvander@1269: { dvander@1339: pFunc->Set(pub->code_offs, this, pub->funcid); dvander@1269: } else { dvander@1269: pFunc = NULL; dvander@1269: } dvander@1269: } dvander@1269: dvander@1269: return pFunc; dvander@1269: } dvander@1269: dvander@1269: int BaseContext::LocalToStringNULL(cell_t local_addr, char **addr) dvander@1269: { dvander@1269: int err; dvander@1269: if ((err = LocalToString(local_addr, addr)) != SP_ERROR_NONE) dvander@1269: { dvander@1269: return err; dvander@1269: } dvander@1269: dvander@1269: if ((cell_t *)*addr == m_pNullString) dvander@1269: { dvander@1269: *addr = NULL; dvander@1269: } dvander@1269: dvander@1269: return SP_ERROR_NONE; dvander@1269: } dvander@1269: dvander@1269: #if defined SOURCEMOD_BUILD dvander@1269: SourceMod::IdentityToken_t *BaseContext::GetIdentity() dvander@1269: { dvander@1269: return m_pToken; dvander@1269: } dvander@1269: dvander@1269: void BaseContext::SetIdentity(SourceMod::IdentityToken_t *token) dvander@1269: { dvander@1269: m_pToken = token; dvander@1269: } dvander@1269: dvander@1269: cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type) dvander@1269: { dvander@1269: if (type == SP_NULL_VECTOR) dvander@1269: { dvander@1269: return m_pNullVec; dvander@1269: } dvander@1269: dvander@1269: return NULL; dvander@1269: } dvander@1269: #endif