sourcepawn/jit/sp_vm_basecontext.cpp
author David Anderson <dvander@alliedmods.net>
Sun Oct 26 23:50:54 2008 -0500 (2008-10-26)
changeset 2377 6ee3bbd6fbdf
parent 2226 d10b38524be3
child 2378 d812052c5821
permissions -rw-r--r--
Fixed leaking relocation table memory in JIT.
     1 /**
     2  * vim: set ts=4 :
     3  * =============================================================================
     4  * SourcePawn
     5  * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
     6  * =============================================================================
     7  *
     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.
    11  * 
    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
    15  * details.
    16  *
    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/>.
    19  *
    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>.
    28  *
    29  * Version: $Id$
    30  */
    31 
    32 #include <string.h>
    33 #include <stdarg.h>
    34 #include <assert.h>
    35 #include <limits.h>
    36 #include "sp_vm_api.h"
    37 #include "sp_vm_basecontext.h"
    38 #include "sp_vm_engine.h"
    39 #include "x86/jit_x86.h"
    40 
    41 using namespace SourcePawn;
    42 
    43 #define CELLBOUNDMAX	(INT_MAX/sizeof(cell_t))
    44 #define STACKMARGIN		((cell_t)(16*sizeof(cell_t)))
    45 
    46 BaseContext::BaseContext(BaseRuntime *pRuntime)
    47 {
    48 	m_pRuntime = pRuntime;
    49 	m_pPlugin = m_pRuntime->m_pPlugin;
    50 
    51 	m_InExec = false;
    52 	m_CustomMsg = false;
    53 
    54 	/* Initialize the null references */
    55 	uint32_t index;
    56 	if (FindPubvarByName("NULL_VECTOR", &index) == SP_ERROR_NONE)
    57 	{
    58 		sp_pubvar_t *pubvar;
    59 		GetPubvarByIndex(index, &pubvar);
    60 		m_pNullVec = pubvar->offs;
    61 	}
    62 	else
    63 	{
    64 		m_pNullVec = NULL;
    65 	}
    66 
    67 	if (FindPubvarByName("NULL_STRING", &index) == SP_ERROR_NONE)
    68 	{
    69 		sp_pubvar_t *pubvar;
    70 		GetPubvarByIndex(index, &pubvar);
    71 		m_pNullString = pubvar->offs;
    72 	}
    73 	else
    74 	{
    75 		m_pNullString = NULL;
    76 	}
    77 
    78 	m_ctx.hp = m_pPlugin->data_size;
    79 	m_ctx.sp = m_pPlugin->mem_size - sizeof(cell_t);
    80 	m_ctx.frm = m_ctx.sp;
    81 	m_ctx.n_err = SP_ERROR_NONE;
    82 	m_ctx.n_idx = SP_ERROR_NONE;
    83 	m_ctx.rp = 0;
    84 
    85 	g_Jit.SetupContextVars(m_pRuntime, this, &m_ctx);
    86 }
    87 
    88 BaseContext::~BaseContext()
    89 {
    90 	g_Jit.FreeContextVars(&m_ctx);
    91 }
    92 
    93 IVirtualMachine *BaseContext::GetVirtualMachine()
    94 {
    95 	return NULL;
    96 }
    97 
    98 sp_context_t *BaseContext::GetContext()
    99 {
   100 	return reinterpret_cast<sp_context_t *>((IPluginContext * )this);
   101 }
   102 
   103 sp_context_t *BaseContext::GetCtx()
   104 {
   105 	return &m_ctx;
   106 }
   107 
   108 bool BaseContext::IsDebugging()
   109 {
   110 	return true;
   111 }
   112 
   113 int BaseContext::SetDebugBreak(void *newpfn, void *oldpfn)
   114 {
   115 	return SP_ERROR_ABORTED;
   116 }
   117 
   118 IPluginDebugInfo *BaseContext::GetDebugInfo()
   119 {
   120 	return NULL;
   121 }
   122 
   123 int BaseContext::Execute(uint32_t code_addr, cell_t *result)
   124 {
   125 	return SP_ERROR_ABORTED;
   126 }
   127 
   128 void BaseContext::SetErrorMessage(const char *msg, va_list ap)
   129 {
   130 	m_CustomMsg = true;
   131 
   132 	vsnprintf(m_MsgCache, sizeof(m_MsgCache), msg, ap);
   133 }
   134 
   135 void BaseContext::_SetErrorMessage(const char *msg, ...)
   136 {
   137 	va_list ap;
   138 	va_start(ap, msg);
   139 	SetErrorMessage(msg, ap);
   140 	va_end(ap);
   141 }
   142 
   143 cell_t BaseContext::ThrowNativeErrorEx(int error, const char *msg, ...)
   144 {
   145 	if (!m_InExec)
   146 	{
   147 		return 0;
   148 	}
   149 
   150 	m_ctx.n_err = error;
   151 	
   152 	if (msg)
   153 	{
   154 		va_list ap;
   155 		va_start(ap, msg);
   156 		SetErrorMessage(msg, ap);
   157 		va_end(ap);
   158 	}
   159 
   160 	return 0;
   161 }
   162 
   163 cell_t BaseContext::ThrowNativeError(const char *msg, ...)
   164 {
   165 	if (!m_InExec)
   166 	{
   167 		return 0;
   168 	}
   169 
   170 	m_ctx.n_err = SP_ERROR_NATIVE;
   171 
   172 	if (msg)
   173 	{
   174 		va_list ap;
   175 		va_start(ap, msg);
   176 		SetErrorMessage(msg, ap);
   177 		va_end(ap);
   178 	}
   179 
   180 	return 0;
   181 }
   182 
   183 int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_addr)
   184 {
   185 	cell_t *addr;
   186 	ucell_t realmem;
   187 
   188 #if 0
   189 	if (cells > CELLBOUNDMAX)
   190 	{
   191 		return SP_ERROR_ARAM;
   192 	}
   193 #else
   194 	assert(cells < CELLBOUNDMAX);
   195 #endif
   196 
   197 	realmem = cells * sizeof(cell_t);
   198 
   199 	/**
   200 	 * Check if the space between the heap and stack is sufficient.
   201 	 */
   202 	if ((cell_t)(m_ctx.sp - m_ctx.hp - realmem) < STACKMARGIN)
   203 	{
   204 		return SP_ERROR_HEAPLOW;
   205 	}
   206 
   207 	addr = (cell_t *)(m_pPlugin->memory + m_ctx.hp);
   208 	/* store size of allocation in cells */
   209 	*addr = (cell_t)cells;
   210 	addr++;
   211 	m_ctx.hp += sizeof(cell_t);
   212 
   213 	*local_addr = m_ctx.hp;
   214 
   215 	if (phys_addr)
   216 	{
   217 		*phys_addr = addr;
   218 	}
   219 
   220 	m_ctx.hp += realmem;
   221 
   222 	return SP_ERROR_NONE;
   223 }
   224 
   225 int BaseContext::HeapPop(cell_t local_addr)
   226 {
   227 	cell_t cellcount;
   228 	cell_t *addr;
   229 
   230 	/* check the bounds of this address */
   231 	local_addr -= sizeof(cell_t);
   232 	if (local_addr < (cell_t)m_pPlugin->data_size || local_addr >= m_ctx.sp)
   233 	{
   234 		return SP_ERROR_INVALID_ADDRESS;
   235 	}
   236 
   237 	addr = (cell_t *)(m_pPlugin->memory + local_addr);
   238 	cellcount = (*addr) * sizeof(cell_t);
   239 	/* check if this memory count looks valid */
   240 	if ((signed)(m_ctx.hp - cellcount - sizeof(cell_t)) != local_addr)
   241 	{
   242 		return SP_ERROR_INVALID_ADDRESS;
   243 	}
   244 
   245 	m_ctx.hp = local_addr;
   246 
   247 	return SP_ERROR_NONE;
   248 }
   249 
   250 
   251 int BaseContext::HeapRelease(cell_t local_addr)
   252 {
   253 	if (local_addr < (cell_t)m_pPlugin->data_size)
   254 	{
   255 		return SP_ERROR_INVALID_ADDRESS;
   256 	}
   257 
   258 	m_ctx.hp = local_addr - sizeof(cell_t);
   259 
   260 	return SP_ERROR_NONE;
   261 }
   262 
   263 int BaseContext::FindNativeByName(const char *name, uint32_t *index)
   264 {
   265 	return m_pRuntime->FindNativeByName(name, index);
   266 }
   267 
   268 int BaseContext::GetNativeByIndex(uint32_t index, sp_native_t **native)
   269 {
   270 	return m_pRuntime->GetNativeByIndex(index, native);
   271 }
   272 
   273 
   274 uint32_t BaseContext::GetNativesNum()
   275 {
   276 	return m_pRuntime->GetNativesNum();
   277 }
   278 
   279 int BaseContext::FindPublicByName(const char *name, uint32_t *index)
   280 {
   281 	return m_pRuntime->FindPublicByName(name, index);
   282 }
   283 
   284 int BaseContext::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
   285 {
   286 	return m_pRuntime->GetPublicByIndex(index, pblic);
   287 }
   288 
   289 uint32_t BaseContext::GetPublicsNum()
   290 {
   291 	return m_pRuntime->GetPublicsNum();
   292 }
   293 
   294 int BaseContext::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
   295 {
   296 	return m_pRuntime->GetPubvarByIndex(index, pubvar);
   297 }
   298 
   299 int BaseContext::FindPubvarByName(const char *name, uint32_t *index)
   300 {
   301 	return m_pRuntime->FindPubvarByName(name, index);
   302 }
   303 
   304 int BaseContext::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
   305 {
   306 	return m_pRuntime->GetPubvarAddrs(index, local_addr, phys_addr);
   307 }
   308 
   309 uint32_t BaseContext::GetPubVarsNum()
   310 {
   311 	return m_pRuntime->GetPubVarsNum();
   312 }
   313 
   314 int BaseContext::BindNatives(const sp_nativeinfo_t *natives, unsigned int num, int overwrite)
   315 {
   316 	return SP_ERROR_ABORTED;
   317 }
   318 
   319 int BaseContext::BindNative(const sp_nativeinfo_t *native)
   320 {
   321 	return SP_ERROR_ABORTED;
   322 }
   323 
   324 int BaseContext::BindNativeToIndex(uint32_t index, SPVM_NATIVE_FUNC func)
   325 {
   326 	return SP_ERROR_ABORTED;
   327 }
   328 
   329 int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native)
   330 {
   331 	return SP_ERROR_ABORTED;
   332 }
   333 
   334 int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
   335 {
   336 	if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) 
   337 		|| (local_addr < 0) || ((ucell_t)local_addr >= m_pPlugin->mem_size))
   338 	{
   339 		return SP_ERROR_INVALID_ADDRESS;
   340 	}
   341 
   342 	if (phys_addr)
   343 	{
   344 		*phys_addr = (cell_t *)(m_pPlugin->memory + local_addr);
   345 	}
   346 
   347 	return SP_ERROR_NONE;
   348 }
   349 
   350 int BaseContext::PushCell(cell_t value)
   351 {
   352 	return SP_ERROR_ABORTED;
   353 }
   354 
   355 int BaseContext::PushCellsFromArray(cell_t array[], unsigned int numcells)
   356 {
   357 	return SP_ERROR_ABORTED;
   358 }
   359 
   360 int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t array[], unsigned int numcells)
   361 {
   362 	return SP_ERROR_ABORTED;
   363 }
   364 
   365 int BaseContext::LocalToString(cell_t local_addr, char **addr)
   366 {
   367 	if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp))
   368 		|| (local_addr < 0) || ((ucell_t)local_addr >= m_pPlugin->mem_size))
   369 	{
   370 		return SP_ERROR_INVALID_ADDRESS;
   371 	}
   372 	*addr = (char *)(m_pPlugin->memory + local_addr);
   373 
   374 	return SP_ERROR_NONE;
   375 }
   376 
   377 int BaseContext::PushString(cell_t *local_addr, char **phys_addr, const char *string)
   378 {
   379 	return SP_ERROR_ABORTED;
   380 }
   381 
   382 int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source)
   383 {
   384 	char *dest;
   385 	size_t len;
   386 
   387 	if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp))
   388 		|| (local_addr < 0) || ((ucell_t)local_addr >= m_pPlugin->mem_size))
   389 	{
   390 		return SP_ERROR_INVALID_ADDRESS;
   391 	}
   392 
   393 	if (bytes == 0)
   394 	{
   395 		return SP_ERROR_NONE;
   396 	}
   397 
   398 	len = strlen(source);
   399 	dest = (char *)(m_pPlugin->memory + local_addr);
   400 
   401 	if (len >= bytes)
   402 	{
   403 		len = bytes - 1;
   404 	}
   405 
   406 	memmove(dest, source, len);
   407 	dest[len] = '\0';
   408 
   409 	return SP_ERROR_NONE;
   410 }
   411 
   412 inline int __CheckValidChar(char *c)
   413 {
   414 	int count;
   415 	int bytecount = 0;
   416 
   417 	for (count=1; (*c & 0xC0) == 0x80; count++)
   418 	{
   419 		c--;
   420 	}
   421 
   422 	switch (*c & 0xF0)
   423 	{
   424 	case 0xC0:
   425 	case 0xD0:
   426 		{
   427 			bytecount = 2;
   428 			break;
   429 		}
   430 	case 0xE0:
   431 		{
   432 			bytecount = 3;
   433 			break;
   434 		}
   435 	case 0xF0:
   436 		{
   437 			bytecount = 4;
   438 			break;
   439 		}
   440 	}
   441 
   442 	if (bytecount != count)
   443 	{
   444 		return count;
   445 	}
   446 
   447 	return 0;
   448 }
   449 
   450 int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char *source, size_t *wrtnbytes)
   451 {
   452 	char *dest;
   453 	size_t len;
   454 	bool needtocheck = false;
   455 
   456 	if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp))
   457 		|| (local_addr < 0)
   458 		|| ((ucell_t)local_addr >= m_pPlugin->mem_size))
   459 	{
   460 		return SP_ERROR_INVALID_ADDRESS;
   461 	}
   462 	
   463 	if (maxbytes == 0)
   464 	{
   465 		return SP_ERROR_NONE;
   466 	}
   467 
   468 	len = strlen(source);
   469 	dest = (char *)(m_pPlugin->memory + local_addr);
   470 
   471 	if ((size_t)len >= maxbytes)
   472 	{
   473 		len = maxbytes - 1;
   474 		needtocheck = true;
   475 	}
   476 
   477 	memmove(dest, source, len);
   478 	if ((dest[len-1] & 1<<7) && needtocheck)
   479 	{
   480 		len -= __CheckValidChar(dest+len-1);
   481 	}
   482 	dest[len] = '\0';
   483 
   484 	if (wrtnbytes)
   485 	{
   486 		*wrtnbytes = len;
   487 	}
   488 
   489 	return SP_ERROR_NONE;
   490 }
   491 
   492 IPluginFunction *BaseContext::GetFunctionById(funcid_t func_id)
   493 {
   494 	return m_pRuntime->GetFunctionById(func_id);
   495 }
   496 
   497 IPluginFunction *BaseContext::GetFunctionByName(const char *public_name)
   498 {
   499 	return m_pRuntime->GetFunctionByName(public_name);
   500 }
   501 
   502 int BaseContext::LocalToStringNULL(cell_t local_addr, char **addr)
   503 {
   504 	int err;
   505 	if ((err = LocalToString(local_addr, addr)) != SP_ERROR_NONE)
   506 	{
   507 		return err;
   508 	}
   509 
   510 	if ((cell_t *)*addr == m_pNullString)
   511 	{
   512 		*addr = NULL;
   513 	}
   514 
   515 	return SP_ERROR_NONE;
   516 }
   517 
   518 SourceMod::IdentityToken_t *BaseContext::GetIdentity()
   519 {
   520 	SourceMod::IdentityToken_t *tok;
   521 
   522 	if (GetKey(1, (void **)&tok))
   523 	{
   524 		return tok;
   525 	}
   526 
   527 	return NULL;
   528 }
   529 
   530 cell_t *BaseContext::GetNullRef(SP_NULL_TYPE type)
   531 {
   532 	if (type == SP_NULL_VECTOR)
   533 	{
   534 		return m_pNullVec;
   535 	}
   536 
   537 	return NULL;
   538 }
   539 
   540 bool BaseContext::IsInExec()
   541 {
   542 	return m_InExec;
   543 }
   544 
   545 int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsigned int num_params, cell_t *result)
   546 {
   547 	int ir;
   548 	int serial;
   549 	cell_t *sp;
   550 	funcid_t fnid;
   551 	JitFunction *fn;
   552 	sp_public_t *pubfunc;
   553 	cell_t _ignore_result;
   554 	unsigned int public_id;
   555 
   556 	fnid = function->GetFunctionID();
   557 
   558 	if (fnid & 1)
   559 	{	
   560 		public_id = fnid >> 1;
   561 
   562 		if (m_pRuntime->GetPublicByIndex(public_id, &pubfunc) != SP_ERROR_NONE)
   563 		{
   564 			return SP_ERROR_NOT_FOUND;
   565 		}
   566 	}
   567 	else
   568 	{
   569 		return SP_ERROR_INVALID_ADDRESS;
   570 	}
   571 
   572 	if (m_pRuntime->IsPaused())
   573 	{
   574 		return SP_ERROR_NOT_RUNNABLE;
   575 	}
   576 
   577 	if ((cell_t)(m_ctx.hp + 16*sizeof(cell_t)) > (cell_t)(m_ctx.sp - (sizeof(cell_t) * (num_params + 1))))
   578 	{
   579 		return SP_ERROR_STACKLOW;
   580 	}
   581 
   582 	if (result == NULL)
   583 	{
   584 		result = &_ignore_result;
   585 	}
   586 
   587 	/* We got this far.  It's time to start profiling. */
   588 	
   589 	if ((m_pPlugin->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS)
   590 	{
   591 		serial = m_pPlugin->profiler->OnCallbackBegin(this, pubfunc);
   592 	}
   593 
   594 	/* See if we have to compile the callee. */
   595 	if ((fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL)
   596 	{
   597 		uint32_t func_idx;
   598 
   599 		/* We might not have to - check pcode offset. */
   600 		if ((func_idx = FuncLookup((CompData *)m_pRuntime->m_pCo, pubfunc->code_offs)) != 0)
   601 		{
   602 			fn = m_pRuntime->GetJittedFunction(func_idx);
   603 			assert(fn != NULL);
   604 			m_pRuntime->m_PubJitFuncs[public_id] = fn;
   605 		}
   606 		else
   607 		{
   608 			if ((fn = g_Jit.CompileFunction(m_pRuntime, pubfunc->code_offs, &ir)) == NULL)
   609 			{
   610 				return ir;
   611 			}
   612 			m_pRuntime->m_PubJitFuncs[public_id] = fn;
   613 		}
   614 	}
   615 
   616 	/* Save our previous state. */
   617 
   618 	bool save_exec;
   619 	uint32_t save_n_idx;
   620 	cell_t save_sp, save_hp, save_rp, save_cip;
   621 
   622 	save_sp = m_ctx.sp;
   623 	save_hp = m_ctx.hp;
   624 	save_exec = m_InExec;
   625 	save_n_idx = m_ctx.n_idx;
   626 	save_rp = m_ctx.rp;
   627 	save_cip = m_ctx.err_cip;
   628 
   629 	/* Push parameters */
   630 
   631 	m_ctx.sp -= sizeof(cell_t) * (num_params + 1);
   632 	sp = (cell_t *)(m_pPlugin->memory + m_ctx.sp);
   633 
   634 	sp[0] = num_params;
   635 	for (unsigned int i = 0; i < num_params; i++)
   636 	{
   637 		sp[i + 1] = params[i];
   638 	}
   639 
   640 	/* Clear internal state */
   641 	m_ctx.n_err = SP_ERROR_NONE;
   642 	m_ctx.n_idx = 0;
   643 	m_MsgCache[0] = '\0';
   644 	m_CustomMsg = false;
   645 	m_InExec = true;
   646 
   647 	/* Start the frame tracer */
   648 
   649 	ir = g_Jit.InvokeFunction(m_pRuntime, fn, result);
   650 
   651 	/* Restore some states, stop the frame tracer */
   652 
   653 	m_InExec = save_exec;
   654 
   655 	if (ir == SP_ERROR_NONE)
   656 	{
   657 		m_ctx.n_err = SP_ERROR_NONE;
   658 		if (m_ctx.sp != save_sp)
   659 		{
   660 			ir = SP_ERROR_STACKLEAK;
   661 			_SetErrorMessage("Stack leak detected: sp:%d should be %d!", 
   662 				m_ctx.sp, 
   663 				save_sp);
   664 		}
   665 		if (m_ctx.hp != save_hp)
   666 		{
   667 			ir = SP_ERROR_HEAPLEAK;
   668 			_SetErrorMessage("Heap leak detected: hp:%d should be %d!", 
   669 				m_ctx.hp, 
   670 				save_hp);
   671 		}
   672 		if (m_ctx.rp != save_rp)
   673 		{
   674 			ir = SP_ERROR_STACKLEAK;
   675 			_SetErrorMessage("Return stack leak detected: rp:%d should be %d!",
   676 				m_ctx.rp,
   677 				save_rp);
   678 		}
   679 	}
   680 
   681 	if (ir != SP_ERROR_NONE)
   682 	{
   683 		g_engine1.ReportError(m_pRuntime, ir, m_MsgCache, save_rp);
   684 	}
   685 
   686 	m_ctx.sp = save_sp;
   687 	m_ctx.hp = save_hp;
   688 	m_ctx.rp = save_rp;
   689 	
   690 	if ((m_pPlugin->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS)
   691 	{
   692 		m_pPlugin->profiler->OnCallbackEnd(serial);
   693 	}
   694 
   695 	m_ctx.err_cip = save_cip;
   696 	m_ctx.n_idx = save_n_idx;
   697 	m_ctx.n_err = SP_ERROR_NONE;
   698 	m_MsgCache[0] = '\0';
   699 	m_CustomMsg = false;
   700 
   701 	return ir;
   702 }
   703 
   704 IPluginRuntime *BaseContext::GetRuntime()
   705 {
   706 	return m_pRuntime;
   707 }
   708 
   709 DebugInfo::DebugInfo(sp_plugin_t *plugin) : m_pPlugin(plugin)
   710 {
   711 }
   712 
   713 #define USHR(x) ((unsigned int)(x)>>1)
   714 
   715 int DebugInfo::LookupFile(ucell_t addr, const char **filename)
   716 {
   717 	int high, low, mid;
   718 
   719 	high = m_pPlugin->debug.files_num;
   720 	low = -1;
   721 
   722 	while (high - low > 1)
   723 	{
   724 		mid = USHR(low + high);
   725 		if (m_pPlugin->debug.files[mid].addr <= addr)
   726 		{
   727 			low = mid;
   728 		} else {
   729 			high = mid;
   730 		}
   731 	}
   732 
   733 	if (low == -1)
   734 	{
   735 		return SP_ERROR_NOT_FOUND;
   736 	}
   737 
   738 	*filename = m_pPlugin->debug.stringbase + m_pPlugin->debug.files[low].name;
   739 
   740 	return SP_ERROR_NONE;
   741 }
   742 
   743 int DebugInfo::LookupFunction(ucell_t addr, const char **name)
   744 {
   745 	uint32_t max, iter;
   746 	sp_fdbg_symbol_t *sym;
   747 	sp_fdbg_arraydim_t *arr;
   748 	uint8_t *cursor = (uint8_t *)(m_pPlugin->debug.symbols);
   749 
   750 	max = m_pPlugin->debug.syms_num;
   751 	for (iter = 0; iter < max; iter++)
   752 	{
   753 		sym = (sp_fdbg_symbol_t *)cursor;
   754 
   755 		if (sym->ident == SP_SYM_FUNCTION
   756 			&& sym->codestart <= addr 
   757 			&& sym->codeend > addr)
   758 		{
   759 			*name = m_pPlugin->debug.stringbase + sym->name;
   760 			return SP_ERROR_NONE;
   761 		}
   762 
   763 		if (sym->dimcount > 0)
   764 		{
   765 			cursor += sizeof(sp_fdbg_symbol_t);
   766 			arr = (sp_fdbg_arraydim_t *)cursor;
   767 			cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount;
   768 			continue;
   769 		}
   770 
   771 		cursor += sizeof(sp_fdbg_symbol_t);
   772 	}
   773 
   774 	return SP_ERROR_NOT_FOUND;
   775 
   776 }
   777 
   778 int DebugInfo::LookupLine(ucell_t addr, uint32_t *line)
   779 {
   780 	int high, low, mid;
   781 
   782 	high = m_pPlugin->debug.lines_num;
   783 	low = -1;
   784 
   785 	while (high - low > 1)
   786 	{
   787 		mid = USHR(low + high);
   788 		if (m_pPlugin->debug.lines[mid].addr <= addr)
   789 		{
   790 			low = mid;
   791 		} else {
   792 			high = mid;
   793 		}
   794 	}
   795 
   796 	if (low == -1)
   797 	{
   798 		return SP_ERROR_NOT_FOUND;
   799 	}
   800 
   801 	/* Since the CIP occurs BEFORE the line, we have to add one */
   802 	*line = m_pPlugin->debug.lines[low].line + 1;
   803 
   804 	return SP_ERROR_NONE;
   805 }
   806 
   807 #undef USHR
   808 
   809 int BaseContext::GetLastNativeError()
   810 {
   811 	return m_ctx.n_err;
   812 }
   813 
   814 cell_t *BaseContext::GetLocalParams()
   815 {
   816 	return (cell_t *)(m_pPlugin->memory + m_ctx.frm + (2 * sizeof(cell_t)));
   817 }
   818 
   819 void BaseContext::SetKey(int k, void *value)
   820 {
   821 	if (k < 1 || k > 4)
   822 	{
   823 		return;
   824 	}
   825 
   826 	m_keys[k - 1] = value;
   827 	m_keys_set[k - 1] = true;
   828 }
   829 
   830 bool BaseContext::GetKey(int k, void **value)
   831 {
   832 	if (k < 1 || k > 4 || m_keys_set[k - 1] == false)
   833 	{
   834 		return false;
   835 	}
   836 
   837 	*value = m_keys[k - 1];
   838 
   839 	return true;
   840 }