sourcepawn/jit/sp_vm_engine.cpp
author David Anderson <dvander@alliedmods.net>
Sun Oct 26 23:50:54 2008 -0500 (2008-10-26)
changeset 2377 6ee3bbd6fbdf
parent 2228 83a7d6b44e3c
child 2471 194be299b700
permissions -rw-r--r--
Fixed leaking relocation table memory in JIT.
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@2193
    32
#include <malloc.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@2193
    55
using namespace SourcePawn;
dvander@2193
    56
dvander@2193
    57
#define ERROR_MESSAGE_MAX		25
dvander@2193
    58
static const char *g_ErrorMsgTable[] = 
dvander@2193
    59
{
dvander@2193
    60
	NULL,
dvander@2193
    61
	"Unrecognizable file format",
dvander@2193
    62
	"Decompressor was not found",
dvander@2193
    63
	"Not enough space on the heap",
dvander@2193
    64
	"Invalid parameter or parameter type",
dvander@2193
    65
	"Invalid plugin address",
dvander@2193
    66
	"Object or index not found",
dvander@2193
    67
	"Invalid index or index not found",
dvander@2193
    68
	"Not enough space on the stack",
dvander@2193
    69
	"Debug section not found or debug not enabled",
dvander@2193
    70
	"Invalid instruction",
dvander@2193
    71
	"Invalid memory access",
dvander@2193
    72
	"Stack went below stack boundary",
dvander@2193
    73
	"Heap went below heap boundary",
dvander@2193
    74
	"Divide by zero",
dvander@2193
    75
	"Array index is out of bounds",
dvander@2193
    76
	"Instruction contained invalid parameter",
dvander@2193
    77
	"Stack memory leaked by native",
dvander@2193
    78
	"Heap memory leaked by native",
dvander@2193
    79
	"Dynamic array is too big",
dvander@2193
    80
	"Tracker stack is out of bounds",
dvander@2193
    81
	"Native is not bound",
dvander@2193
    82
	"Maximum number of parameters reached",
dvander@2193
    83
	"Native detected error",
dvander@2193
    84
	"Plugin not runnable",
dvander@2193
    85
	"Call was aborted",
dvander@2226
    86
	"Plugin format is too old",
dvander@2226
    87
	"Plugin format is too new",
dvander@2193
    88
};
dvander@2193
    89
dvander@2193
    90
const char *SourcePawnEngine::GetErrorString(int error)
dvander@2193
    91
{
dvander@2193
    92
	if (error < 1 || error > ERROR_MESSAGE_MAX)
dvander@2193
    93
	{
dvander@2193
    94
		return NULL;
dvander@2193
    95
	}
dvander@2193
    96
dvander@2193
    97
	return g_ErrorMsgTable[error];
dvander@2193
    98
}
dvander@2193
    99
dvander@2193
   100
SourcePawnEngine::SourcePawnEngine()
dvander@2193
   101
{
dvander@2193
   102
	m_pDebugHook = NULL;
dvander@2193
   103
}
dvander@2193
   104
dvander@2193
   105
SourcePawnEngine::~SourcePawnEngine()
dvander@2193
   106
{
dvander@2193
   107
}
dvander@2193
   108
dvander@2193
   109
void *SourcePawnEngine::ExecAlloc(size_t size)
dvander@2193
   110
{
dvander@2193
   111
#if defined WIN32
dvander@2193
   112
	return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
dvander@2193
   113
#elif defined __GNUC__
dvander@2193
   114
	void *base = memalign(sysconf(_SC_PAGESIZE), size);
dvander@2193
   115
	if (mprotect(base, size, PROT_READ|PROT_WRITE|PROT_EXEC) != 0)
dvander@2193
   116
	{
dvander@2193
   117
		free(base);
dvander@2193
   118
		return NULL;
dvander@2193
   119
	}
dvander@2193
   120
	return base;
dvander@2193
   121
#endif
dvander@2193
   122
}
dvander@2193
   123
dvander@2193
   124
void *SourcePawnEngine::AllocatePageMemory(size_t size)
dvander@2193
   125
{
dvander@2226
   126
	return g_Jit.AllocCode(size);
dvander@2193
   127
}
dvander@2193
   128
dvander@2193
   129
void SourcePawnEngine::SetReadExecute(void *ptr)
dvander@2193
   130
{
dvander@2226
   131
	/* already re */
dvander@2193
   132
}
dvander@2193
   133
dvander@2193
   134
void SourcePawnEngine::SetReadWrite(void *ptr)
dvander@2193
   135
{
dvander@2226
   136
	/* already rw */
dvander@2193
   137
}
dvander@2193
   138
dvander@2193
   139
void SourcePawnEngine::FreePageMemory(void *ptr)
dvander@2193
   140
{
dvander@2226
   141
	g_Jit.FreeCode(ptr);
dvander@2193
   142
}
dvander@2193
   143
dvander@2193
   144
void SourcePawnEngine::ExecFree(void *address)
dvander@2193
   145
{
dvander@2193
   146
#if defined WIN32
dvander@2193
   147
	VirtualFree(address, 0, MEM_RELEASE);
dvander@2193
   148
#elif defined __GNUC__
dvander@2193
   149
	free(address);
dvander@2193
   150
#endif
dvander@2193
   151
}
dvander@2193
   152
dvander@2226
   153
void SourcePawnEngine::SetReadWriteExecute(void *ptr)
dvander@2226
   154
{
dvander@2226
   155
//:TODO:	g_ExeMemory.SetRWE(ptr);
dvander@2226
   156
	SetReadExecute(ptr);
dvander@2226
   157
}
dvander@2226
   158
dvander@2193
   159
void *SourcePawnEngine::BaseAlloc(size_t size)
dvander@2193
   160
{
dvander@2193
   161
	return malloc(size);
dvander@2193
   162
}
dvander@2193
   163
dvander@2193
   164
void SourcePawnEngine::BaseFree(void *memory)
dvander@2193
   165
{
dvander@2193
   166
	free(memory);
dvander@2193
   167
}
dvander@2193
   168
dvander@2193
   169
sp_plugin_t *SourcePawnEngine::LoadFromFilePointer(FILE *fp, int *err)
dvander@2193
   170
{
dvander@2193
   171
	if (err != NULL)
dvander@2193
   172
	{
dvander@2193
   173
		*err = SP_ERROR_ABORTED;
dvander@2193
   174
	}
dvander@2193
   175
dvander@2193
   176
	return NULL;
dvander@2193
   177
}
dvander@2193
   178
dvander@2193
   179
sp_plugin_t *SourcePawnEngine::LoadFromMemory(void *base, sp_plugin_t *plugin, int *err)
dvander@2193
   180
{
dvander@2193
   181
	if (err != NULL)
dvander@2193
   182
	{
dvander@2193
   183
		*err = SP_ERROR_ABORTED;
dvander@2193
   184
	}
dvander@2193
   185
dvander@2193
   186
	return NULL;
dvander@2193
   187
}
dvander@2193
   188
dvander@2193
   189
int SourcePawnEngine::FreeFromMemory(sp_plugin_t *plugin)
dvander@2193
   190
{
dvander@2193
   191
	return SP_ERROR_ABORTED;
dvander@2193
   192
}
dvander@2193
   193
dvander@2193
   194
IDebugListener *SourcePawnEngine::SetDebugListener(IDebugListener *pListener)
dvander@2193
   195
{
dvander@2193
   196
	IDebugListener *old = m_pDebugHook;
dvander@2193
   197
dvander@2193
   198
	m_pDebugHook = pListener;
dvander@2193
   199
dvander@2193
   200
	return old;
dvander@2193
   201
}
dvander@2193
   202
dvander@2193
   203
unsigned int SourcePawnEngine::GetEngineAPIVersion()
dvander@2193
   204
{
dvander@2193
   205
	return SOURCEPAWN_ENGINE_API_VERSION;
dvander@2193
   206
}
dvander@2193
   207
dvander@2226
   208
unsigned int SourcePawnEngine::GetContextCallCount()
dvander@2226
   209
{
dvander@2226
   210
	return 0;
dvander@2226
   211
}
dvander@2226
   212
dvander@2226
   213
void SourcePawnEngine::ReportError(BaseRuntime *runtime, int err, const char *errstr, cell_t rp_start)
dvander@2193
   214
{
dvander@2226
   215
	if (m_pDebugHook == NULL)
dvander@2226
   216
	{
dvander@2226
   217
		return;
dvander@2226
   218
	}
dvander@2226
   219
dvander@2226
   220
	CContextTrace trace(runtime, err, errstr, rp_start);
dvander@2226
   221
dvander@2226
   222
	m_pDebugHook->OnContextExecuteError(runtime->GetDefaultContext(), &trace);
dvander@2226
   223
}
dvander@2226
   224
dvander@2226
   225
CContextTrace::CContextTrace(BaseRuntime *pRuntime, int err, const char *errstr, cell_t start_rp) 
dvander@2226
   226
: m_pRuntime(pRuntime), m_Error(err), m_pMsg(errstr), m_StartRp(start_rp), m_Level(0)
dvander@2226
   227
{
dvander@2226
   228
	m_ctx = pRuntime->m_pCtx->GetCtx();
dvander@2226
   229
	m_pDebug = m_pRuntime->GetDebugInfo();
dvander@2193
   230
}
dvander@2193
   231
dvander@2193
   232
bool CContextTrace::DebugInfoAvailable()
dvander@2193
   233
{
dvander@2226
   234
	return (m_pDebug != NULL);
dvander@2193
   235
}
dvander@2193
   236
dvander@2193
   237
const char *CContextTrace::GetCustomErrorString()
dvander@2193
   238
{
dvander@2193
   239
	return m_pMsg;
dvander@2193
   240
}
dvander@2193
   241
dvander@2193
   242
int CContextTrace::GetErrorCode()
dvander@2193
   243
{
dvander@2193
   244
	return m_Error;
dvander@2193
   245
}
dvander@2193
   246
dvander@2193
   247
const char *CContextTrace::GetErrorString()
dvander@2193
   248
{
dvander@2226
   249
	if (m_Error > ERROR_MESSAGE_MAX || m_Error < 1)
dvander@2193
   250
	{
dvander@2193
   251
		return "Invalid error code";
dvander@2226
   252
	}
dvander@2226
   253
	else
dvander@2226
   254
	{
dvander@2193
   255
		return g_ErrorMsgTable[m_Error];
dvander@2193
   256
	}
dvander@2193
   257
}
dvander@2193
   258
dvander@2193
   259
void CContextTrace::ResetTrace()
dvander@2193
   260
{
dvander@2226
   261
	m_Level = 0;
dvander@2193
   262
}
dvander@2193
   263
dvander@2193
   264
bool CContextTrace::GetTraceInfo(CallStackInfo *trace)
dvander@2193
   265
{
dvander@2226
   266
	cell_t cip;
dvander@2226
   267
dvander@2226
   268
	if (m_Level == 0)
dvander@2193
   269
	{
dvander@2226
   270
		cip = m_ctx->err_cip;
dvander@2193
   271
	}
dvander@2226
   272
	else if (m_ctx->rp > 0)
dvander@2193
   273
	{
dvander@2226
   274
		/* Entries go from ctx.rp - 1 to m_StartRp */
dvander@2226
   275
		cell_t offs, start, end;
dvander@2226
   276
dvander@2226
   277
		offs = m_Level - 1;
dvander@2226
   278
		start = m_ctx->rp - 1;
dvander@2226
   279
		end = m_StartRp;
dvander@2193
   280
dvander@2226
   281
		if (start - offs < end)
dvander@2226
   282
		{
dvander@2226
   283
			return false;
dvander@2226
   284
		}
dvander@2193
   285
dvander@2226
   286
		cip = m_ctx->rstk_cips[start - offs];
dvander@2226
   287
	}
dvander@2226
   288
	else
dvander@2193
   289
	{
dvander@2193
   290
		return false;
dvander@2193
   291
	}
dvander@2193
   292
dvander@2226
   293
	if (trace == NULL)
dvander@2193
   294
	{
dvander@2226
   295
		m_Level++;
dvander@2193
   296
		return true;
dvander@2193
   297
	}
dvander@2193
   298
dvander@2226
   299
	if (m_pDebug->LookupFile(cip, &(trace->filename)) != SP_ERROR_NONE)
dvander@2193
   300
	{
dvander@2193
   301
		trace->filename = NULL;
dvander@2193
   302
	}
dvander@2193
   303
dvander@2226
   304
	if (m_pDebug->LookupFunction(cip, &(trace->function)) != SP_ERROR_NONE)
dvander@2193
   305
	{
dvander@2193
   306
		trace->function = NULL;
dvander@2193
   307
	}
dvander@2193
   308
dvander@2226
   309
	if (m_pDebug->LookupLine(cip, &(trace->line)) != SP_ERROR_NONE)
dvander@2193
   310
	{
dvander@2193
   311
		trace->line = 0;
dvander@2193
   312
	}
dvander@2193
   313
dvander@2226
   314
	m_Level++;
dvander@2193
   315
dvander@2193
   316
	return true;
dvander@2193
   317
}
dvander@2193
   318
dvander@2193
   319
const char *CContextTrace::GetLastNative(uint32_t *index)
dvander@2193
   320
{
dvander@2226
   321
	if (m_ctx->n_err == SP_ERROR_NONE)
dvander@2193
   322
	{
dvander@2193
   323
		return NULL;
dvander@2193
   324
	}
dvander@2193
   325
dvander@2193
   326
	sp_native_t *native;
dvander@2226
   327
	if (m_pRuntime->GetNativeByIndex(m_ctx->n_idx, &native) != SP_ERROR_NONE)
dvander@2193
   328
	{
dvander@2193
   329
		return NULL;
dvander@2193
   330
	}
dvander@2193
   331
dvander@2193
   332
	if (index)
dvander@2193
   333
	{
dvander@2226
   334
		*index = m_ctx->n_idx;
dvander@2193
   335
	}
dvander@2193
   336
dvander@2193
   337
	return native->name;
dvander@2193
   338
}
dvander@2226
   339
dvander@2226
   340
IDebugListener *SourcePawnEngine::GetDebugHook()
dvander@2226
   341
{
dvander@2226
   342
	return m_pDebugHook;
dvander@2226
   343
}
dvander@2226
   344