3 * ================================================================
4 * SourcePawn (C)2004-2007 AlliedModders LLC. All rights reserved.
5 * ================================================================
7 * This file is not open source and may not be copied without explicit
8 * written permission of AlliedModders LLC. This file may not be redistributed
9 * in whole or significant part.
10 * For information, see LICENSE.txt or http://www.sourcemod.net/license.php
19 #include "sp_vm_api.h"
20 #include "sp_vm_basecontext.h"
21 #include "sp_vm_engine.h"
23 #ifdef SOURCEMOD_BUILD
27 using namespace SourcePawn;
29 extern SourcePawnEngine g_SourcePawn;
31 #define CELLBOUNDMAX (INT_MAX/sizeof(cell_t))
32 #define STACKMARGIN ((cell_t)(16*sizeof(cell_t)))
34 int GlobalDebugBreak(sp_context_t *ctx, uint32_t frm, uint32_t cip)
36 g_SourcePawn.RunTracer(ctx, frm, cip);
41 BaseContext::BaseContext(sp_context_t *_ctx)
45 ctx->dbreak = GlobalDebugBreak;
48 m_funcsnum = ctx->vmbase->FunctionCount(ctx);
56 * Note: Since the m_plugin member will never change,
57 * it is safe to assume the function count will never change
59 if (m_funcsnum && m_priv_funcs == NULL)
61 m_priv_funcs = new CFunction *[m_funcsnum];
62 memset(m_priv_funcs, 0, sizeof(CFunction *) * m_funcsnum);
68 if (ctx->plugin->info.publics_num && m_pub_funcs == NULL)
70 m_pub_funcs = new CFunction *[ctx->plugin->info.publics_num];
71 memset(m_pub_funcs, 0, sizeof(CFunction *) * ctx->plugin->info.publics_num);
77 void BaseContext::FlushFunctionCache()
81 for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
83 delete m_pub_funcs[i];
84 m_pub_funcs[i] = NULL;
91 for (unsigned int i=0; i<m_funcsnum; i++)
93 delete m_priv_funcs[i];
94 m_priv_funcs[i] = NULL;
100 void BaseContext::RefreshFunctionCache()
105 for (uint32_t i=0; i<ctx->plugin->info.publics_num; i++)
111 if (GetPublicByIndex(i, &pub) != SP_ERROR_NONE)
115 m_pub_funcs[i]->Set(pub->code_offs, this);
122 for (unsigned int i=0; i<m_funcsnum; i++)
124 if (!m_priv_funcs[i])
134 BaseContext::~BaseContext()
136 FlushFunctionCache();
137 delete [] m_pub_funcs;
140 delete [] m_priv_funcs;
145 void BaseContext::SetContext(sp_context_t *_ctx)
153 ctx->dbreak = GlobalDebugBreak;
154 RefreshFunctionCache();
157 IVirtualMachine *BaseContext::GetVirtualMachine()
159 return (IVirtualMachine *)ctx->vmbase;
162 sp_context_t *BaseContext::GetContext()
167 bool BaseContext::IsDebugging()
169 return (ctx->flags & SPFLAG_PLUGIN_DEBUG);
172 int BaseContext::SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK *oldpfn)
176 return SP_ERROR_NOTDEBUGGING;
179 *oldpfn = ctx->dbreak;
180 ctx->dbreak = newpfn;
182 return SP_ERROR_NONE;
185 IPluginDebugInfo *BaseContext::GetDebugInfo()
190 int BaseContext::Execute(uint32_t code_addr, cell_t *result)
192 if ((ctx->flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED)
194 return SP_ERROR_NOT_RUNNABLE;
197 /* tada, prevent a crash */
198 cell_t _ignore_result;
201 result = &_ignore_result;
204 IVirtualMachine *vm = (IVirtualMachine *)ctx->vmbase;
206 uint32_t pushcount = ctx->pushcount;
209 PushCell(pushcount++);
212 cell_t save_sp = ctx->sp;
213 cell_t save_hp = ctx->hp;
214 uint32_t n_idx = ctx->n_idx;
216 bool wasExec = m_InExec;
218 /* Clear the error state, if any */
219 ctx->n_err = SP_ERROR_NONE;
222 m_MsgCache[0] = '\0';
225 g_SourcePawn.PushTracer(ctx);
227 err = vm->ContextExecute(ctx, code_addr, result);
232 * :TODO: Calling from a plugin in here will erase the cached message...
233 * Should that be documented?
235 g_SourcePawn.PopTracer(err, m_CustomMsg ? m_MsgCache : NULL);
237 #if 1//defined _DEBUG
238 //:TODO: debug code for leak detection, remove before the release?
239 if (err == SP_ERROR_NONE)
241 if ((ctx->sp - (cell_t)(pushcount * sizeof(cell_t))) != save_sp)
244 ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
245 g_Logger.LogError("Stack leak detected: sp:%d should be %d on function %s", ctx->sp, save_sp, name);
247 if (ctx->hp != save_hp)
250 ctx->context->GetDebugInfo()->LookupFunction(code_addr, &name);
251 g_Logger.LogError("Heap leak detected: hp:%d should be %d on function %s", ctx->hp, save_hp, name);
253 //assert(ctx->sp - pushcount * sizeof(cell_t) == save_sp);
254 //assert(ctx->hp == save_hp);
257 if (err != SP_ERROR_NONE)
268 void BaseContext::SetErrorMessage(const char *msg, va_list ap)
272 vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap);
275 cell_t BaseContext::ThrowNativeErrorEx(int error, const char *msg, ...)
288 SetErrorMessage(msg, ap);
295 cell_t BaseContext::ThrowNativeError(const char *msg, ...)
302 ctx->n_err = SP_ERROR_NATIVE;
308 SetErrorMessage(msg, ap);
315 int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr)
321 if (cells > CELLBOUNDMAX)
323 return SP_ERROR_ARAM;
326 assert(cells < CELLBOUNDMAX);
329 realmem = cells * sizeof(cell_t);
332 * Check if the space between the heap and stack is sufficient.
334 if ((cell_t)(ctx->sp - ctx->hp - realmem) < STACKMARGIN)
336 return SP_ERROR_HEAPLOW;
339 addr = (cell_t *)(ctx->memory + ctx->hp);
340 /* store size of allocation in cells */
341 *addr = (cell_t)cells;
343 ctx->hp += sizeof(cell_t);
345 *local_addr = ctx->hp;
354 return SP_ERROR_NONE;
357 int BaseContext::HeapPop(cell_t local_addr)
362 /* check the bounds of this address */
363 local_addr -= sizeof(cell_t);
364 if (local_addr < ctx->heap_base || local_addr >= ctx->sp)
366 return SP_ERROR_INVALID_ADDRESS;
369 addr = (cell_t *)(ctx->memory + local_addr);
370 cellcount = (*addr) * sizeof(cell_t);
371 /* check if this memory count looks valid */
372 if ((signed)(ctx->hp - cellcount - sizeof(cell_t)) != local_addr)
374 return SP_ERROR_INVALID_ADDRESS;
377 ctx->hp = local_addr;
379 return SP_ERROR_NONE;
383 int BaseContext::HeapRelease(cell_t local_addr)
385 if (local_addr < ctx->heap_base)
387 return SP_ERROR_INVALID_ADDRESS;
390 ctx->hp = local_addr - sizeof(cell_t);
392 return SP_ERROR_NONE;
395 int BaseContext::FindNativeByName(const char *name, uint32_t *index)
399 high = ctx->plugin->info.natives_num - 1;
401 for (uint32_t i=0; i<ctx->plugin->info.natives_num; i++)
403 if (strcmp(ctx->natives[i].name, name) == 0)
409 return SP_ERROR_NONE;
413 return SP_ERROR_NOT_FOUND;
416 int BaseContext::GetNativeByIndex(uint32_t index, sp_native_t **native)
418 if (index >= ctx->plugin->info.natives_num)
420 return SP_ERROR_INDEX;
425 *native = &(ctx->natives[index]);
428 return SP_ERROR_NONE;
432 uint32_t BaseContext::GetNativesNum()
434 return ctx->plugin->info.natives_num;
437 int BaseContext::FindPublicByName(const char *name, uint32_t *index)
442 high = ctx->plugin->info.publics_num - 1;
447 mid = (low + high) / 2;
448 diff = strcmp(ctx->publics[mid].name, name);
455 return SP_ERROR_NONE;
456 } else if (diff < 0) {
463 return SP_ERROR_NOT_FOUND;
466 int BaseContext::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
468 if (index >= ctx->plugin->info.publics_num)
470 return SP_ERROR_INDEX;
475 *pblic = &(ctx->publics[index]);
478 return SP_ERROR_NONE;
481 uint32_t BaseContext::GetPublicsNum()
483 return ctx->plugin->info.publics_num;
486 int BaseContext::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
488 if (index >= ctx->plugin->info.pubvars_num)
490 return SP_ERROR_INDEX;
495 *pubvar = &(ctx->pubvars[index]);
498 return SP_ERROR_NONE;
501 int BaseContext::FindPubvarByName(const char *name, uint32_t *index)
506 high = ctx->plugin->info.pubvars_num - 1;
511 mid = (low + high) / 2;
512 diff = strcmp(ctx->pubvars[mid].name, name);
519 return SP_ERROR_NONE;
520 } else if (diff < 0) {
527 return SP_ERROR_NOT_FOUND;
530 int BaseContext::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
532 if (index >= ctx->plugin->info.pubvars_num)
534 return SP_ERROR_INDEX;
537 *local_addr = ctx->plugin->info.pubvars[index].address;
538 *phys_addr = ctx->pubvars[index].offs;
540 return SP_ERROR_NONE;
543 uint32_t BaseContext::GetPubVarsNum()
545 return ctx->plugin->info.pubvars_num;
548 int BaseContext::BindNatives(const sp_nativeinfo_t *natives, unsigned int num, int overwrite)
552 max = ctx->plugin->info.natives_num;
554 for (i=0; i<max; i++)
556 if ((ctx->natives[i].status == SP_NATIVE_BOUND) && !overwrite)
561 for (j=0; (natives[j].name) && (!num || j<num); j++)
563 if (!strcmp(ctx->natives[i].name, natives[j].name))
565 ctx->natives[i].pfn = natives[j].func;
566 ctx->natives[i].status = SP_NATIVE_BOUND;
571 return SP_ERROR_NONE;
574 int BaseContext::BindNative(const sp_nativeinfo_t *native)
579 if ((err = FindNativeByName(native->name, &index)) != SP_ERROR_NONE)
584 ctx->natives[index].pfn = native->func;
585 ctx->natives[index].status = SP_NATIVE_BOUND;
587 return SP_ERROR_NONE;
590 int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native)
592 uint32_t nativesnum, i;
594 nativesnum = ctx->plugin->info.natives_num;
596 for (i=0; i<nativesnum; i++)
598 if (ctx->natives[i].status == SP_NATIVE_UNBOUND)
600 ctx->natives[i].pfn = native;
601 ctx->natives[i].status = SP_NATIVE_BOUND;
605 return SP_ERROR_NONE;
608 int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
610 if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
612 return SP_ERROR_INVALID_ADDRESS;
617 *phys_addr = (cell_t *)(ctx->memory + local_addr);
620 return SP_ERROR_NONE;
623 int BaseContext::PushCell(cell_t value)
625 if ((ctx->hp + STACKMARGIN) > (cell_t)(ctx->sp - sizeof(cell_t)))
627 return SP_ERROR_STACKLOW;
630 ctx->sp -= sizeof(cell_t);
631 *(cell_t *)(ctx->memory + ctx->sp) = value;
634 return SP_ERROR_NONE;
637 int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells)
642 for (i=0; i<numcells; i++)
644 if ((err = PushCell(array[i])) != SP_ERROR_NONE)
646 ctx->sp += (cell_t)(i * sizeof(cell_t));
652 return SP_ERROR_NONE;
655 int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells)
660 if ((err = HeapAlloc(numcells, local_addr, &ph_addr)) != SP_ERROR_NONE)
665 memcpy(ph_addr, array, numcells * sizeof(cell_t));
667 if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
669 HeapRelease(*local_addr);
675 *phys_addr = ph_addr;
678 return SP_ERROR_NONE;
681 int BaseContext::LocalToString(cell_t local_addr, char **addr)
683 if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
685 return SP_ERROR_INVALID_ADDRESS;
687 *addr = (char *)(ctx->memory + local_addr);
689 return SP_ERROR_NONE;
692 int BaseContext::PushString(cell_t *local_addr, char **phys_addr, const char *string)
696 unsigned int len, numcells = ((len=strlen(string)) + sizeof(cell_t)) / sizeof(cell_t);
698 if ((err = HeapAlloc(numcells, local_addr, (cell_t **)&ph_addr)) != SP_ERROR_NONE)
703 memcpy(ph_addr, string, len);
706 if ((err = PushCell(*local_addr)) != SP_ERROR_NONE)
708 HeapRelease(*local_addr);
714 *phys_addr = ph_addr;
717 return SP_ERROR_NONE;
720 int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source)
725 if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
727 return SP_ERROR_INVALID_ADDRESS;
732 return SP_ERROR_NONE;
735 len = strlen(source);
736 dest = (char *)(ctx->memory + local_addr);
743 memcpy(dest, source, len);
746 return SP_ERROR_NONE;
749 inline int __CheckValidChar(char *c)
754 for (count=1; (*c & 0xC0) == 0x80; count++)
779 if (bytecount != count)
787 int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes)
791 bool needtocheck = false;
793 if (((local_addr >= ctx->hp) && (local_addr < ctx->sp)) || (local_addr < 0) || ((ucell_t)local_addr >= ctx->mem_size))
795 return SP_ERROR_INVALID_ADDRESS;
800 return SP_ERROR_NONE;
803 len = strlen(source);
804 dest = (char *)(ctx->memory + local_addr);
806 if ((size_t)len >= maxbytes)
812 memcpy(dest, source, len);
813 if ((dest[len-1] & 1<<7) && needtocheck)
815 len -= __CheckValidChar(dest+len-1);
824 return SP_ERROR_NONE;
827 #define USHR(x) ((unsigned int)(x)>>1)
829 int BaseContext::LookupFile(ucell_t addr, const char **filename)
833 high = ctx->plugin->debug.files_num;
836 while (high - low > 1)
838 mid = USHR(low + high);
839 if (ctx->files[mid].addr <= addr)
849 return SP_ERROR_NOT_FOUND;
852 *filename = ctx->files[low].name;
854 return SP_ERROR_NONE;
857 int BaseContext::LookupFunction(ucell_t addr, const char **name)
859 uint32_t iter, max = ctx->plugin->debug.syms_num;
861 for (iter=0; iter<max; iter++)
863 if ((ctx->symbols[iter].sym->ident == SP_SYM_FUNCTION)
864 && (ctx->symbols[iter].codestart <= addr)
865 && (ctx->symbols[iter].codeend > addr))
873 return SP_ERROR_NOT_FOUND;
876 *name = ctx->symbols[iter].name;
878 return SP_ERROR_NONE;
881 int BaseContext::LookupLine(ucell_t addr, uint32_t *line)
885 high = ctx->plugin->debug.lines_num;
888 while (high - low > 1)
890 mid = USHR(low + high);
891 if (ctx->lines[mid].addr <= addr)
901 return SP_ERROR_NOT_FOUND;
904 /* Since the CIP occurs BEFORE the line, we have to add one */
905 *line = ctx->lines[low].line + 1;
907 return SP_ERROR_NONE;
910 IPluginFunction *BaseContext::GetFunctionById(funcid_t func_id)
912 CFunction *pFunc = NULL;
917 if (func_id >= ctx->plugin->info.publics_num)
921 pFunc = m_pub_funcs[func_id];
924 m_pub_funcs[func_id] = new CFunction(ctx->publics[func_id].code_offs, this);
925 pFunc = m_pub_funcs[func_id];
926 } else if (pFunc->IsInvalidated()) {
927 pFunc->Set(ctx->publics[func_id].code_offs, this);
930 /* :TODO: currently not used */
934 if (!g_pVM->FunctionLookup(ctx, func_id, &index))
938 pFunc = m_priv_funcs[func_id];
941 m_priv_funcs[func_id] = new CFunction(save, this);
942 pFunc = m_priv_funcs[func_id];
951 IPluginFunction *BaseContext::GetFunctionByName(const char *public_name)
955 if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
960 CFunction *pFunc = m_pub_funcs[index];
963 sp_public_t *pub = NULL;
964 GetPublicByIndex(index, &pub);
967 m_pub_funcs[index] = new CFunction(pub->code_offs, this);
969 pFunc = m_pub_funcs[index];
970 } else if (pFunc->IsInvalidated()) {
971 sp_public_t *pub = NULL;
972 GetPublicByIndex(index, &pub);
975 pFunc->Set(pub->code_offs, this);
984 #if defined SOURCEMOD_BUILD
985 SourceMod::IdentityToken_t *BaseContext::GetIdentity()
990 void BaseContext::SetIdentity(SourceMod::IdentityToken_t *token)