sourcepawn/jit/sp_vm_engine.cpp
author GoD-Tony <aangione@gmail.com>
Sun Aug 04 10:03:40 2013 -0400 (2013-08-04)
changeset 3971 d2e8100b2814
parent 2473 2df0a0591913
child 3977 fca2b54741ca
permissions -rw-r--r--
Added ISDKHooks interface with entity listeners (bug 5602, r=psychonic).
dvander@2193
     1
/**
dvander@2193
     2
 * vim: set ts=4 :
dvander@2193
     3
 * =============================================================================
dvander@2193
     4
 * SourcePawn
dvander@2193
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
dvander@2193
     6
 * =============================================================================
dvander@2193
     7
 *
dvander@2193
     8
 * This program is free software; you can redistribute it and/or modify it under
dvander@2193
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
dvander@2193
    10
 * Free Software Foundation.
dvander@2193
    11
 * 
dvander@2193
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
dvander@2193
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
dvander@2193
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
dvander@2193
    15
 * details.
dvander@2193
    16
 *
dvander@2193
    17
 * You should have received a copy of the GNU General Public License along with
dvander@2193
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@2193
    19
 *
dvander@2193
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
dvander@2193
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
dvander@2193
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
dvander@2193
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
dvander@2193
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
dvander@2193
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
dvander@2193
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
dvander@2193
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@2193
    28
 *
dvander@2193
    29
 * Version: $Id$
dvander@2193
    30
 */
dvander@2193
    31
dvander@2471
    32
#include <stdlib.h>
dvander@2193
    33
#include <string.h>
dvander@2193
    34
#include <assert.h>
dvander@2228
    35
#include "sp_vm_types.h"
dvander@2226
    36
#include <KeCodeAllocator.h>
dvander@2193
    37
#include "sp_file_headers.h"
dvander@2193
    38
#include "sp_vm_engine.h"
dvander@2193
    39
#include "zlib/zlib.h"
dvander@2193
    40
#include "sp_vm_basecontext.h"
dvander@2226
    41
#include "jit_x86.h"
dvander@2226
    42
#if defined __GNUC__
dvander@2226
    43
#include <unistd.h>
dvander@2226
    44
#endif
dvander@2193
    45
dvander@2193
    46
SourcePawnEngine g_engine1;
dvander@2193
    47
dvander@2193
    48
#if defined WIN32
dvander@2193
    49
 #define WIN32_LEAN_AND_MEAN
dvander@2193
    50
 #include <windows.h>
dvander@2193
    51
#elif defined __GNUC__
dvander@2193
    52
 #include <sys/mman.h>
dvander@2193
    53
#endif
dvander@2193
    54
dvander@2473
    55
#if defined __linux__
dvander@2472
    56
#include <malloc.h>
dvander@2472
    57
#endif
dvander@2472
    58
dvander@2193
    59
using namespace SourcePawn;
dvander@2193
    60
dvander@2193
    61
#define ERROR_MESSAGE_MAX		25
dvander@2193
    62
static const char *g_ErrorMsgTable[] = 
dvander@2193
    63
{
dvander@2193
    64
	NULL,
dvander@2193
    65
	"Unrecognizable file format",
dvander@2193
    66
	"Decompressor was not found",
dvander@2193
    67
	"Not enough space on the heap",
dvander@2193
    68
	"Invalid parameter or parameter type",
dvander@2193
    69
	"Invalid plugin address",
dvander@2193
    70
	"Object or index not found",
dvander@2193
    71
	"Invalid index or index not found",
dvander@2193
    72
	"Not enough space on the stack",
dvander@2193
    73
	"Debug section not found or debug not enabled",
dvander@2193
    74
	"Invalid instruction",
dvander@2193
    75
	"Invalid memory access",
dvander@2193
    76
	"Stack went below stack boundary",
dvander@2193
    77
	"Heap went below heap boundary",
dvander@2193
    78
	"Divide by zero",
dvander@2193
    79
	"Array index is out of bounds",
dvander@2193
    80
	"Instruction contained invalid parameter",
dvander@2193
    81
	"Stack memory leaked by native",
dvander@2193
    82
	"Heap memory leaked by native",
dvander@2193
    83
	"Dynamic array is too big",
dvander@2193
    84
	"Tracker stack is out of bounds",
dvander@2193
    85
	"Native is not bound",
dvander@2193
    86
	"Maximum number of parameters reached",
dvander@2193
    87
	"Native detected error",
dvander@2193
    88
	"Plugin not runnable",
dvander@2193
    89
	"Call was aborted",
dvander@2226
    90
	"Plugin format is too old",
dvander@2226
    91
	"Plugin format is too new",
dvander@2193
    92
};
dvander@2193
    93
dvander@2193
    94
const char *SourcePawnEngine::GetErrorString(int error)
dvander@2193
    95
{
dvander@2193
    96
	if (error < 1 || error > ERROR_MESSAGE_MAX)
dvander@2193
    97
	{
dvander@2193
    98
		return NULL;
dvander@2193
    99
	}
dvander@2193
   100
dvander@2193
   101
	return g_ErrorMsgTable[error];
dvander@2193
   102
}
dvander@2193
   103
dvander@2193
   104
SourcePawnEngine::SourcePawnEngine()
dvander@2193
   105
{
dvander@2193
   106
	m_pDebugHook = NULL;
dvander@2193
   107
}
dvander@2193
   108
dvander@2193
   109
SourcePawnEngine::~SourcePawnEngine()
dvander@2193
   110
{
dvander@2193
   111
}
dvander@2193
   112
dvander@2193
   113
void *SourcePawnEngine::ExecAlloc(size_t size)
dvander@2193
   114
{
dvander@2193
   115
#if defined WIN32
dvander@2193
   116
	return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
dvander@2193
   117
#elif defined __GNUC__
dvander@2471
   118
# if defined __APPLE__
dvander@2471
   119
	void *base = valloc(size);
dvander@2471
   120
# else
dvander@2193
   121
	void *base = memalign(sysconf(_SC_PAGESIZE), size);
dvander@2471
   122
# endif
dvander@2193
   123
	if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
dvander@2193
   124
	{
dvander@2193
   125
		free(base);
dvander@2193
   126
		return NULL;
dvander@2193
   127
	}
dvander@2193
   128
	return base;
dvander@2193
   129
#endif
dvander@2193
   130
}
dvander@2193
   131
dvander@2193
   132
void *SourcePawnEngine::AllocatePageMemory(size_t size)
dvander@2193
   133
{
dvander@2226
   134
	return g_Jit.AllocCode(size);
dvander@2193
   135
}
dvander@2193
   136
dvander@2193
   137
void SourcePawnEngine::SetReadExecute(void *ptr)
dvander@2193
   138
{
dvander@2226
   139
	/* already re */
dvander@2193
   140
}
dvander@2193
   141
dvander@2193
   142
void SourcePawnEngine::SetReadWrite(void *ptr)
dvander@2193
   143
{
dvander@2226
   144
	/* already rw */
dvander@2193
   145
}
dvander@2193
   146
dvander@2193
   147
void SourcePawnEngine::FreePageMemory(void *ptr)
dvander@2193
   148
{
dvander@2226
   149
	g_Jit.FreeCode(ptr);
dvander@2193
   150
}
dvander@2193
   151
dvander@2193
   152
void SourcePawnEngine::ExecFree(void *address)
dvander@2193
   153
{
dvander@2193
   154
#if defined WIN32
dvander@2193
   155
	VirtualFree(address, 0, MEM_RELEASE);
dvander@2193
   156
#elif defined __GNUC__
dvander@2193
   157
	free(address);
dvander@2193
   158
#endif
dvander@2193
   159
}
dvander@2193
   160
dvander@2226
   161
void SourcePawnEngine::SetReadWriteExecute(void *ptr)
dvander@2226
   162
{
dvander@2226
   163
//:TODO:	g_ExeMemory.SetRWE(ptr);
dvander@2226
   164
	SetReadExecute(ptr);
dvander@2226
   165
}
dvander@2226
   166
dvander@2193
   167
void *SourcePawnEngine::BaseAlloc(size_t size)
dvander@2193
   168
{
dvander@2193
   169
	return malloc(size);
dvander@2193
   170
}
dvander@2193
   171
dvander@2193
   172
void SourcePawnEngine::BaseFree(void *memory)
dvander@2193
   173
{
dvander@2193
   174
	free(memory);
dvander@2193
   175
}
dvander@2193
   176
dvander@2193
   177
sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
dvander@2193
   178
{
dvander@2193
   179
	if (err != NULL)
dvander@2193
   180
	{
dvander@2193
   181
		*err = SP_ERROR_ABORTED;
dvander@2193
   182
	}
dvander@2193
   183
dvander@2193
   184
	return NULL;
dvander@2193
   185
}
dvander@2193
   186
dvander@2193
   187
sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
dvander@2193
   188
{
dvander@2193
   189
	if (err != NULL)
dvander@2193
   190
	{
dvander@2193
   191
		*err = SP_ERROR_ABORTED;
dvander@2193
   192
	}
dvander@2193
   193
dvander@2193
   194
	return NULL;
dvander@2193
   195
}
dvander@2193
   196
dvander@2193
   197
int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
dvander@2193
   198
{
dvander@2193
   199
	return SP_ERROR_ABORTED;
dvander@2193
   200
}
dvander@2193
   201
dvander@2193
   202
IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
dvander@2193
   203
{
dvander@2193
   204
	IDebugListener *old = m_pDebugHook;
dvander@2193
   205
dvander@2193
   206
	m_pDebugHook = pListener;
dvander@2193
   207
dvander@2193
   208
	return old;
dvander@2193
   209
}
dvander@2193
   210
dvander@2193
   211
unsigned int SourcePawnEngine::GetEngineAPIVersion()
dvander@2193
   212
{
dvander@2193
   213
	return SOURCEPAWN_ENGINE_API_VERSION;
dvander@2193
   214
}
dvander@2193
   215
dvander@2226
   216
unsigned int SourcePawnEngine::GetContextCallCount()
dvander@2226
   217
{
dvander@2226
   218
	return 0;
dvander@2226
   219
}
dvander@2226
   220
dvander@2226
   221
void SourcePawnEngine::ReportError(BaseRuntime *runtime, int err, const char *errstr, cell_t rp_start)
dvander@2193
   222
{
dvander@2226
   223
	if (m_pDebugHook == NULL)
dvander@2226
   224
	{
dvander@2226
   225
		return;
dvander@2226
   226
	}
dvander@2226
   227
dvander@2226
   228
	CContextTrace trace(runtime, err, errstr, rp_start);
dvander@2226
   229
dvander@2226
   230
	m_pDebugHook->OnContextExecuteError(runtime->GetDefaultContext(), &trace);
dvander@2226
   231
}
dvander@2226
   232
dvander@2226
   233
CContextTrace::CContextTrace(BaseRuntime *pRuntime, int err, const char *errstr, cell_t start_rp) 
dvander@2226
   234
: m_pRuntime(pRuntime), m_Error(err), m_pMsg(errstr), m_StartRp(start_rp), m_Level(0)
dvander@2226
   235
{
dvander@2226
   236
	m_ctx = pRuntime->m_pCtx->GetCtx();
dvander@2226
   237
	m_pDebug = m_pRuntime->GetDebugInfo();
dvander@2193
   238
}
dvander@2193
   239
dvander@2193
   240
bool CContextTrace::DebugInfoAvailable()
dvander@2193
   241
{
dvander@2226
   242
	return (m_pDebug != NULL);
dvander@2193
   243
}
dvander@2193
   244
dvander@2193
   245
const char *CContextTrace::GetCustomErrorString()
dvander@2193
   246
{
dvander@2193
   247
	return m_pMsg;
dvander@2193
   248
}
dvander@2193
   249
dvander@2193
   250
int CContextTrace::GetErrorCode()
dvander@2193
   251
{
dvander@2193
   252
	return m_Error;
dvander@2193
   253
}
dvander@2193
   254
dvander@2193
   255
const char *CContextTrace::GetErrorString()
dvander@2193
   256
{
dvander@2226
   257
	if (m_Error > ERROR_MESSAGE_MAX || m_Error < 1)
dvander@2193
   258
	{
dvander@2193
   259
		return "Invalid error code";
dvander@2226
   260
	}
dvander@2226
   261
	else
dvander@2226
   262
	{
dvander@2193
   263
		return g_ErrorMsgTable[m_Error];
dvander@2193
   264
	}
dvander@2193
   265
}
dvander@2193
   266
dvander@2193
   267
void CContextTrace::ResetTrace()
dvander@2193
   268
{
dvander@2226
   269
	m_Level = 0;
dvander@2193
   270
}
dvander@2193
   271
dvander@2193
   272
bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
dvander@2193
   273
{
dvander@2226
   274
	cell_t cip;
dvander@2226
   275
dvander@2226
   276
	if (m_Level == 0)
dvander@2193
   277
	{
dvander@2226
   278
		cip = m_ctx->err_cip;
dvander@2193
   279
	}
dvander@2226
   280
	else if (m_ctx->rp > 0)
dvander@2193
   281
	{
dvander@2226
   282
		/* Entries go from ctx.rp - 1 to m_StartRp */
dvander@2226
   283
		cell_t offs, start, end;
dvander@2226
   284
dvander@2226
   285
		offs = m_Level - 1;
dvander@2226
   286
		start = m_ctx->rp - 1;
dvander@2226
   287
		end = m_StartRp;
dvander@2193
   288
dvander@2226
   289
		if (start - offs < end)
dvander@2226
   290
		{
dvander@2226
   291
			return false;
dvander@2226
   292
		}
dvander@2193
   293
dvander@2226
   294
		cip = m_ctx->rstk_cips[start - offs];
dvander@2226
   295
	}
dvander@2226
   296
	else
dvander@2193
   297
	{
dvander@2193
   298
		return false;
dvander@2193
   299
	}
dvander@2193
   300
dvander@2226
   301
	if (trace == NULL)
dvander@2193
   302
	{
dvander@2226
   303
		m_Level++;
dvander@2193
   304
		return true;
dvander@2193
   305
	}
dvander@2193
   306
dvander@2226
   307
	if (m_pDebug->LookupFile(cip, &(trace->filename)) != SP_ERROR_NONE)
dvander@2193
   308
	{
dvander@2193
   309
		trace->filename = NULL;
dvander@2193
   310
	}
dvander@2193
   311
dvander@2226
   312
	if (m_pDebug->LookupFunction(cip, &(trace->function)) != SP_ERROR_NONE)
dvander@2193
   313
	{
dvander@2193
   314
		trace->function = NULL;
dvander@2193
   315
	}
dvander@2193
   316
dvander@2226
   317
	if (m_pDebug->LookupLine(cip, &(trace->line)) != SP_ERROR_NONE)
dvander@2193
   318
	{
dvander@2193
   319
		trace->line = 0;
dvander@2193
   320
	}
dvander@2193
   321
dvander@2226
   322
	m_Level++;
dvander@2193
   323
dvander@2193
   324
	return true;
dvander@2193
   325
}
dvander@2193
   326
dvander@2193
   327
const char *CContextTrace::GetLastNative(uint32_t *index)
dvander@2193
   328
{
dvander@2226
   329
	if (m_ctx->n_err == SP_ERROR_NONE)
dvander@2193
   330
	{
dvander@2193
   331
		return NULL;
dvander@2193
   332
	}
dvander@2193
   333
dvander@2193
   334
	sp_native_t *native;
dvander@2226
   335
	if (m_pRuntime->GetNativeByIndex(m_ctx->n_idx, &native) != SP_ERROR_NONE)
dvander@2193
   336
	{
dvander@2193
   337
		return NULL;
dvander@2193
   338
	}
dvander@2193
   339
dvander@2193
   340
	if (index)
dvander@2193
   341
	{
dvander@2226
   342
		*index = m_ctx->n_idx;
dvander@2193
   343
	}
dvander@2193
   344
dvander@2193
   345
	return native->name;
dvander@2193
   346
}
dvander@2226
   347
dvander@2226
   348
IDebugListener *SourcePawnEngine::GetDebugHook()
dvander@2226
   349
{
dvander@2226
   350
	return m_pDebugHook;
dvander@2226
   351
}
dvander@2226
   352