Rewrite the .smx parser.
This removes one the last remnants of the SourceMod 1.0 VM implementation. The new parser introduces a number of design changes in the VM. First, the VM now takes greater responsibility for validating and sanity checking the structure of the SMX container format. Previously, malformed SMX files could easily crash SourcePawn. The loader now rejects files that have out-of-bounds offsets or incomplete sections. Complex sections, like debug info or the code stream, are verified lazily. Internally, the sp_plugin_t structure has been removed. It has been replaced by a new LegacyImage class, designed to be independent from the SPVM API. This potentially lets us load code streams from non-.smx containers. More importantly, it removes a lot of bookkeeping and pre-computed state from PluginRuntime. The LegacyImage class is now responsible for handling debug info as well. PluginRuntime is now intended to hold only cached or immutable data, and PluginContext holds all VM state. As such PluginContext is now responsible for allocating a plugin's runtime memory, not PluginRuntime. Finally, some aspects of the loading process have been cleaned up. The decompression and image handoff logic should now be easier to understand.
This commit is contained in:
parent
afbcdc8a20
commit
04827466b0
@ -932,31 +932,18 @@ LoadRes CPluginManager::_LoadPlugin(CPlugin **aResult, const char *path, bool de
|
||||
char fullpath[PLATFORM_MAX_PATH];
|
||||
g_pSM->BuildPath(Path_SM, fullpath, sizeof(fullpath), "plugins/%s", pPlugin->m_filename);
|
||||
|
||||
pPlugin->m_pRuntime = g_pSourcePawn2->LoadPlugin(nullptr, fullpath, &err);
|
||||
if (pPlugin->m_pRuntime == NULL)
|
||||
{
|
||||
char loadmsg[255];
|
||||
pPlugin->m_pRuntime = g_pSourcePawn2->LoadBinaryFromFile(fullpath, loadmsg, sizeof(loadmsg));
|
||||
if (!pPlugin->m_pRuntime) {
|
||||
if (error)
|
||||
{
|
||||
smcore.Format(error,
|
||||
maxlength,
|
||||
"Unable to load plugin (error %d: %s)",
|
||||
err,
|
||||
g_pSourcePawn2->GetErrorString(err));
|
||||
}
|
||||
smcore.Format(error, maxlength, "Unable to load plugin (%s)", loadmsg);
|
||||
pPlugin->m_status = Plugin_BadLoad;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pPlugin->UpdateInfo())
|
||||
{
|
||||
} else {
|
||||
if (pPlugin->UpdateInfo()) {
|
||||
pPlugin->m_status = Plugin_Created;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
if (error)
|
||||
{
|
||||
smcore.Format(error, maxlength, "%s", pPlugin->m_errormsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,8 +22,8 @@
|
||||
#include "sp_vm_types.h"
|
||||
|
||||
/** SourcePawn Engine API Versions */
|
||||
#define SOURCEPAWN_ENGINE2_API_VERSION 8
|
||||
#define SOURCEPAWN_API_VERSION 0x0208
|
||||
#define SOURCEPAWN_ENGINE2_API_VERSION 9
|
||||
#define SOURCEPAWN_API_VERSION 0x0209
|
||||
|
||||
namespace SourceMod {
|
||||
struct IdentityToken_t;
|
||||
@ -1316,6 +1316,16 @@ namespace SourcePawn
|
||||
* @param tool Profiling tool.
|
||||
*/
|
||||
virtual void SetProfilingTool(IProfilingTool *tool) =0;
|
||||
|
||||
/**
|
||||
* @brief Loads a plugin from disk.
|
||||
*
|
||||
* @param file Path to the file to compile.
|
||||
* @param errpr Buffer to store an error message (optional).
|
||||
* @param maxlength Maximum length of the error buffer.
|
||||
* @return New runtime pointer, or NULL on failure.
|
||||
*/
|
||||
virtual IPluginRuntime *LoadBinaryFromFile(const char *file, char *error, size_t maxlength) = 0;
|
||||
};
|
||||
|
||||
// @brief This class is the v3 API for SourcePawn. It provides access to
|
||||
|
@ -35,13 +35,15 @@ library.sources += [
|
||||
'api.cpp',
|
||||
'code-allocator.cpp',
|
||||
'code-stubs.cpp',
|
||||
'plugin-context.cpp',
|
||||
'plugin-runtime.cpp',
|
||||
'compiled-function.cpp',
|
||||
'debug-trace.cpp',
|
||||
'environment.cpp',
|
||||
'scripted-invoker.cpp',
|
||||
'file-utils.cpp',
|
||||
'opcodes.cpp',
|
||||
'plugin-context.cpp',
|
||||
'plugin-runtime.cpp',
|
||||
'scripted-invoker.cpp',
|
||||
'smx-v1-image.cpp',
|
||||
'watchdog_timer.cpp',
|
||||
'x86/code-stubs-x86.cpp',
|
||||
'x86/jit_x86.cpp',
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
#include <sourcemod_version.h>
|
||||
#include "code-stubs.h"
|
||||
#include "smx-v1-image.h"
|
||||
|
||||
using namespace sp;
|
||||
using namespace SourcePawn;
|
||||
@ -176,124 +177,93 @@ SourcePawnEngine2::SourcePawnEngine2()
|
||||
{
|
||||
}
|
||||
|
||||
static size_t
|
||||
UTIL_Format(char *buffer, size_t maxlength, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
size_t len = vsnprintf(buffer, maxlength, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (len >= maxlength) {
|
||||
buffer[maxlength - 1] = '\0';
|
||||
return maxlength - 1;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
IPluginRuntime *
|
||||
SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file, int *err)
|
||||
{
|
||||
sp_file_hdr_t hdr;
|
||||
uint8_t *base;
|
||||
int z_result;
|
||||
int error;
|
||||
size_t ignore;
|
||||
PluginRuntime *pRuntime;
|
||||
|
||||
if (co) {
|
||||
if (err)
|
||||
*err = SP_ERROR_PARAM;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IPluginRuntime *rt = LoadBinaryFromFile(file, nullptr, 0);
|
||||
if (!rt) {
|
||||
if (err) {
|
||||
if (FILE *fp = fopen(file, "rb")) {
|
||||
fclose(fp);
|
||||
*err = SP_ERROR_FILE_FORMAT;
|
||||
} else {
|
||||
*err = SP_ERROR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
IPluginRuntime *
|
||||
SourcePawnEngine2::LoadBinaryFromFile(const char *file, char *error, size_t maxlength)
|
||||
{
|
||||
FILE *fp = fopen(file, "rb");
|
||||
|
||||
if (!fp) {
|
||||
error = SP_ERROR_NOT_FOUND;
|
||||
goto return_error;
|
||||
UTIL_Format(error, maxlength, "file not found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Rewind for safety */
|
||||
ignore = fread(&hdr, sizeof(sp_file_hdr_t), 1, fp);
|
||||
|
||||
if (hdr.magic != SmxConsts::FILE_MAGIC) {
|
||||
error = SP_ERROR_FILE_FORMAT;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
switch (hdr.compression)
|
||||
{
|
||||
case SmxConsts::FILE_COMPRESSION_GZ:
|
||||
{
|
||||
uint32_t uncompsize = hdr.imagesize - hdr.dataoffs;
|
||||
uint32_t compsize = hdr.disksize - hdr.dataoffs;
|
||||
uint32_t sectsize = hdr.dataoffs - sizeof(sp_file_hdr_t);
|
||||
uLongf destlen = uncompsize;
|
||||
|
||||
char *tempbuf = (char *)malloc(compsize);
|
||||
void *uncompdata = malloc(uncompsize);
|
||||
void *sectheader = malloc(sectsize);
|
||||
|
||||
ignore = fread(sectheader, sectsize, 1, fp);
|
||||
ignore = fread(tempbuf, compsize, 1, fp);
|
||||
|
||||
z_result = uncompress((Bytef *)uncompdata, &destlen, (Bytef *)tempbuf, compsize);
|
||||
free(tempbuf);
|
||||
if (z_result != Z_OK)
|
||||
{
|
||||
free(sectheader);
|
||||
free(uncompdata);
|
||||
error = SP_ERROR_DECOMPRESSOR;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
base = (uint8_t *)malloc(hdr.imagesize);
|
||||
memcpy(base, &hdr, sizeof(sp_file_hdr_t));
|
||||
memcpy(base + sizeof(sp_file_hdr_t), sectheader, sectsize);
|
||||
free(sectheader);
|
||||
memcpy(base + hdr.dataoffs, uncompdata, uncompsize);
|
||||
free(uncompdata);
|
||||
break;
|
||||
}
|
||||
case SmxConsts::FILE_COMPRESSION_NONE:
|
||||
{
|
||||
base = (uint8_t *)malloc(hdr.imagesize);
|
||||
rewind(fp);
|
||||
ignore = fread(base, hdr.imagesize, 1, fp);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
error = SP_ERROR_DECOMPRESSOR;
|
||||
goto return_error;
|
||||
}
|
||||
}
|
||||
|
||||
pRuntime = new PluginRuntime();
|
||||
if ((error = pRuntime->CreateFromMemory(&hdr, base)) != SP_ERROR_NONE) {
|
||||
delete pRuntime;
|
||||
goto return_error;
|
||||
}
|
||||
|
||||
size_t len;
|
||||
|
||||
len = strlen(file);
|
||||
for (size_t i = len - 1; i < len; i--)
|
||||
{
|
||||
if (file[i] == '/'
|
||||
#if defined WIN32
|
||||
|| file[i] == '\\'
|
||||
#endif
|
||||
)
|
||||
{
|
||||
pRuntime->SetName(&file[i+1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
(void)ignore;
|
||||
|
||||
if (!pRuntime->plugin()->name)
|
||||
pRuntime->SetName(file);
|
||||
|
||||
ke::AutoPtr<SmxV1Image> image(new SmxV1Image(fp));
|
||||
fclose(fp);
|
||||
|
||||
return pRuntime;
|
||||
|
||||
return_error:
|
||||
*err = error;
|
||||
if (fp != NULL)
|
||||
{
|
||||
fclose(fp);
|
||||
if (!image->validate()) {
|
||||
const char *errorMessage = image->errorMessage();
|
||||
if (!errorMessage)
|
||||
errorMessage = "file parse error";
|
||||
UTIL_Format(error, maxlength, "%s", errorMessage);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
PluginRuntime *pRuntime = new PluginRuntime(image.take());
|
||||
if (!pRuntime->Initialize()) {
|
||||
delete pRuntime;
|
||||
|
||||
UTIL_Format(error, maxlength, "out of memory");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t len = strlen(file);
|
||||
for (size_t i = len - 1; i < len; i--) {
|
||||
if (file[i] == '/'
|
||||
# if defined WIN32
|
||||
|| file[i] == '\\'
|
||||
# endif
|
||||
)
|
||||
{
|
||||
pRuntime->SetName(&file[i + 1]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pRuntime->Name())
|
||||
pRuntime->SetName(file);
|
||||
|
||||
return pRuntime;
|
||||
}
|
||||
|
||||
SPVM_NATIVE_FUNC
|
||||
@ -362,8 +332,10 @@ SourcePawnEngine2::CreateEmptyRuntime(const char *name, uint32_t memory)
|
||||
{
|
||||
int err;
|
||||
|
||||
PluginRuntime *rt = new PluginRuntime();
|
||||
if ((err = rt->CreateBlank(memory)) != SP_ERROR_NONE) {
|
||||
ke::AutoPtr<EmptyImage> image(new EmptyImage(memory));
|
||||
|
||||
PluginRuntime *rt = new PluginRuntime(image.take());
|
||||
if (!rt->Initialize()) {
|
||||
delete rt;
|
||||
return NULL;
|
||||
}
|
||||
|
@ -61,13 +61,13 @@ class SourcePawnEngine2 : public ISourcePawnEngine2
|
||||
void Shutdown() KE_OVERRIDE;
|
||||
IPluginRuntime *CreateEmptyRuntime(const char *name, uint32_t memory) KE_OVERRIDE;
|
||||
bool InstallWatchdogTimer(size_t timeout_ms) KE_OVERRIDE;
|
||||
|
||||
bool SetJitEnabled(bool enabled) KE_OVERRIDE;
|
||||
bool IsJitEnabled() KE_OVERRIDE;
|
||||
void SetProfiler(IProfiler *profiler) KE_OVERRIDE;
|
||||
void EnableProfiling() KE_OVERRIDE;
|
||||
void DisableProfiling() KE_OVERRIDE;
|
||||
void SetProfilingTool(IProfilingTool *tool) KE_OVERRIDE;
|
||||
IPluginRuntime *LoadBinaryFromFile(const char *file, char *error, size_t maxlength) KE_OVERRIDE;
|
||||
};
|
||||
|
||||
} // namespace SourcePawn
|
||||
|
@ -16,10 +16,9 @@
|
||||
#include <stdint.h>
|
||||
#include <sp_vm_api.h>
|
||||
|
||||
class PluginContext;
|
||||
|
||||
namespace sp {
|
||||
|
||||
class PluginContext;
|
||||
class Environment;
|
||||
|
||||
typedef int (*InvokeStubFn)(PluginContext *cx, void *code, cell_t *rval);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "debug-trace.h"
|
||||
#include "plugin-context.h"
|
||||
#include "environment.h"
|
||||
#include "plugin-runtime.h"
|
||||
|
||||
using namespace ke;
|
||||
using namespace sp;
|
||||
|
@ -15,13 +15,13 @@
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
|
||||
class PluginRuntime;
|
||||
class PluginContext;
|
||||
|
||||
namespace sp {
|
||||
|
||||
using namespace SourcePawn;
|
||||
|
||||
class PluginRuntime;
|
||||
class PluginContext;
|
||||
|
||||
class CContextTrace : public IContextTrace
|
||||
{
|
||||
public:
|
||||
|
@ -20,12 +20,11 @@
|
||||
#include "code-allocator.h"
|
||||
#include "plugin-runtime.h"
|
||||
|
||||
class PluginRuntime;
|
||||
|
||||
namespace sp {
|
||||
|
||||
using namespace SourcePawn;
|
||||
|
||||
class PluginRuntime;
|
||||
class CodeStubs;
|
||||
class WatchdogTimer;
|
||||
|
||||
|
52
sourcepawn/jit/file-utils.cpp
Normal file
52
sourcepawn/jit/file-utils.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2004-2015 AlliedModers LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is licensed under the GNU
|
||||
// General Public License, version 3.0 (GPL). If a copy of the GPL was not
|
||||
// provided with this file, you can obtain it here:
|
||||
// http://www.gnu.org/licenses/gpl.html
|
||||
//
|
||||
#include <stdint.h>
|
||||
#include <smx/smx-headers.h>
|
||||
#include "file-utils.h"
|
||||
|
||||
using namespace sp;
|
||||
|
||||
FileType
|
||||
sp::DetectFileType(FILE *fp)
|
||||
{
|
||||
uint32_t magic = 0;
|
||||
if (fread(&magic, sizeof(uint32_t), 1, fp) != 1)
|
||||
return FileType::UNKNOWN;
|
||||
|
||||
if (magic == SmxConsts::FILE_MAGIC)
|
||||
return FileType::SPFF;
|
||||
|
||||
return FileType::UNKNOWN;
|
||||
}
|
||||
|
||||
FileReader::FileReader(FILE *fp)
|
||||
: length_(0)
|
||||
{
|
||||
if (fseek(fp, 0, SEEK_END) != 0)
|
||||
return;
|
||||
long size = ftell(fp);
|
||||
if (size < 0)
|
||||
return;
|
||||
if (fseek(fp, 0, SEEK_SET) != 0)
|
||||
return;
|
||||
|
||||
ke::AutoArray<uint8_t> bytes(new uint8_t[size]);
|
||||
if (!bytes || fread(bytes, sizeof(uint8_t), size, fp) != size)
|
||||
return;
|
||||
|
||||
buffer_ = bytes.take();
|
||||
length_ = size;
|
||||
}
|
||||
|
||||
FileReader::FileReader(ke::AutoArray<uint8_t> &buffer, size_t length)
|
||||
: buffer_(buffer.take()),
|
||||
length_(length)
|
||||
{
|
||||
}
|
47
sourcepawn/jit/file-utils.h
Normal file
47
sourcepawn/jit/file-utils.h
Normal file
@ -0,0 +1,47 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2004-2015 AlliedModers LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is licensed under the GNU
|
||||
// General Public License, version 3.0 (GPL). If a copy of the GPL was not
|
||||
// provided with this file, you can obtain it here:
|
||||
// http://www.gnu.org/licenses/gpl.html
|
||||
//
|
||||
#ifndef _include_sourcepawn_file_parser_h_
|
||||
#define _include_sourcepawn_file_parser_h_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <am-utility.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
enum class FileType {
|
||||
UNKNOWN,
|
||||
AMX,
|
||||
AMXMODX,
|
||||
SPFF
|
||||
};
|
||||
|
||||
FileType DetectFileType(FILE *fp);
|
||||
|
||||
class FileReader
|
||||
{
|
||||
public:
|
||||
FileReader(FILE *fp);
|
||||
FileReader(ke::AutoArray<uint8_t> &buffer, size_t length);
|
||||
|
||||
const uint8_t *buffer() const {
|
||||
return buffer_;
|
||||
}
|
||||
size_t length() const {
|
||||
return length_;
|
||||
}
|
||||
|
||||
protected:
|
||||
ke::AutoArray<uint8_t> buffer_;
|
||||
size_t length_;
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
|
||||
#endif // _include_sourcepawn_file_parser_h_
|
@ -1,78 +0,0 @@
|
||||
// vim: set ts=4 sw=4 tw=99 noet:
|
||||
#ifndef _INCLUDE_SOURCEPAWN_JIT_SHARED_H_
|
||||
#define _INCLUDE_SOURCEPAWN_JIT_SHARED_H_
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
#include <smx/smx-v1.h>
|
||||
|
||||
using namespace sp;
|
||||
using namespace SourcePawn;
|
||||
|
||||
#define SP_MAX_RETURN_STACK 1024
|
||||
|
||||
/**
|
||||
* @brief Information about the plugin's debug tables. These are all present if one is present.
|
||||
*/
|
||||
typedef struct sp_plugin_debug_s
|
||||
{
|
||||
const char *stringbase; /**< base of string table */
|
||||
uint32_t files_num; /**< number of files */
|
||||
sp_fdbg_file_t *files; /**< files table */
|
||||
uint32_t lines_num; /**< number of lines */
|
||||
sp_fdbg_line_t *lines; /**< lines table */
|
||||
uint32_t syms_num; /**< number of symbols */
|
||||
sp_fdbg_symbol_t *symbols; /**< symbol table */
|
||||
bool unpacked; /**< Whether debug structures are unpacked */
|
||||
} sp_plugin_debug_t;
|
||||
|
||||
class PluginContext;
|
||||
|
||||
/**
|
||||
* Breaks into a debugger
|
||||
* Params:
|
||||
* [0] - plugin context
|
||||
* [1] - frm
|
||||
* [2] - cip
|
||||
*/
|
||||
typedef int (*SPVM_DEBUGBREAK)(PluginContext *, uint32_t, uint32_t);
|
||||
|
||||
/**
|
||||
* @brief The rebased memory format of a plugin. This differs from the on-disk structure
|
||||
* to ensure that the format is properly read.
|
||||
*/
|
||||
namespace SourcePawn
|
||||
{
|
||||
typedef struct sp_plugin_s
|
||||
{
|
||||
uint8_t *base; /**< Base of memory for this plugin. */
|
||||
uint8_t *pcode; /**< P-Code of plugin */
|
||||
uint32_t pcode_size; /**< Size of p-code */
|
||||
uint8_t *data; /**< Data/memory layout */
|
||||
uint32_t data_size; /**< Size of data */
|
||||
uint32_t mem_size; /**< Required memory space */
|
||||
uint16_t flags; /**< Code flags */
|
||||
sp_plugin_debug_t debug; /**< Debug info table */
|
||||
size_t base_size; /**< Size of the entire plugin base */
|
||||
uint8_t *memory; /**< Data chunk */
|
||||
const char *stringbase; /**< base of string table */
|
||||
sp_public_t *publics; /**< Public functions table */
|
||||
uint32_t num_publics; /**< Number of publics. */
|
||||
sp_pubvar_t *pubvars; /**< Public variables table */
|
||||
uint32_t num_pubvars; /**< Number of public variables */
|
||||
sp_native_t *natives; /**< Natives table */
|
||||
uint32_t num_natives; /**< Number of natives */
|
||||
uint32_t prof_flags; /**< Profiling flags */
|
||||
uint32_t run_flags; /**< Runtime flags */
|
||||
uint32_t pcode_version; /**< P-Code version number */
|
||||
char *name; /**< Plugin/script name */
|
||||
} sp_plugin_t;
|
||||
}
|
||||
|
||||
class PluginContext;
|
||||
|
||||
//#define SPFLAG_PLUGIN_DEBUG (1<<0)
|
||||
#define SPFLAG_PLUGIN_PAUSED (1<<1)
|
||||
|
||||
#define INVALID_CIP 0xFFFFFFFF
|
||||
|
||||
#endif //_INCLUDE_SOURCEPAWN_JIT_SHARED_H_
|
138
sourcepawn/jit/legacy-image.h
Normal file
138
sourcepawn/jit/legacy-image.h
Normal file
@ -0,0 +1,138 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2006-2015 AlliedModders LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is free software: you can
|
||||
// redistribute it and/or modify it under the terms of the GNU General Public
|
||||
// License as published by the Free Software Foundation, either version 3 of
|
||||
// the License, or (at your option) any later version.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along with
|
||||
// SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||
//
|
||||
#ifndef _include_sourcepawn_vm_legacy_image_h_
|
||||
#define _include_sourcepawn_vm_legacy_image_h_
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace sp {
|
||||
|
||||
// A LegacyImage is an abstraction layer for reading the various types of
|
||||
// binaries that expose Pawn or SourcePawn v1 pcode.
|
||||
class LegacyImage
|
||||
{
|
||||
public:
|
||||
virtual ~LegacyImage()
|
||||
{}
|
||||
|
||||
enum class CodeVersion {
|
||||
Unknown,
|
||||
SP_1_0,
|
||||
SP_1_1
|
||||
};
|
||||
|
||||
struct Code {
|
||||
const uint8_t *bytes;
|
||||
size_t length;
|
||||
CodeVersion version;
|
||||
};
|
||||
struct Data {
|
||||
const uint8_t *bytes;
|
||||
size_t length;
|
||||
};
|
||||
|
||||
// (Almost) everything needed to implement the AMX and SPVM API.
|
||||
virtual Code DescribeCode() const = 0;
|
||||
virtual Data DescribeData() const = 0;
|
||||
virtual size_t NumNatives() const = 0;
|
||||
virtual const char *GetNative(size_t index) const = 0;
|
||||
virtual bool FindNative(const char *name, size_t *indexp) const = 0;
|
||||
virtual size_t NumPublics() const = 0;
|
||||
virtual void GetPublic(size_t index, uint32_t *offsetp, const char **namep) const = 0;
|
||||
virtual bool FindPublic(const char *name, size_t *indexp) const = 0;
|
||||
virtual size_t NumPubvars() const = 0;
|
||||
virtual void GetPubvar(size_t index, uint32_t *offsetp, const char **namep) const = 0;
|
||||
virtual bool FindPubvar(const char *name, size_t *indexp) const = 0;
|
||||
virtual size_t HeapSize() const = 0;
|
||||
virtual size_t ImageSize() const = 0;
|
||||
virtual const char *LookupFile(uint32_t code_offset) = 0;
|
||||
virtual const char *LookupFunction(uint32_t code_offset) = 0;
|
||||
virtual bool LookupLine(uint32_t code_offset, uint32_t *line) = 0;
|
||||
};
|
||||
|
||||
class EmptyImage : public LegacyImage
|
||||
{
|
||||
public:
|
||||
EmptyImage(size_t heapSize)
|
||||
: heap_size_(heapSize)
|
||||
{
|
||||
heap_size_ += sizeof(uint32_t);
|
||||
heap_size_ -= heap_size_ % sizeof(uint32_t);
|
||||
memset(data_, 0, sizeof(data_));
|
||||
memset(code_, 0, sizeof(code_));
|
||||
}
|
||||
|
||||
public:
|
||||
Code DescribeCode() const KE_OVERRIDE {
|
||||
Code out;
|
||||
out.bytes = code_;
|
||||
out.length = sizeof(code_);
|
||||
out.version = CodeVersion::SP_1_1;
|
||||
return out;
|
||||
}
|
||||
Data DescribeData() const KE_OVERRIDE {
|
||||
Data out;
|
||||
out.bytes = data_;
|
||||
out.length = sizeof(data_);
|
||||
return out;
|
||||
}
|
||||
size_t NumNatives() const KE_OVERRIDE {
|
||||
return 0;
|
||||
}
|
||||
const char *GetNative(size_t index) const KE_OVERRIDE {
|
||||
return nullptr;
|
||||
}
|
||||
bool FindNative(const char *name, size_t *indexp) const KE_OVERRIDE {
|
||||
return false;
|
||||
}
|
||||
size_t NumPublics() const KE_OVERRIDE {
|
||||
return 0;
|
||||
}
|
||||
void GetPublic(size_t index, uint32_t *offsetp, const char **namep) const KE_OVERRIDE {
|
||||
}
|
||||
bool FindPublic(const char *name, size_t *indexp) const KE_OVERRIDE {
|
||||
return false;
|
||||
}
|
||||
size_t NumPubvars() const KE_OVERRIDE {
|
||||
return 0;
|
||||
}
|
||||
void GetPubvar(size_t index, uint32_t *offsetp, const char **namep) const KE_OVERRIDE {
|
||||
}
|
||||
bool FindPubvar(const char *name, size_t *indexp) const KE_OVERRIDE {
|
||||
return false;
|
||||
}
|
||||
size_t HeapSize() const KE_OVERRIDE {
|
||||
return heap_size_;
|
||||
}
|
||||
size_t ImageSize() const KE_OVERRIDE {
|
||||
return 0;
|
||||
}
|
||||
const char *LookupFile(uint32_t code_offset) KE_OVERRIDE {
|
||||
return nullptr;
|
||||
}
|
||||
const char *LookupFunction(uint32_t code_offset) {
|
||||
return nullptr;
|
||||
}
|
||||
bool LookupLine(uint32_t code_offset, uint32_t *line) KE_OVERRIDE {
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t heap_size_;
|
||||
uint8_t data_[4];
|
||||
uint8_t code_[4];
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // _include_sourcepawn_vm_legacy_image_h_
|
@ -58,7 +58,7 @@ MD5::MD5(){
|
||||
// operation, processing another message block, and updating the
|
||||
// context.
|
||||
|
||||
void MD5::update (uint1 *input, uint4 input_length) {
|
||||
void MD5::update (const uint1 *input, uint4 input_length) {
|
||||
|
||||
uint4 input_index, buffer_index;
|
||||
uint4 buffer_space; // how much space is left in buffer
|
||||
@ -271,7 +271,7 @@ void MD5::init(){
|
||||
|
||||
|
||||
// MD5 basic transformation. Transforms state based on block.
|
||||
void MD5::transform (uint1 block[64]){
|
||||
void MD5::transform (const uint1 block[64]){
|
||||
|
||||
uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
|
||||
|
||||
@ -365,7 +365,7 @@ void MD5::transform (uint1 block[64]){
|
||||
|
||||
// Encodes input (UINT4) into output (unsigned char). Assumes len is
|
||||
// a multiple of 4.
|
||||
void MD5::encode (uint1 *output, uint4 *input, uint4 len) {
|
||||
void MD5::encode (uint1 *output, const uint4 *input, uint4 len) {
|
||||
|
||||
unsigned int i, j;
|
||||
|
||||
@ -382,7 +382,7 @@ void MD5::encode (uint1 *output, uint4 *input, uint4 len) {
|
||||
|
||||
// Decodes input (unsigned char) into output (UINT4). Assumes len is
|
||||
// a multiple of 4.
|
||||
void MD5::decode (uint4 *output, uint1 *input, uint4 len){
|
||||
void MD5::decode (uint4 *output, const uint1 *input, uint4 len){
|
||||
|
||||
unsigned int i, j;
|
||||
|
||||
@ -396,7 +396,7 @@ void MD5::decode (uint4 *output, uint1 *input, uint4 len){
|
||||
|
||||
|
||||
// Note: Replace "for loop" with standard memcpy if possible.
|
||||
void MD5::memcpy (uint1 *output, uint1 *input, uint4 len){
|
||||
void MD5::memcpy (uint1 *output, const uint1 *input, uint4 len){
|
||||
|
||||
unsigned int i;
|
||||
|
||||
|
@ -48,7 +48,7 @@ class MD5 {
|
||||
public:
|
||||
// methods for controlled operation:
|
||||
MD5 (); // simple initializer
|
||||
void update (unsigned char *input, unsigned int input_length);
|
||||
void update (const unsigned char *input, unsigned int input_length);
|
||||
void update (FILE *file);
|
||||
void finalize ();
|
||||
|
||||
@ -81,12 +81,12 @@ private:
|
||||
|
||||
// last, the private methods, mostly static:
|
||||
void init (); // called by all constructors
|
||||
void transform (uint1 *buffer); // does the real update work. Note
|
||||
void transform (const uint1 *buffer); // does the real update work. Note
|
||||
// that length is implied to be 64.
|
||||
|
||||
static void encode (uint1 *dest, uint4 *src, uint4 length);
|
||||
static void decode (uint4 *dest, uint1 *src, uint4 length);
|
||||
static void memcpy (uint1 *dest, uint1 *src, uint4 length);
|
||||
static void encode (uint1 *dest, const uint4 *src, uint4 length);
|
||||
static void decode (uint4 *dest, const uint1 *src, uint4 length);
|
||||
static void memcpy (uint1 *dest, const uint1 *src, uint4 length);
|
||||
static void memset (uint1 *start, uint1 val, uint4 length);
|
||||
|
||||
static inline uint4 rotate_left (uint4 x, uint4 n);
|
||||
|
@ -29,7 +29,6 @@
|
||||
* Version: $Id$
|
||||
*/
|
||||
#include "opcodes.h"
|
||||
#include "jit_shared.h"
|
||||
|
||||
using namespace sp;
|
||||
using namespace SourcePawn;
|
||||
@ -41,6 +40,7 @@ const char *OpcodeNames[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
#ifdef JIT_SPEW
|
||||
void
|
||||
SourcePawn::SpewOpcode(const sp_plugin_t *plugin, cell_t *start, cell_t *cip)
|
||||
{
|
||||
@ -137,4 +137,5 @@ SourcePawn::SpewOpcode(const sp_plugin_t *plugin, cell_t *start, cell_t *cip)
|
||||
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -33,10 +33,8 @@
|
||||
#define _INCLUDE_SOURCEPAWN_JIT_X86_OPCODES_H_
|
||||
|
||||
#include <smx/smx-v1-opcodes.h>
|
||||
#include "jit_shared.h"
|
||||
|
||||
namespace SourcePawn {
|
||||
void SpewOpcode(const sp_plugin_t *plugin, cell_t *start, cell_t *cip);
|
||||
}
|
||||
|
||||
#endif //_INCLUDE_SOURCEPAWN_JIT_X86_OPCODES_H_
|
||||
|
@ -19,18 +19,62 @@
|
||||
#include "watchdog_timer.h"
|
||||
#include "x86/jit_x86.h"
|
||||
#include "environment.h"
|
||||
#include "compiled-function.h"
|
||||
|
||||
using namespace sp;
|
||||
using namespace SourcePawn;
|
||||
|
||||
#define CELLBOUNDMAX (INT_MAX/sizeof(cell_t))
|
||||
#define STACKMARGIN ((cell_t)(16*sizeof(cell_t)))
|
||||
|
||||
PluginContext::PluginContext(PluginRuntime *pRuntime)
|
||||
{
|
||||
m_pRuntime = pRuntime;
|
||||
static const size_t kMinHeapSize = 16384;
|
||||
|
||||
m_InExec = false;
|
||||
m_CustomMsg = false;
|
||||
PluginContext::PluginContext(PluginRuntime *pRuntime)
|
||||
: m_pRuntime(pRuntime),
|
||||
memory_(nullptr),
|
||||
data_size_(m_pRuntime->data().length),
|
||||
mem_size_(m_pRuntime->image()->HeapSize()),
|
||||
m_pNullVec(nullptr),
|
||||
m_pNullString(nullptr),
|
||||
m_CustomMsg(false),
|
||||
m_InExec(false)
|
||||
{
|
||||
// Compute and align a minimum memory amount.
|
||||
if (mem_size_ < data_size_)
|
||||
mem_size_ = data_size_;
|
||||
mem_size_ = ke::Align(mem_size_, sizeof(cell_t));
|
||||
|
||||
// Add a minimum heap size if needed.
|
||||
if (mem_size_ < data_size_ + kMinHeapSize)
|
||||
mem_size_ = data_size_ + kMinHeapSize;
|
||||
assert(ke::IsAligned(mem_size_, sizeof(cell_t)));
|
||||
|
||||
hp_ = data_size_;
|
||||
sp_ = mem_size_ - sizeof(cell_t);
|
||||
frm_ = sp_;
|
||||
rp_ = 0;
|
||||
last_native_ = -1;
|
||||
native_error_ = SP_ERROR_NONE;
|
||||
|
||||
tracker_.pBase = (ucell_t *)malloc(1024);
|
||||
tracker_.pCur = tracker_.pBase;
|
||||
tracker_.size = 1024 / sizeof(cell_t);
|
||||
}
|
||||
|
||||
PluginContext::~PluginContext()
|
||||
{
|
||||
free(tracker_.pBase);
|
||||
delete[] memory_;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginContext::Initialize()
|
||||
{
|
||||
memory_ = new uint8_t[mem_size_];
|
||||
if (!memory_)
|
||||
return false;
|
||||
memset(memory_ + data_size_, 0, mem_size_ - data_size_);
|
||||
memcpy(memory_, m_pRuntime->data().bytes, data_size_);
|
||||
|
||||
/* Initialize the null references */
|
||||
uint32_t index;
|
||||
@ -50,21 +94,7 @@ PluginContext::PluginContext(PluginRuntime *pRuntime)
|
||||
m_pNullString = NULL;
|
||||
}
|
||||
|
||||
hp_ = m_pRuntime->plugin()->data_size;
|
||||
sp_ = m_pRuntime->plugin()->mem_size - sizeof(cell_t);
|
||||
frm_ = sp_;
|
||||
rp_ = 0;
|
||||
last_native_ = -1;
|
||||
native_error_ = SP_ERROR_NONE;
|
||||
|
||||
tracker_.pBase = (ucell_t *)malloc(1024);
|
||||
tracker_.pCur = tracker_.pBase;
|
||||
tracker_.size = 1024 / sizeof(cell_t);
|
||||
}
|
||||
|
||||
PluginContext::~PluginContext()
|
||||
{
|
||||
free(tracker_.pBase);
|
||||
return true;
|
||||
}
|
||||
|
||||
IVirtualMachine *
|
||||
@ -179,7 +209,7 @@ PluginContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_a
|
||||
if ((cell_t)(sp_ - hp_ - realmem) < STACKMARGIN)
|
||||
return SP_ERROR_HEAPLOW;
|
||||
|
||||
addr = (cell_t *)(m_pRuntime->plugin()->memory + hp_);
|
||||
addr = (cell_t *)(memory_ + hp_);
|
||||
/* store size of allocation in cells */
|
||||
*addr = (cell_t)cells;
|
||||
addr++;
|
||||
@ -203,10 +233,10 @@ PluginContext::HeapPop(cell_t local_addr)
|
||||
|
||||
/* check the bounds of this address */
|
||||
local_addr -= sizeof(cell_t);
|
||||
if (local_addr < (cell_t)m_pRuntime->plugin()->data_size || local_addr >= sp_)
|
||||
if (local_addr < (cell_t)data_size_ || local_addr >= sp_)
|
||||
return SP_ERROR_INVALID_ADDRESS;
|
||||
|
||||
addr = (cell_t *)(m_pRuntime->plugin()->memory + local_addr);
|
||||
addr = (cell_t *)(memory_ + local_addr);
|
||||
cellcount = (*addr) * sizeof(cell_t);
|
||||
/* check if this memory count looks valid */
|
||||
if ((signed)(hp_ - cellcount - sizeof(cell_t)) != local_addr)
|
||||
@ -221,7 +251,7 @@ PluginContext::HeapPop(cell_t local_addr)
|
||||
int
|
||||
PluginContext::HeapRelease(cell_t local_addr)
|
||||
{
|
||||
if (local_addr < (cell_t)m_pRuntime->plugin()->data_size)
|
||||
if (local_addr < (cell_t)data_size_)
|
||||
return SP_ERROR_INVALID_ADDRESS;
|
||||
|
||||
hp_ = local_addr - sizeof(cell_t);
|
||||
@ -317,13 +347,13 @@ int
|
||||
PluginContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
|
||||
{
|
||||
if (((local_addr >= hp_) && (local_addr < sp_)) ||
|
||||
(local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
|
||||
(local_addr < 0) || ((ucell_t)local_addr >= mem_size_))
|
||||
{
|
||||
return SP_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
|
||||
if (phys_addr)
|
||||
*phys_addr = (cell_t *)(m_pRuntime->plugin()->memory + local_addr);
|
||||
*phys_addr = (cell_t *)(memory_ + local_addr);
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
@ -350,11 +380,11 @@ int
|
||||
PluginContext::LocalToString(cell_t local_addr, char **addr)
|
||||
{
|
||||
if (((local_addr >= hp_) && (local_addr < sp_)) ||
|
||||
(local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
|
||||
(local_addr < 0) || ((ucell_t)local_addr >= mem_size_))
|
||||
{
|
||||
return SP_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
*addr = (char *)(m_pRuntime->plugin()->memory + local_addr);
|
||||
*addr = (char *)(memory_ + local_addr);
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
@ -372,7 +402,7 @@ PluginContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source
|
||||
size_t len;
|
||||
|
||||
if (((local_addr >= hp_) && (local_addr < sp_)) ||
|
||||
(local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
|
||||
(local_addr < 0) || ((ucell_t)local_addr >= mem_size_))
|
||||
{
|
||||
return SP_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
@ -381,7 +411,7 @@ PluginContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source
|
||||
return SP_ERROR_NONE;
|
||||
|
||||
len = strlen(source);
|
||||
dest = (char *)(m_pRuntime->plugin()->memory + local_addr);
|
||||
dest = (char *)(memory_ + local_addr);
|
||||
|
||||
if (len >= bytes)
|
||||
len = bytes - 1;
|
||||
@ -436,7 +466,7 @@ PluginContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char
|
||||
|
||||
if (((local_addr >= hp_) && (local_addr < sp_)) ||
|
||||
(local_addr < 0) ||
|
||||
((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
|
||||
((ucell_t)local_addr >= mem_size_))
|
||||
{
|
||||
return SP_ERROR_INVALID_ADDRESS;
|
||||
}
|
||||
@ -445,7 +475,7 @@ PluginContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char
|
||||
return SP_ERROR_NONE;
|
||||
|
||||
len = strlen(source);
|
||||
dest = (char *)(m_pRuntime->plugin()->memory + local_addr);
|
||||
dest = (char *)(memory_ + local_addr);
|
||||
|
||||
if ((size_t)len >= maxbytes) {
|
||||
len = maxbytes - 1;
|
||||
@ -577,7 +607,7 @@ PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigne
|
||||
/* Push parameters */
|
||||
|
||||
sp_ -= sizeof(cell_t) * (num_params + 1);
|
||||
sp = (cell_t *)(m_pRuntime->plugin()->memory + sp_);
|
||||
sp = (cell_t *)(memory_ + sp_);
|
||||
|
||||
sp[0] = num_params;
|
||||
for (unsigned int i = 0; i < num_params; i++)
|
||||
@ -645,122 +675,6 @@ PluginContext::GetRuntime()
|
||||
return m_pRuntime;
|
||||
}
|
||||
|
||||
DebugInfo::DebugInfo(sp_plugin_t *plugin) : m_pPlugin(plugin)
|
||||
{
|
||||
}
|
||||
|
||||
#define USHR(x) ((unsigned int)(x)>>1)
|
||||
|
||||
int
|
||||
DebugInfo::LookupFile(ucell_t addr, const char **filename)
|
||||
{
|
||||
int high, low, mid;
|
||||
|
||||
high = m_pPlugin->debug.files_num;
|
||||
low = -1;
|
||||
|
||||
while (high - low > 1) {
|
||||
mid = USHR(low + high);
|
||||
if (m_pPlugin->debug.files[mid].addr <= addr)
|
||||
low = mid;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
|
||||
if (low == -1)
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
|
||||
*filename = m_pPlugin->debug.stringbase + m_pPlugin->debug.files[low].name;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
DebugInfo::LookupFunction(ucell_t addr, const char **name)
|
||||
{
|
||||
if (!m_pPlugin->debug.unpacked) {
|
||||
uint32_t max, iter;
|
||||
sp_fdbg_symbol_t *sym;
|
||||
uint8_t *cursor = (uint8_t *)(m_pPlugin->debug.symbols);
|
||||
|
||||
max = m_pPlugin->debug.syms_num;
|
||||
for (iter = 0; iter < max; iter++) {
|
||||
sym = (sp_fdbg_symbol_t *)cursor;
|
||||
|
||||
if (sym->ident == sp::IDENT_FUNCTION &&
|
||||
sym->codestart <= addr &&
|
||||
sym->codeend > addr)
|
||||
{
|
||||
*name = m_pPlugin->debug.stringbase + sym->name;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
if (sym->dimcount > 0) {
|
||||
cursor += sizeof(sp_fdbg_symbol_t);
|
||||
cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount;
|
||||
continue;
|
||||
}
|
||||
|
||||
cursor += sizeof(sp_fdbg_symbol_t);
|
||||
}
|
||||
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
} else {
|
||||
uint32_t max, iter;
|
||||
sp_u_fdbg_symbol_t *sym;
|
||||
uint8_t *cursor = (uint8_t *)(m_pPlugin->debug.symbols);
|
||||
|
||||
max = m_pPlugin->debug.syms_num;
|
||||
for (iter = 0; iter < max; iter++) {
|
||||
sym = (sp_u_fdbg_symbol_t *)cursor;
|
||||
|
||||
if (sym->ident == sp::IDENT_FUNCTION &&
|
||||
sym->codestart <= addr &&
|
||||
sym->codeend > addr)
|
||||
{
|
||||
*name = m_pPlugin->debug.stringbase + sym->name;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
if (sym->dimcount > 0) {
|
||||
cursor += sizeof(sp_u_fdbg_symbol_t);
|
||||
cursor += sizeof(sp_u_fdbg_arraydim_t) * sym->dimcount;
|
||||
continue;
|
||||
}
|
||||
|
||||
cursor += sizeof(sp_u_fdbg_symbol_t);
|
||||
}
|
||||
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
DebugInfo::LookupLine(ucell_t addr, uint32_t *line)
|
||||
{
|
||||
int high, low, mid;
|
||||
|
||||
high = m_pPlugin->debug.lines_num;
|
||||
low = -1;
|
||||
|
||||
while (high - low > 1) {
|
||||
mid = USHR(low + high);
|
||||
if (m_pPlugin->debug.lines[mid].addr <= addr)
|
||||
low = mid;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
|
||||
if (low == -1)
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
|
||||
/* Since the CIP occurs BEFORE the line, we have to add one */
|
||||
*line = m_pPlugin->debug.lines[low].line + 1;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
#undef USHR
|
||||
|
||||
int
|
||||
PluginContext::GetLastNativeError()
|
||||
{
|
||||
@ -770,7 +684,7 @@ PluginContext::GetLastNativeError()
|
||||
cell_t *
|
||||
PluginContext::GetLocalParams()
|
||||
{
|
||||
return (cell_t *)(m_pRuntime->plugin()->memory + frm_ + (2 * sizeof(cell_t)));
|
||||
return (cell_t *)(memory_ + frm_ + (2 * sizeof(cell_t)));
|
||||
}
|
||||
|
||||
void
|
||||
@ -809,7 +723,7 @@ PluginContext::popTrackerAndSetHeap()
|
||||
return SP_ERROR_TRACKER_BOUNDS;
|
||||
|
||||
ucell_t amt = *tracker_.pCur;
|
||||
if (amt > (hp_ - m_pRuntime->plugin()->data_size))
|
||||
if (amt > (hp_ - data_size_))
|
||||
return SP_ERROR_HEAPMIN;
|
||||
|
||||
hp_ -= amt;
|
||||
@ -846,7 +760,7 @@ PluginContext::invokeNative(ucell_t native_idx, cell_t *params)
|
||||
// Note: Invoke() saves the last native, so we don't need to here.
|
||||
last_native_ = native_idx;
|
||||
|
||||
sp_native_t *native = &m_pRuntime->plugin()->natives[native_idx];
|
||||
const sp_native_t *native = m_pRuntime->GetNative(native_idx);
|
||||
|
||||
if (native->status == SP_NATIVE_UNBOUND) {
|
||||
native_error_ = SP_ERROR_INVALID_NATIVE;
|
||||
@ -995,7 +909,7 @@ PluginContext::generateFullArray(uint32_t argc, cell_t *argv, int autozero)
|
||||
return SP_ERROR_ARRAY_TOO_BIG;
|
||||
|
||||
uint32_t new_hp = hp_ + bytes;
|
||||
cell_t *dat_hp = reinterpret_cast<cell_t *>(m_pRuntime->plugin()->memory + new_hp);
|
||||
cell_t *dat_hp = reinterpret_cast<cell_t *>(memory_ + new_hp);
|
||||
|
||||
// argv, coincidentally, is STK.
|
||||
if (dat_hp >= argv - STACK_MARGIN)
|
||||
@ -1004,7 +918,7 @@ PluginContext::generateFullArray(uint32_t argc, cell_t *argv, int autozero)
|
||||
if (int err = pushTracker(bytes))
|
||||
return err;
|
||||
|
||||
cell_t *base = reinterpret_cast<cell_t *>(m_pRuntime->plugin()->memory + hp_);
|
||||
cell_t *base = reinterpret_cast<cell_t *>(memory_ + hp_);
|
||||
cell_t offs = GenerateArrayIndirectionVectors(base, argv, argc, !!autozero);
|
||||
assert(size_t(offs) == cells);
|
||||
|
||||
@ -1025,14 +939,14 @@ PluginContext::generateArray(cell_t dims, cell_t *stk, bool autozero)
|
||||
uint32_t bytes = size * 4;
|
||||
|
||||
hp_ += bytes;
|
||||
if (uintptr_t(m_pRuntime->plugin()->memory + hp_) >= uintptr_t(stk))
|
||||
if (uintptr_t(memory_ + hp_) >= uintptr_t(stk))
|
||||
return SP_ERROR_HEAPLOW;
|
||||
|
||||
if (int err = pushTracker(bytes))
|
||||
return err;
|
||||
|
||||
if (autozero)
|
||||
memset(m_pRuntime->plugin()->memory + hp_, 0, bytes);
|
||||
memset(memory_ + hp_, 0, bytes);
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
@ -16,7 +16,8 @@
|
||||
#include "sp_vm_api.h"
|
||||
#include "scripted-invoker.h"
|
||||
#include "plugin-runtime.h"
|
||||
#include "jit_shared.h"
|
||||
|
||||
namespace sp {
|
||||
|
||||
struct HeapTracker
|
||||
{
|
||||
@ -30,12 +31,16 @@ struct HeapTracker
|
||||
ucell_t *pCur;
|
||||
};
|
||||
|
||||
static const size_t SP_MAX_RETURN_STACK = 1024;
|
||||
|
||||
class PluginContext : public IPluginContext
|
||||
{
|
||||
public:
|
||||
PluginContext(PluginRuntime *pRuntime);
|
||||
~PluginContext();
|
||||
|
||||
bool Initialize();
|
||||
|
||||
public: //IPluginContext
|
||||
IVirtualMachine *GetVirtualMachine();
|
||||
sp_context_t *GetContext();
|
||||
@ -84,6 +89,16 @@ class PluginContext : public IPluginContext
|
||||
void Refresh();
|
||||
void ClearLastNativeError();
|
||||
|
||||
size_t HeapSize() const {
|
||||
return mem_size_;
|
||||
}
|
||||
uint8_t *memory() const {
|
||||
return memory_;
|
||||
}
|
||||
size_t DataSize() const {
|
||||
return data_size_;
|
||||
}
|
||||
|
||||
public:
|
||||
bool IsInExec();
|
||||
|
||||
@ -108,6 +123,9 @@ class PluginContext : public IPluginContext
|
||||
static inline size_t offsetOfRuntime() {
|
||||
return offsetof(PluginContext, m_pRuntime);
|
||||
}
|
||||
static inline size_t offsetOfMemory() {
|
||||
return offsetof(PluginContext, memory_);
|
||||
}
|
||||
|
||||
int32_t *addressOfCip() {
|
||||
return &cip_;
|
||||
@ -163,13 +181,13 @@ class PluginContext : public IPluginContext
|
||||
}
|
||||
|
||||
inline bool checkAddress(cell_t *stk, cell_t addr) {
|
||||
if (uint32_t(addr) >= m_pRuntime->plugin()->mem_size)
|
||||
if (uint32_t(addr) >= mem_size_)
|
||||
return false;
|
||||
|
||||
if (addr < hp_)
|
||||
return true;
|
||||
|
||||
if (reinterpret_cast<cell_t *>(m_pRuntime->plugin()->memory + addr) < stk)
|
||||
if (reinterpret_cast<cell_t *>(memory_ + addr) < stk)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@ -180,12 +198,16 @@ class PluginContext : public IPluginContext
|
||||
void _SetErrorMessage(const char *msg, ...);
|
||||
|
||||
private:
|
||||
PluginRuntime *m_pRuntime;
|
||||
uint8_t *memory_;
|
||||
uint32_t data_size_;
|
||||
uint32_t mem_size_;
|
||||
|
||||
cell_t *m_pNullVec;
|
||||
cell_t *m_pNullString;
|
||||
char m_MsgCache[1024];
|
||||
bool m_CustomMsg;
|
||||
bool m_InExec;
|
||||
PluginRuntime *m_pRuntime;
|
||||
void *m_keys[4];
|
||||
bool m_keys_set[4];
|
||||
|
||||
@ -209,4 +231,6 @@ class PluginContext : public IPluginContext
|
||||
cell_t frm_;
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
|
||||
#endif //_INCLUDE_SOURCEPAWN_BASECONTEXT_H_
|
||||
|
@ -30,15 +30,16 @@ IsPointerCellAligned(void *p)
|
||||
return uintptr_t(p) % 4 == 0;
|
||||
}
|
||||
|
||||
PluginRuntime::PluginRuntime()
|
||||
: m_Debug(&m_plugin),
|
||||
m_PubFuncs(NULL),
|
||||
m_CompSerial(0)
|
||||
PluginRuntime::PluginRuntime(LegacyImage *image)
|
||||
: image_(image),
|
||||
paused_(false),
|
||||
computed_code_hash_(false),
|
||||
computed_data_hash_(false)
|
||||
{
|
||||
memset(&m_plugin, 0, sizeof(m_plugin));
|
||||
|
||||
memset(m_CodeHash, 0, sizeof(m_CodeHash));
|
||||
memset(m_DataHash, 0, sizeof(m_DataHash));
|
||||
code_ = image_->DescribeCode();
|
||||
data_ = image_->DescribeData();
|
||||
memset(code_hash_, 0, sizeof(code_hash_));
|
||||
memset(data_hash_, 0, sizeof(data_hash_));
|
||||
|
||||
ke::AutoLock lock(Environment::get()->lock());
|
||||
Environment::get()->RegisterRuntime(this);
|
||||
@ -54,19 +55,56 @@ PluginRuntime::~PluginRuntime()
|
||||
|
||||
Environment::get()->DeregisterRuntime(this);
|
||||
|
||||
for (uint32_t i = 0; i < m_plugin.num_publics; i++)
|
||||
delete m_PubFuncs[i];
|
||||
delete [] m_PubFuncs;
|
||||
for (uint32_t i = 0; i < image_->NumPublics(); i++)
|
||||
delete entrypoints_[i];
|
||||
|
||||
for (size_t i = 0; i < m_JitFunctions.length(); i++)
|
||||
delete m_JitFunctions[i];
|
||||
}
|
||||
|
||||
free(m_plugin.base);
|
||||
delete [] m_plugin.memory;
|
||||
delete [] m_plugin.publics;
|
||||
delete [] m_plugin.pubvars;
|
||||
delete [] m_plugin.natives;
|
||||
free(m_plugin.name);
|
||||
bool
|
||||
PluginRuntime::Initialize()
|
||||
{
|
||||
if (!ke::IsAligned(code_.bytes, sizeof(cell_t))) {
|
||||
// Align the code section.
|
||||
aligned_code_ = new uint8_t[code_.length];
|
||||
if (!aligned_code_)
|
||||
return false;
|
||||
|
||||
memcpy(aligned_code_, code_.bytes, code_.length);
|
||||
code_.bytes = aligned_code_;
|
||||
}
|
||||
|
||||
natives_ = new sp_native_t[image_->NumNatives()];
|
||||
if (!natives_)
|
||||
return false;
|
||||
memset(natives_, 0, sizeof(sp_native_t) * image_->NumNatives());
|
||||
|
||||
publics_ = new sp_public_t[image_->NumPublics()];
|
||||
if (!publics_)
|
||||
return false;
|
||||
memset(publics_, 0, sizeof(sp_public_t) * image_->NumPublics());
|
||||
|
||||
pubvars_ = new sp_pubvar_t[image_->NumPubvars()];
|
||||
if (!pubvars_)
|
||||
return false;
|
||||
memset(pubvars_, 0, sizeof(sp_pubvar_t) * image_->NumPubvars());
|
||||
|
||||
entrypoints_ = new ScriptedInvoker *[image_->NumPublics()];
|
||||
if (!entrypoints_)
|
||||
return false;
|
||||
memset(entrypoints_, 0, sizeof(ScriptedInvoker *) * image_->NumPublics());
|
||||
|
||||
context_ = new PluginContext(this);
|
||||
if (!context_->Initialize())
|
||||
return false;
|
||||
|
||||
SetupFloatNativeRemapping();
|
||||
|
||||
if (!function_map_.init(32))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
struct NativeMapping {
|
||||
@ -99,9 +137,9 @@ static const NativeMapping sNativeMap[] = {
|
||||
void
|
||||
PluginRuntime::SetupFloatNativeRemapping()
|
||||
{
|
||||
float_table_ = new floattbl_t[m_plugin.num_natives];
|
||||
for (size_t i = 0; i < m_plugin.num_natives; i++) {
|
||||
const char *name = m_plugin.natives[i].name;
|
||||
float_table_ = new floattbl_t[image_->NumNatives()];
|
||||
for (size_t i = 0; i < image_->NumNatives(); i++) {
|
||||
const char *name = image_->GetNative(i);
|
||||
const NativeMapping *iter = sNativeMap;
|
||||
while (iter->name) {
|
||||
if (strcmp(name, iter->name) == 0) {
|
||||
@ -125,175 +163,17 @@ PluginRuntime::GetNativeReplacement(size_t index)
|
||||
void
|
||||
PluginRuntime::SetName(const char *name)
|
||||
{
|
||||
m_plugin.name = strdup(name);
|
||||
size_t len = strlen(name);
|
||||
name_ = new char[len + 1];
|
||||
strcpy(name_, name);
|
||||
}
|
||||
|
||||
static cell_t InvalidNative(IPluginContext *pCtx, const cell_t *params)
|
||||
static cell_t
|
||||
InvalidNative(IPluginContext *pCtx, const cell_t *params)
|
||||
{
|
||||
return pCtx->ThrowNativeErrorEx(SP_ERROR_INVALID_NATIVE, "Invalid native");
|
||||
}
|
||||
|
||||
int PluginRuntime::CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base)
|
||||
{
|
||||
char *nameptr;
|
||||
uint8_t sectnum = 0;
|
||||
sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
|
||||
|
||||
memset(&m_plugin, 0, sizeof(m_plugin));
|
||||
|
||||
m_plugin.base = base;
|
||||
m_plugin.base_size = hdr->imagesize;
|
||||
|
||||
if (hdr->version == 0x0101)
|
||||
m_plugin.debug.unpacked = true;
|
||||
|
||||
/* We have to read the name section first */
|
||||
for (sectnum = 0; sectnum < hdr->sections; sectnum++) {
|
||||
nameptr = (char *)(base + hdr->stringtab + secptr[sectnum].nameoffs);
|
||||
if (strcmp(nameptr, ".names") == 0) {
|
||||
m_plugin.stringbase = (const char *)(base + secptr[sectnum].dataoffs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
sectnum = 0;
|
||||
|
||||
/* Now read the rest of the sections */
|
||||
while (sectnum < hdr->sections) {
|
||||
nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
|
||||
|
||||
if (!(m_plugin.pcode) && !strcmp(nameptr, ".code")) {
|
||||
sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
|
||||
|
||||
if (cod->codeversion < SmxConsts::CODE_VERSION_SP1_MIN)
|
||||
return SP_ERROR_CODE_TOO_OLD;
|
||||
if (cod->codeversion > SmxConsts::CODE_VERSION_SP1_MAX)
|
||||
return SP_ERROR_CODE_TOO_NEW;
|
||||
|
||||
m_plugin.pcode = base + secptr->dataoffs + cod->code;
|
||||
m_plugin.pcode_size = cod->codesize;
|
||||
m_plugin.flags = cod->flags;
|
||||
m_plugin.pcode_version = cod->codeversion;
|
||||
if (!IsPointerCellAligned(m_plugin.pcode)) {
|
||||
// The JIT requires that pcode is cell-aligned, so if it's not, we
|
||||
// remap the code segment to a new address.
|
||||
alt_pcode_ = new uint8_t[m_plugin.pcode_size];
|
||||
memcpy(alt_pcode_, m_plugin.pcode, m_plugin.pcode_size);
|
||||
assert(IsPointerCellAligned(alt_pcode_));
|
||||
|
||||
m_plugin.pcode = alt_pcode_;
|
||||
}
|
||||
} else if (!(m_plugin.data) && !strcmp(nameptr, ".data")) {
|
||||
sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs);
|
||||
m_plugin.data = base + secptr->dataoffs + dat->data;
|
||||
m_plugin.data_size = dat->datasize;
|
||||
m_plugin.mem_size = dat->memsize;
|
||||
m_plugin.memory = new uint8_t[m_plugin.mem_size];
|
||||
memcpy(m_plugin.memory, m_plugin.data, m_plugin.data_size);
|
||||
} else if ((m_plugin.publics == NULL) && !strcmp(nameptr, ".publics")) {
|
||||
sp_file_publics_t *publics;
|
||||
|
||||
publics = (sp_file_publics_t *)(base + secptr->dataoffs);
|
||||
m_plugin.num_publics = secptr->size / sizeof(sp_file_publics_t);
|
||||
|
||||
if (m_plugin.num_publics > 0) {
|
||||
m_plugin.publics = new sp_public_t[m_plugin.num_publics];
|
||||
|
||||
for (uint32_t i = 0; i < m_plugin.num_publics; i++) {
|
||||
m_plugin.publics[i].code_offs = publics[i].address;
|
||||
m_plugin.publics[i].funcid = (i << 1) | 1;
|
||||
m_plugin.publics[i].name = m_plugin.stringbase + publics[i].name;
|
||||
}
|
||||
}
|
||||
} else if ((m_plugin.pubvars == NULL) && !strcmp(nameptr, ".pubvars")) {
|
||||
sp_file_pubvars_t *pubvars;
|
||||
|
||||
pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
|
||||
m_plugin.num_pubvars = secptr->size / sizeof(sp_file_pubvars_t);
|
||||
|
||||
if (m_plugin.num_pubvars > 0) {
|
||||
m_plugin.pubvars = new sp_pubvar_t[m_plugin.num_pubvars];
|
||||
|
||||
for (uint32_t i = 0; i < m_plugin.num_pubvars; i++) {
|
||||
m_plugin.pubvars[i].name = m_plugin.stringbase + pubvars[i].name;
|
||||
m_plugin.pubvars[i].offs = (cell_t *)(m_plugin.memory + pubvars[i].address);
|
||||
}
|
||||
}
|
||||
} else if ((m_plugin.natives == NULL) && !strcmp(nameptr, ".natives")) {
|
||||
sp_file_natives_t *natives;
|
||||
|
||||
natives = (sp_file_natives_t *)(base + secptr->dataoffs);
|
||||
m_plugin.num_natives = secptr->size / sizeof(sp_file_natives_t);
|
||||
|
||||
if (m_plugin.num_natives > 0) {
|
||||
m_plugin.natives = new sp_native_t[m_plugin.num_natives];
|
||||
|
||||
for (uint32_t i = 0; i < m_plugin.num_natives; i++) {
|
||||
m_plugin.natives[i].flags = 0;
|
||||
m_plugin.natives[i].pfn = InvalidNative;
|
||||
m_plugin.natives[i].status = SP_NATIVE_UNBOUND;
|
||||
m_plugin.natives[i].user = NULL;
|
||||
m_plugin.natives[i].name = m_plugin.stringbase + natives[i].name;
|
||||
}
|
||||
}
|
||||
} else if (!(m_plugin.debug.files) && !strcmp(nameptr, ".dbg.files")) {
|
||||
m_plugin.debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
|
||||
} else if (!(m_plugin.debug.lines) && !strcmp(nameptr, ".dbg.lines")) {
|
||||
m_plugin.debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
|
||||
} else if (!(m_plugin.debug.symbols) && !strcmp(nameptr, ".dbg.symbols")) {
|
||||
m_plugin.debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
|
||||
} else if (!(m_plugin.debug.lines_num) && !strcmp(nameptr, ".dbg.info")) {
|
||||
sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs);
|
||||
m_plugin.debug.files_num = inf->num_files;
|
||||
m_plugin.debug.lines_num = inf->num_lines;
|
||||
m_plugin.debug.syms_num = inf->num_syms;
|
||||
} else if (!(m_plugin.debug.stringbase) && !strcmp(nameptr, ".dbg.strings")) {
|
||||
m_plugin.debug.stringbase = (const char *)(base + secptr->dataoffs);
|
||||
} else if (strcmp(nameptr, ".dbg.natives") == 0) {
|
||||
m_plugin.debug.unpacked = false;
|
||||
}
|
||||
|
||||
secptr++;
|
||||
sectnum++;
|
||||
}
|
||||
|
||||
if (m_plugin.pcode == NULL || m_plugin.data == NULL)
|
||||
return SP_ERROR_FILE_FORMAT;
|
||||
|
||||
if ((m_plugin.flags & sp::CODEFLAG_DEBUG) && (
|
||||
!(m_plugin.debug.files) ||
|
||||
!(m_plugin.debug.lines) ||
|
||||
!(m_plugin.debug.symbols) ||
|
||||
!(m_plugin.debug.stringbase) ))
|
||||
{
|
||||
return SP_ERROR_FILE_FORMAT;
|
||||
}
|
||||
|
||||
if (m_plugin.num_publics > 0) {
|
||||
m_PubFuncs = new ScriptedInvoker *[m_plugin.num_publics];
|
||||
memset(m_PubFuncs, 0, sizeof(ScriptedInvoker *) * m_plugin.num_publics);
|
||||
}
|
||||
|
||||
MD5 md5_pcode;
|
||||
md5_pcode.update(m_plugin.pcode, m_plugin.pcode_size);
|
||||
md5_pcode.finalize();
|
||||
md5_pcode.raw_digest(m_CodeHash);
|
||||
|
||||
MD5 md5_data;
|
||||
md5_data.update(m_plugin.data, m_plugin.data_size);
|
||||
md5_data.finalize();
|
||||
md5_data.raw_digest(m_DataHash);
|
||||
|
||||
m_pCtx = new PluginContext(this);
|
||||
|
||||
SetupFloatNativeRemapping();
|
||||
|
||||
if (!function_map_.init(32))
|
||||
return SP_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
PluginRuntime::AddJittedFunction(CompiledFunction *fn)
|
||||
{
|
||||
@ -318,15 +198,14 @@ PluginRuntime::GetJittedFunctionByOffset(cell_t pcode_offset)
|
||||
int
|
||||
PluginRuntime::FindNativeByName(const char *name, uint32_t *index)
|
||||
{
|
||||
for (uint32_t i=0; i<m_plugin.num_natives; i++) {
|
||||
if (strcmp(m_plugin.natives[i].name, name) == 0) {
|
||||
if (index)
|
||||
*index = i;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
}
|
||||
size_t idx;
|
||||
if (!image_->FindNative(name, &idx))
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
if (index)
|
||||
*index = idx;
|
||||
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
@ -338,10 +217,10 @@ PluginRuntime::GetNativeByIndex(uint32_t index, sp_native_t **native)
|
||||
int
|
||||
PluginRuntime::UpdateNativeBinding(uint32_t index, SPVM_NATIVE_FUNC pfn, uint32_t flags, void *data)
|
||||
{
|
||||
if (index >= m_plugin.num_natives)
|
||||
if (index >= image_->NumNatives())
|
||||
return SP_ERROR_INDEX;
|
||||
|
||||
sp_native_t *native = &m_plugin.natives[index];
|
||||
sp_native_t *native = &natives_[index];
|
||||
|
||||
native->pfn = pfn;
|
||||
native->status = pfn
|
||||
@ -355,127 +234,120 @@ PluginRuntime::UpdateNativeBinding(uint32_t index, SPVM_NATIVE_FUNC pfn, uint32_
|
||||
const sp_native_t *
|
||||
PluginRuntime::GetNative(uint32_t index)
|
||||
{
|
||||
if (index >= m_plugin.num_natives)
|
||||
if (index >= image_->NumNatives())
|
||||
return nullptr;
|
||||
return &m_plugin.natives[index];
|
||||
|
||||
if (!natives_[index].name)
|
||||
natives_[index].name = image_->GetNative(index);
|
||||
|
||||
return &natives_[index];
|
||||
}
|
||||
|
||||
uint32_t
|
||||
PluginRuntime::GetNativesNum()
|
||||
{
|
||||
return m_plugin.num_natives;
|
||||
return image_->NumNatives();
|
||||
}
|
||||
|
||||
int
|
||||
PluginRuntime::FindPublicByName(const char *name, uint32_t *index)
|
||||
{
|
||||
int diff, high, low;
|
||||
uint32_t mid;
|
||||
size_t idx;
|
||||
if (!image_->FindPublic(name, &idx))
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
|
||||
high = m_plugin.num_publics - 1;
|
||||
low = 0;
|
||||
|
||||
while (low <= high) {
|
||||
mid = (low + high) / 2;
|
||||
diff = strcmp(m_plugin.publics[mid].name, name);
|
||||
if (diff == 0) {
|
||||
if (index)
|
||||
*index = mid;
|
||||
return SP_ERROR_NONE;
|
||||
} else if (diff < 0) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
if (index)
|
||||
*index = idx;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
PluginRuntime::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
|
||||
PluginRuntime::GetPublicByIndex(uint32_t index, sp_public_t **out)
|
||||
{
|
||||
if (index >= m_plugin.num_publics)
|
||||
if (index >= image_->NumPublics())
|
||||
return SP_ERROR_INDEX;
|
||||
|
||||
if (pblic)
|
||||
*pblic = &(m_plugin.publics[index]);
|
||||
sp_public_t &entry = publics_[index];
|
||||
if (!entry.name) {
|
||||
uint32_t offset;
|
||||
image_->GetPublic(index, &offset, &entry.name);
|
||||
entry.code_offs = offset;
|
||||
entry.funcid = (index << 1) | 1;
|
||||
}
|
||||
|
||||
if (out)
|
||||
*out = &entry;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
PluginRuntime::GetPublicsNum()
|
||||
{
|
||||
return m_plugin.num_publics;
|
||||
return image_->NumPublics();
|
||||
}
|
||||
|
||||
int
|
||||
PluginRuntime::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
|
||||
PluginRuntime::GetPubvarByIndex(uint32_t index, sp_pubvar_t **out)
|
||||
{
|
||||
if (index >= m_plugin.num_pubvars)
|
||||
if (index >= image_->NumPubvars())
|
||||
return SP_ERROR_INDEX;
|
||||
|
||||
if (pubvar)
|
||||
*pubvar = &(m_plugin.pubvars[index]);
|
||||
sp_pubvar_t *pubvar = &pubvars_[index];
|
||||
if (!pubvar->name) {
|
||||
uint32_t offset;
|
||||
image_->GetPubvar(index, &offset, &pubvar->name);
|
||||
if (int err = context_->LocalToPhysAddr(offset, &pubvar->offs))
|
||||
return err;
|
||||
}
|
||||
|
||||
if (out)
|
||||
*out = pubvar;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
PluginRuntime::FindPubvarByName(const char *name, uint32_t *index)
|
||||
{
|
||||
int diff, high, low;
|
||||
uint32_t mid;
|
||||
size_t idx;
|
||||
if (!image_->FindPubvar(name, &idx))
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
|
||||
high = m_plugin.num_pubvars - 1;
|
||||
low = 0;
|
||||
|
||||
while (low <= high) {
|
||||
mid = (low + high) / 2;
|
||||
diff = strcmp(m_plugin.pubvars[mid].name, name);
|
||||
if (diff == 0) {
|
||||
if (index)
|
||||
*index = mid;
|
||||
return SP_ERROR_NONE;
|
||||
} else if (diff < 0) {
|
||||
low = mid + 1;
|
||||
} else {
|
||||
high = mid - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
if (index)
|
||||
*index = idx;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
PluginRuntime::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
|
||||
{
|
||||
if (index >= m_plugin.num_pubvars)
|
||||
if (index >= image_->NumPubvars())
|
||||
return SP_ERROR_INDEX;
|
||||
|
||||
*local_addr = (uint8_t *)m_plugin.pubvars[index].offs - m_plugin.memory;
|
||||
*phys_addr = m_plugin.pubvars[index].offs;
|
||||
uint32_t offset;
|
||||
image_->GetPubvar(index, &offset, nullptr);
|
||||
|
||||
if (int err = context_->LocalToPhysAddr(offset, phys_addr))
|
||||
return err;
|
||||
*local_addr = offset;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
PluginRuntime::GetPubVarsNum()
|
||||
{
|
||||
return m_plugin.num_pubvars;
|
||||
return image_->NumPubvars();
|
||||
}
|
||||
|
||||
IPluginContext *
|
||||
PluginRuntime::GetDefaultContext()
|
||||
{
|
||||
return m_pCtx;
|
||||
return context_;
|
||||
}
|
||||
|
||||
IPluginDebugInfo *
|
||||
PluginRuntime::GetDebugInfo()
|
||||
{
|
||||
return &m_Debug;
|
||||
return this;
|
||||
}
|
||||
|
||||
IPluginFunction *
|
||||
@ -485,12 +357,12 @@ PluginRuntime::GetFunctionById(funcid_t func_id)
|
||||
|
||||
if (func_id & 1) {
|
||||
func_id >>= 1;
|
||||
if (func_id >= m_plugin.num_publics)
|
||||
if (func_id >= image_->NumPublics())
|
||||
return NULL;
|
||||
pFunc = m_PubFuncs[func_id];
|
||||
pFunc = entrypoints_[func_id];
|
||||
if (!pFunc) {
|
||||
m_PubFuncs[func_id] = new ScriptedInvoker(this, (func_id << 1) | 1, func_id);
|
||||
pFunc = m_PubFuncs[func_id];
|
||||
entrypoints_[func_id] = new ScriptedInvoker(this, (func_id << 1) | 1, func_id);
|
||||
pFunc = entrypoints_[func_id];
|
||||
}
|
||||
}
|
||||
|
||||
@ -500,13 +372,14 @@ PluginRuntime::GetFunctionById(funcid_t func_id)
|
||||
ScriptedInvoker *
|
||||
PluginRuntime::GetPublicFunction(size_t index)
|
||||
{
|
||||
ScriptedInvoker *pFunc = m_PubFuncs[index];
|
||||
assert(index < image_->NumPublics());
|
||||
ScriptedInvoker *pFunc = entrypoints_[index];
|
||||
if (!pFunc) {
|
||||
sp_public_t *pub = NULL;
|
||||
GetPublicByIndex(index, &pub);
|
||||
if (pub)
|
||||
m_PubFuncs[index] = new ScriptedInvoker(this, (index << 1) | 1, index);
|
||||
pFunc = m_PubFuncs[index];
|
||||
entrypoints_[index] = new ScriptedInvoker(this, (index << 1) | 1, index);
|
||||
pFunc = entrypoints_[index];
|
||||
}
|
||||
|
||||
return pFunc;
|
||||
@ -523,53 +396,64 @@ PluginRuntime::GetFunctionByName(const char *public_name)
|
||||
return GetPublicFunction(index);
|
||||
}
|
||||
|
||||
bool PluginRuntime::IsDebugging()
|
||||
bool
|
||||
PluginRuntime::IsDebugging()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void PluginRuntime::SetPauseState(bool paused)
|
||||
void
|
||||
PluginRuntime::SetPauseState(bool paused)
|
||||
{
|
||||
if (paused)
|
||||
{
|
||||
m_plugin.run_flags |= SPFLAG_PLUGIN_PAUSED;
|
||||
paused_ = paused;
|
||||
}
|
||||
|
||||
bool
|
||||
PluginRuntime::IsPaused()
|
||||
{
|
||||
return paused_;
|
||||
}
|
||||
|
||||
size_t
|
||||
PluginRuntime::GetMemUsage()
|
||||
{
|
||||
return sizeof(*this) +
|
||||
sizeof(PluginContext) +
|
||||
image_->ImageSize() +
|
||||
(aligned_code_ ? code_.length : 0) +
|
||||
context_->HeapSize();
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
PluginRuntime::GetCodeHash()
|
||||
{
|
||||
if (!computed_code_hash_) {
|
||||
MD5 md5_pcode;
|
||||
md5_pcode.update((const unsigned char *)code_.bytes, code_.length);
|
||||
md5_pcode.finalize();
|
||||
md5_pcode.raw_digest(code_hash_);
|
||||
computed_code_hash_ = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_plugin.run_flags &= ~SPFLAG_PLUGIN_PAUSED;
|
||||
return code_hash_;
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
PluginRuntime::GetDataHash()
|
||||
{
|
||||
if (!computed_data_hash_) {
|
||||
MD5 md5_data;
|
||||
md5_data.update((const unsigned char *)data_.bytes, data_.length);
|
||||
md5_data.finalize();
|
||||
md5_data.raw_digest(data_hash_);
|
||||
computed_data_hash_ = true;
|
||||
}
|
||||
return data_hash_;
|
||||
}
|
||||
|
||||
bool PluginRuntime::IsPaused()
|
||||
PluginContext *
|
||||
PluginRuntime::GetBaseContext()
|
||||
{
|
||||
return ((m_plugin.run_flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED);
|
||||
}
|
||||
|
||||
size_t PluginRuntime::GetMemUsage()
|
||||
{
|
||||
size_t mem = 0;
|
||||
|
||||
mem += sizeof(this);
|
||||
mem += sizeof(sp_plugin_t);
|
||||
mem += sizeof(PluginContext);
|
||||
mem += m_plugin.base_size;
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
unsigned char *PluginRuntime::GetCodeHash()
|
||||
{
|
||||
return m_CodeHash;
|
||||
}
|
||||
|
||||
unsigned char *PluginRuntime::GetDataHash()
|
||||
{
|
||||
return m_DataHash;
|
||||
}
|
||||
|
||||
PluginContext *PluginRuntime::GetBaseContext()
|
||||
{
|
||||
return m_pCtx;
|
||||
return context_;
|
||||
}
|
||||
|
||||
int
|
||||
@ -579,18 +463,31 @@ PluginRuntime::ApplyCompilationOptions(ICompilation *co)
|
||||
}
|
||||
|
||||
int
|
||||
PluginRuntime::CreateBlank(uint32_t heastk)
|
||||
PluginRuntime::LookupLine(ucell_t addr, uint32_t *line)
|
||||
{
|
||||
memset(&m_plugin, 0, sizeof(m_plugin));
|
||||
|
||||
/* Align to cell_t bytes */
|
||||
heastk += sizeof(cell_t);
|
||||
heastk -= heastk % sizeof(cell_t);
|
||||
|
||||
m_plugin.mem_size = heastk;
|
||||
m_plugin.memory = new uint8_t[heastk];
|
||||
|
||||
m_pCtx = new PluginContext(this);
|
||||
|
||||
if (!image_->LookupLine(addr, line))
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
PluginRuntime::LookupFunction(ucell_t addr, const char **out)
|
||||
{
|
||||
const char *name = image_->LookupFunction(addr);
|
||||
if (!name)
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
if (out)
|
||||
*out = name;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
||||
int
|
||||
PluginRuntime::LookupFile(ucell_t addr, const char **out)
|
||||
{
|
||||
const char *name = image_->LookupFile(addr);
|
||||
if (!name)
|
||||
return SP_ERROR_NOT_FOUND;
|
||||
if (out)
|
||||
*out = name;
|
||||
return SP_ERROR_NONE;
|
||||
}
|
||||
|
@ -17,24 +17,14 @@
|
||||
#include <am-vector.h>
|
||||
#include <am-inlinelist.h>
|
||||
#include <am-hashmap.h>
|
||||
#include "jit_shared.h"
|
||||
#include "compiled-function.h"
|
||||
#include "scripted-invoker.h"
|
||||
#include "legacy-image.h"
|
||||
|
||||
namespace sp {
|
||||
|
||||
class PluginContext;
|
||||
|
||||
class DebugInfo : public IPluginDebugInfo
|
||||
{
|
||||
public:
|
||||
DebugInfo(sp_plugin_t *plugin);
|
||||
public:
|
||||
int LookupFile(ucell_t addr, const char **filename);
|
||||
int LookupFunction(ucell_t addr, const char **name);
|
||||
int LookupLine(ucell_t addr, uint32_t *line);
|
||||
private:
|
||||
sp_plugin_t *m_pPlugin;
|
||||
};
|
||||
|
||||
struct floattbl_t
|
||||
{
|
||||
floattbl_t() {
|
||||
@ -48,15 +38,16 @@ struct floattbl_t
|
||||
/* Jit wants fast access to this so we expose things as public */
|
||||
class PluginRuntime
|
||||
: public SourcePawn::IPluginRuntime,
|
||||
public SourcePawn::IPluginDebugInfo,
|
||||
public ke::InlineListNode<PluginRuntime>
|
||||
{
|
||||
public:
|
||||
PluginRuntime();
|
||||
PluginRuntime(LegacyImage *image);
|
||||
~PluginRuntime();
|
||||
|
||||
bool Initialize();
|
||||
|
||||
public:
|
||||
virtual int CreateBlank(uint32_t heastk);
|
||||
virtual int CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base);
|
||||
virtual bool IsDebugging();
|
||||
virtual IPluginDebugInfo *GetDebugInfo();
|
||||
virtual int FindNativeByName(const char *name, uint32_t *index);
|
||||
@ -85,11 +76,11 @@ class PluginRuntime
|
||||
ScriptedInvoker *GetPublicFunction(size_t index);
|
||||
int UpdateNativeBinding(uint32_t index, SPVM_NATIVE_FUNC pfn, uint32_t flags, void *data) KE_OVERRIDE;
|
||||
const sp_native_t *GetNative(uint32_t index) KE_OVERRIDE;
|
||||
int LookupLine(ucell_t addr, uint32_t *line) KE_OVERRIDE;
|
||||
int LookupFunction(ucell_t addr, const char **name) KE_OVERRIDE;
|
||||
int LookupFile(ucell_t addr, const char **filename) KE_OVERRIDE;
|
||||
|
||||
PluginContext *GetBaseContext();
|
||||
const sp_plugin_t *plugin() const {
|
||||
return &m_plugin;
|
||||
}
|
||||
|
||||
size_t NumJitFunctions() const {
|
||||
return m_JitFunctions.length();
|
||||
@ -97,18 +88,43 @@ class PluginRuntime
|
||||
CompiledFunction *GetJitFunction(size_t i) const {
|
||||
return m_JitFunctions[i];
|
||||
}
|
||||
const char *Name() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
static inline size_t offsetToPlugin() {
|
||||
return offsetof(PluginRuntime, m_plugin);
|
||||
return 0x0fff0000;
|
||||
}
|
||||
|
||||
public:
|
||||
typedef LegacyImage::Code Code;
|
||||
typedef LegacyImage::Data Data;
|
||||
|
||||
const Code &code() const {
|
||||
return code_;
|
||||
}
|
||||
const Data &data() const {
|
||||
return data_;
|
||||
}
|
||||
LegacyImage *image() const {
|
||||
return image_;
|
||||
}
|
||||
|
||||
private:
|
||||
void SetupFloatNativeRemapping();
|
||||
|
||||
private:
|
||||
sp_plugin_t m_plugin;
|
||||
ke::AutoArray<uint8_t> alt_pcode_;
|
||||
ke::AutoPtr<sp::LegacyImage> image_;
|
||||
ke::AutoArray<uint8_t> aligned_code_;
|
||||
ke::AutoArray<floattbl_t> float_table_;
|
||||
ke::AutoArray<char> name_;
|
||||
Code code_;
|
||||
Data data_;
|
||||
ke::AutoArray<sp_native_t> natives_;
|
||||
ke::AutoArray<sp_public_t> publics_;
|
||||
ke::AutoArray<sp_pubvar_t> pubvars_;
|
||||
ke::AutoArray<ScriptedInvoker *> entrypoints_;
|
||||
ke::AutoPtr<PluginContext> context_;
|
||||
|
||||
struct FunctionMapPolicy {
|
||||
static inline uint32_t hash(ucell_t value) {
|
||||
@ -123,17 +139,17 @@ class PluginRuntime
|
||||
FunctionMap function_map_;
|
||||
ke::Vector<CompiledFunction *> m_JitFunctions;
|
||||
|
||||
public:
|
||||
DebugInfo m_Debug;
|
||||
ke::AutoPtr<PluginContext> m_pCtx;
|
||||
ScriptedInvoker **m_PubFuncs;
|
||||
// Pause state.
|
||||
bool paused_;
|
||||
|
||||
public:
|
||||
unsigned int m_CompSerial;
|
||||
|
||||
unsigned char m_CodeHash[16];
|
||||
unsigned char m_DataHash[16];
|
||||
// Checksumming.
|
||||
bool computed_code_hash_;
|
||||
bool computed_data_hash_;
|
||||
unsigned char code_hash_[16];
|
||||
unsigned char data_hash_[16];
|
||||
};
|
||||
|
||||
} // sp
|
||||
|
||||
#endif //_INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_
|
||||
|
||||
|
@ -20,6 +20,9 @@
|
||||
* FUNCTION CALLING *
|
||||
********************/
|
||||
|
||||
using namespace sp;
|
||||
using namespace SourcePawn;
|
||||
|
||||
ScriptedInvoker::~ScriptedInvoker()
|
||||
{
|
||||
delete [] full_name_;
|
||||
@ -59,11 +62,11 @@ ScriptedInvoker::ScriptedInvoker(PluginRuntime *runtime, funcid_t id, uint32_t p
|
||||
|
||||
runtime->GetPublicByIndex(pub_id, &public_);
|
||||
|
||||
size_t rt_len = strlen(runtime->plugin()->name);
|
||||
size_t rt_len = strlen(runtime->Name());
|
||||
size_t len = rt_len + strlen("::") + strlen(public_->name);
|
||||
|
||||
full_name_ = new char[len + 1];
|
||||
strcpy(full_name_, runtime->plugin()->name);
|
||||
strcpy(full_name_, runtime->Name());
|
||||
strcpy(&full_name_[rt_len], "::");
|
||||
strcpy(&full_name_[rt_len + 2], public_->name);
|
||||
}
|
||||
|
@ -15,13 +15,12 @@
|
||||
|
||||
#include <sp_vm_api.h>
|
||||
|
||||
class PluginRuntime;
|
||||
namespace sp {
|
||||
|
||||
using namespace SourcePawn;
|
||||
|
||||
namespace sp {
|
||||
class PluginRuntime;
|
||||
class CompiledFunction;
|
||||
}
|
||||
|
||||
struct ParamInfo
|
||||
{
|
||||
@ -37,8 +36,6 @@ struct ParamInfo
|
||||
} str;
|
||||
};
|
||||
|
||||
class CPlugin;
|
||||
|
||||
class ScriptedInvoker : public IPluginFunction
|
||||
{
|
||||
public:
|
||||
@ -74,10 +71,10 @@ class ScriptedInvoker : public IPluginFunction
|
||||
return public_;
|
||||
}
|
||||
|
||||
sp::CompiledFunction *cachedCompiledFunction() const {
|
||||
CompiledFunction *cachedCompiledFunction() const {
|
||||
return cc_function_;
|
||||
}
|
||||
void setCachedCompiledFunction(sp::CompiledFunction *fn) {
|
||||
void setCachedCompiledFunction(CompiledFunction *fn) {
|
||||
cc_function_ = fn;
|
||||
}
|
||||
|
||||
@ -94,7 +91,9 @@ class ScriptedInvoker : public IPluginFunction
|
||||
funcid_t m_FnId;
|
||||
char *full_name_;
|
||||
sp_public_t *public_;
|
||||
sp::CompiledFunction *cc_function_;
|
||||
CompiledFunction *cc_function_;
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
|
||||
#endif //_INCLUDE_SOURCEMOD_BASEFUNCTION_H_
|
||||
|
617
sourcepawn/jit/smx-v1-image.cpp
Normal file
617
sourcepawn/jit/smx-v1-image.cpp
Normal file
@ -0,0 +1,617 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2004-2015 AlliedModers LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is licensed under the GNU
|
||||
// General Public License, version 3.0 (GPL). If a copy of the GPL was not
|
||||
// provided with this file, you can obtain it here:
|
||||
// http://www.gnu.org/licenses/gpl.html
|
||||
//
|
||||
#include "smx-v1-image.h"
|
||||
#include "zlib/zlib.h"
|
||||
|
||||
using namespace ke;
|
||||
using namespace sp;
|
||||
|
||||
SmxV1Image::SmxV1Image(FILE *fp)
|
||||
: FileReader(fp),
|
||||
hdr_(nullptr),
|
||||
header_strings_(nullptr),
|
||||
names_section_(nullptr),
|
||||
names_(nullptr),
|
||||
debug_names_section_(nullptr),
|
||||
debug_names_(nullptr),
|
||||
debug_syms_(nullptr),
|
||||
debug_syms_unpacked_(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
// Validating SMX v1 scripts is fairly expensive. We reserve real validation
|
||||
// for v2.
|
||||
bool
|
||||
SmxV1Image::validate()
|
||||
{
|
||||
if (length_ < sizeof(sp_file_hdr_t))
|
||||
return error("bad header");
|
||||
|
||||
hdr_ = (sp_file_hdr_t *)buffer();
|
||||
if (hdr_->magic != SmxConsts::FILE_MAGIC)
|
||||
return error("bad header");
|
||||
|
||||
switch (hdr_->version) {
|
||||
case SmxConsts::SP1_VERSION_1_0:
|
||||
case SmxConsts::SP1_VERSION_1_1:
|
||||
case SmxConsts::SP1_VERSION_1_7:
|
||||
break;
|
||||
default:
|
||||
return error("unsupported version");
|
||||
}
|
||||
|
||||
switch (hdr_->compression) {
|
||||
case SmxConsts::FILE_COMPRESSION_GZ:
|
||||
{
|
||||
// The start of the compression cannot be larger than the file.
|
||||
if (hdr_->dataoffs > length_)
|
||||
return error("illegal compressed region");
|
||||
|
||||
// The compressed region must start after the header.
|
||||
if (hdr_->dataoffs < sizeof(sp_file_hdr_t))
|
||||
return error("illegal compressed region");
|
||||
|
||||
// The full size of the image must be at least as large as the start
|
||||
// of the compressed region.
|
||||
if (hdr_->imagesize < hdr_->dataoffs)
|
||||
return error("illegal image size");
|
||||
|
||||
// Allocate the uncompressed image buffer.
|
||||
uint32_t compressedSize = hdr_->disksize - hdr_->dataoffs;
|
||||
AutoArray<uint8_t> uncompressed(new uint8_t[hdr_->imagesize]);
|
||||
if (!uncompressed)
|
||||
return error("out of memory");
|
||||
|
||||
// Decompress.
|
||||
const uint8_t *src = buffer() + hdr_->dataoffs;
|
||||
uint8_t *dest = (uint8_t *)uncompressed + hdr_->dataoffs;
|
||||
uLongf destlen = hdr_->imagesize - hdr_->dataoffs;
|
||||
int rv = uncompress(
|
||||
(Bytef *)dest,
|
||||
&destlen,
|
||||
src,
|
||||
compressedSize);
|
||||
if (rv != Z_OK)
|
||||
return error("could not decode compressed region");
|
||||
|
||||
// Copy the initial uncompressed region back in.
|
||||
memcpy((uint8_t *)uncompressed, buffer(), hdr_->dataoffs);
|
||||
|
||||
// Replace the original buffer.
|
||||
length_ = hdr_->imagesize;
|
||||
buffer_ = uncompressed.take();
|
||||
hdr_ = (sp_file_hdr_t *)buffer();
|
||||
break;
|
||||
}
|
||||
|
||||
case SmxConsts::FILE_COMPRESSION_NONE:
|
||||
break;
|
||||
|
||||
default:
|
||||
return error("unknown compression type");
|
||||
}
|
||||
|
||||
// Validate the string table.
|
||||
if (hdr_->stringtab >= length_)
|
||||
return error("invalid string table");
|
||||
header_strings_ = reinterpret_cast<const char *>(buffer() + hdr_->stringtab);
|
||||
|
||||
// Validate sections header.
|
||||
if ((sizeof(sp_file_hdr_t) + hdr_->sections * sizeof(sp_file_section_t)) > length_)
|
||||
return error("invalid section table");
|
||||
|
||||
size_t last_header_string = 0;
|
||||
const sp_file_section_t *sections =
|
||||
reinterpret_cast<const sp_file_section_t *>(buffer() + sizeof(sp_file_hdr_t));
|
||||
for (size_t i = 0; i < hdr_->sections; i++) {
|
||||
if (sections[i].nameoffs >= (hdr_->dataoffs - hdr_->stringtab))
|
||||
return error("invalid section name");
|
||||
|
||||
if (sections[i].nameoffs > last_header_string)
|
||||
last_header_string = sections[i].nameoffs;
|
||||
|
||||
sections_.append(Section());
|
||||
sections_.back().dataoffs = sections[i].dataoffs;
|
||||
sections_.back().size = sections[i].size;
|
||||
sections_.back().name = header_strings_ + sections[i].nameoffs;
|
||||
}
|
||||
|
||||
// Validate sanity of section header strings.
|
||||
bool found_terminator = false;
|
||||
for (const uint8_t *iter = buffer() + last_header_string;
|
||||
iter < buffer() + hdr_->dataoffs;
|
||||
iter++)
|
||||
{
|
||||
if (*iter == '\0') {
|
||||
found_terminator = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found_terminator)
|
||||
return error("malformed section names header");
|
||||
|
||||
names_section_ = findSection(".names");
|
||||
if (!names_section_)
|
||||
return error("could not find .names section");
|
||||
if (!validateSection(names_section_))
|
||||
return error("invalid names section");
|
||||
names_ = reinterpret_cast<const char *>(buffer() + names_section_->dataoffs);
|
||||
|
||||
// The names section must be 0-length or be null-terminated.
|
||||
if (names_section_->size != 0 &&
|
||||
*(names_ + names_section_->size - 1) != '\0')
|
||||
{
|
||||
return error("malformed names section");
|
||||
}
|
||||
|
||||
if (!validateCode())
|
||||
return false;
|
||||
if (!validateData())
|
||||
return false;
|
||||
if (!validatePublics())
|
||||
return false;
|
||||
if (!validatePubvars())
|
||||
return false;
|
||||
if (!validateNatives())
|
||||
return false;
|
||||
if (!validateDebugInfo())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const SmxV1Image::Section *
|
||||
SmxV1Image::findSection(const char *name)
|
||||
{
|
||||
for (size_t i = 0; i < sections_.length(); i++) {
|
||||
if (strcmp(sections_[i].name, name) == 0)
|
||||
return §ions_[i];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::validateSection(const Section *section)
|
||||
{
|
||||
if (section->dataoffs >= length_)
|
||||
return false;
|
||||
if (section->size > length_ - section->dataoffs)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::validateData()
|
||||
{
|
||||
// .data is required.
|
||||
const Section *section = findSection(".data");
|
||||
if (!section)
|
||||
return error("could not find data");
|
||||
if (!validateSection(section))
|
||||
return error("invalid data section");
|
||||
|
||||
const sp_file_data_t *data =
|
||||
reinterpret_cast<const sp_file_data_t *>(buffer() + section->dataoffs);
|
||||
if (data->data > section->size)
|
||||
return error("invalid data blob");
|
||||
if (data->datasize > (section->size - data->data))
|
||||
return error("invalid data blob");
|
||||
|
||||
const uint8_t *blob =
|
||||
reinterpret_cast<const uint8_t *>(data) + data->data;
|
||||
data_ = Blob<sp_file_data_t>(section, data, blob, data->datasize);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::validateCode()
|
||||
{
|
||||
// .code is required.
|
||||
const Section *section = findSection(".code");
|
||||
if (!section)
|
||||
return error("could not find code");
|
||||
if (!validateSection(section))
|
||||
return error("invalid code section");
|
||||
|
||||
const sp_file_code_t *code =
|
||||
reinterpret_cast<const sp_file_code_t *>(buffer() + section->dataoffs);
|
||||
if (code->codeversion < SmxConsts::CODE_VERSION_SP1_MIN)
|
||||
return error("code version is too old, no longer supported");
|
||||
if (code->codeversion > SmxConsts::CODE_VERSION_SP1_MAX)
|
||||
return error("code version is too new, not supported");
|
||||
if (code->cellsize != 4)
|
||||
return error("unsupported cellsize");
|
||||
if (code->flags & ~CODEFLAG_DEBUG)
|
||||
return error("unsupported code settings");
|
||||
if (code->code > section->size)
|
||||
return error("invalid code blob");
|
||||
if (code->codesize > (section->size - code->code))
|
||||
return error("invalid code blob");
|
||||
|
||||
const uint8_t *blob =
|
||||
reinterpret_cast<const uint8_t *>(code) + code->code;
|
||||
code_ = Blob<sp_file_code_t>(section, code, blob, code->codesize);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::validatePublics()
|
||||
{
|
||||
const Section *section = findSection(".publics");
|
||||
if (!section)
|
||||
return true;
|
||||
if (!validateSection(section))
|
||||
return error("invalid .publics section");
|
||||
if ((section->size % sizeof(sp_file_publics_t)) != 0)
|
||||
return error("invalid .publics section");
|
||||
|
||||
const sp_file_publics_t *publics =
|
||||
reinterpret_cast<const sp_file_publics_t *>(buffer() + section->dataoffs);
|
||||
size_t length = section->size / sizeof(sp_file_publics_t);
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (!validateName(publics[i].name))
|
||||
return error("invalid public name");
|
||||
}
|
||||
|
||||
publics_ = List<sp_file_publics_t>(publics, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::validatePubvars()
|
||||
{
|
||||
const Section *section = findSection(".pubvars");
|
||||
if (!section)
|
||||
return true;
|
||||
if (!validateSection(section))
|
||||
return error("invalid .pubvars section");
|
||||
if ((section->size % sizeof(sp_file_pubvars_t)) != 0)
|
||||
return error("invalid .pubvars section");
|
||||
|
||||
const sp_file_pubvars_t *pubvars =
|
||||
reinterpret_cast<const sp_file_pubvars_t *>(buffer() + section->dataoffs);
|
||||
size_t length = section->size / sizeof(sp_file_pubvars_t);
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (!validateName(pubvars[i].name))
|
||||
return error("invalid pubvar name");
|
||||
}
|
||||
|
||||
pubvars_ = List<sp_file_pubvars_t>(pubvars, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::validateNatives()
|
||||
{
|
||||
const Section *section = findSection(".natives");
|
||||
if (!section)
|
||||
return true;
|
||||
if (!validateSection(section))
|
||||
return error("invalid .natives section");
|
||||
if ((section->size % sizeof(sp_file_natives_t)) != 0)
|
||||
return error("invalid .natives section");
|
||||
|
||||
const sp_file_natives_t *natives =
|
||||
reinterpret_cast<const sp_file_natives_t *>(buffer() + section->dataoffs);
|
||||
size_t length = section->size / sizeof(sp_file_natives_t);
|
||||
|
||||
for (size_t i = 0; i < length; i++) {
|
||||
if (!validateName(natives[i].name))
|
||||
return error("invalid pubvar name");
|
||||
}
|
||||
|
||||
natives_ = List<sp_file_natives_t>(natives, length);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::validateName(size_t offset)
|
||||
{
|
||||
return offset < names_section_->size;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::validateDebugInfo()
|
||||
{
|
||||
const Section *dbginfo = findSection(".dbg.info");
|
||||
if (!dbginfo)
|
||||
return true;
|
||||
if (!validateSection(dbginfo))
|
||||
return error("invalid .dbg.info section");
|
||||
|
||||
debug_info_ =
|
||||
reinterpret_cast<const sp_fdbg_info_t *>(buffer() + dbginfo->dataoffs);
|
||||
|
||||
debug_names_section_ = findSection(".dbg.strings");
|
||||
if (!debug_names_section_)
|
||||
return error("no debug string table");
|
||||
if (!validateSection(debug_names_section_))
|
||||
return error("invalid .dbg.strings section");
|
||||
debug_names_ = reinterpret_cast<const char *>(buffer() + debug_names_section_->dataoffs);
|
||||
|
||||
// Name tables must be null-terminated.
|
||||
if (debug_names_section_->size != 0 &&
|
||||
*(debug_names_ + debug_names_section_->size - 1) != '\0')
|
||||
{
|
||||
return error("invalid .dbg.strings section");
|
||||
}
|
||||
|
||||
const Section *files = findSection(".dbg.files");
|
||||
if (!files)
|
||||
return error("no debug file table");
|
||||
if (!validateSection(files))
|
||||
return error("invalid debug file table");
|
||||
if (files->size < sizeof(sp_fdbg_file_t) * debug_info_->num_files)
|
||||
return error("invalid debug file table");
|
||||
debug_files_ = List<sp_fdbg_file_t>(
|
||||
reinterpret_cast<const sp_fdbg_file_t *>(buffer() + files->dataoffs),
|
||||
debug_info_->num_files);
|
||||
|
||||
const Section *lines = findSection(".dbg.lines");
|
||||
if (!lines)
|
||||
return error("no debug lines table");
|
||||
if (!validateSection(lines))
|
||||
return error("invalid debug lines table");
|
||||
if (lines->size < sizeof(sp_fdbg_line_t) * debug_info_->num_lines)
|
||||
return error("invalid debug lines table");
|
||||
debug_lines_ = List<sp_fdbg_line_t>(
|
||||
reinterpret_cast<const sp_fdbg_line_t *>(buffer() + lines->dataoffs),
|
||||
debug_info_->num_lines);
|
||||
|
||||
debug_symbols_section_ = findSection(".dbg.symbols");
|
||||
if (!debug_symbols_section_)
|
||||
return error("no debug symbol table");
|
||||
if (!validateSection(debug_symbols_section_))
|
||||
return error("invalid debug symbol table");
|
||||
|
||||
// See the note about unpacked debug sections in smx-headers.h.
|
||||
if (hdr_->version == SmxConsts::SP1_VERSION_1_0 &&
|
||||
!findSection(".dbg.natives"))
|
||||
{
|
||||
debug_syms_unpacked_ =
|
||||
reinterpret_cast<const sp_u_fdbg_symbol_t *>(buffer() + debug_symbols_section_->dataoffs);
|
||||
} else {
|
||||
debug_syms_ =
|
||||
reinterpret_cast<const sp_fdbg_symbol_t *>(buffer() + debug_symbols_section_->dataoffs);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
auto
|
||||
SmxV1Image::DescribeCode() const -> Code
|
||||
{
|
||||
Code code;
|
||||
code.bytes = code_.blob();
|
||||
code.length = code_.length();
|
||||
switch (code_->codeversion) {
|
||||
case SmxConsts::CODE_VERSION_JIT_1_0:
|
||||
code.version = CodeVersion::SP_1_0;
|
||||
break;
|
||||
case SmxConsts::CODE_VERSION_JIT_1_1:
|
||||
code.version = CodeVersion::SP_1_1;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
code.version = CodeVersion::Unknown;
|
||||
break;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
auto
|
||||
SmxV1Image::DescribeData() const -> Data
|
||||
{
|
||||
Data data;
|
||||
data.bytes = data_.blob();
|
||||
data.length = data_.length();
|
||||
return data;
|
||||
}
|
||||
|
||||
size_t
|
||||
SmxV1Image::NumNatives() const
|
||||
{
|
||||
return natives_.length();
|
||||
}
|
||||
|
||||
const char *
|
||||
SmxV1Image::GetNative(size_t index) const
|
||||
{
|
||||
assert(index < natives_.length());
|
||||
return names_ + natives_[index].name;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::FindNative(const char *name, size_t *indexp) const
|
||||
{
|
||||
for (size_t i = 0; i < natives_.length(); i++) {
|
||||
const char *candidate = names_ + natives_[i].name;
|
||||
if (strcmp(candidate, name) == 0) {
|
||||
if (indexp)
|
||||
*indexp = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t
|
||||
SmxV1Image::NumPublics() const
|
||||
{
|
||||
return publics_.length();
|
||||
}
|
||||
|
||||
void
|
||||
SmxV1Image::GetPublic(size_t index, uint32_t *offsetp, const char **namep) const
|
||||
{
|
||||
assert(index < publics_.length());
|
||||
if (offsetp)
|
||||
*offsetp = publics_[index].address;
|
||||
if (namep)
|
||||
*namep = names_ + publics_[index].name;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::FindPublic(const char *name, size_t *indexp) const
|
||||
{
|
||||
int high = publics_.length() - 1;
|
||||
int low = 0;
|
||||
while (low <= high) {
|
||||
int mid = (low + high) / 2;
|
||||
const char *candidate = names_ + publics_[mid].name;
|
||||
int diff = strcmp(candidate, name);
|
||||
if (diff == 0) {
|
||||
if (indexp) {
|
||||
*indexp = mid;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (diff < 0)
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid - 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t
|
||||
SmxV1Image::NumPubvars() const
|
||||
{
|
||||
return pubvars_.length();
|
||||
}
|
||||
|
||||
void
|
||||
SmxV1Image::GetPubvar(size_t index, uint32_t *offsetp, const char **namep) const
|
||||
{
|
||||
assert(index < pubvars_.length());
|
||||
if (offsetp)
|
||||
*offsetp = pubvars_[index].address;
|
||||
if (namep)
|
||||
*namep = names_ + pubvars_[index].name;
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::FindPubvar(const char *name, size_t *indexp) const
|
||||
{
|
||||
int high = pubvars_.length() - 1;
|
||||
int low = 0;
|
||||
while (low <= high) {
|
||||
int mid = (low + high) / 2;
|
||||
const char *candidate = names_ + pubvars_[mid].name;
|
||||
int diff = strcmp(candidate, name);
|
||||
if (diff == 0) {
|
||||
if (indexp) {
|
||||
*indexp = mid;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (diff < 0)
|
||||
low = mid + 1;
|
||||
else
|
||||
high = mid - 1;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t
|
||||
SmxV1Image::HeapSize() const
|
||||
{
|
||||
return data_->memsize;
|
||||
}
|
||||
|
||||
size_t
|
||||
SmxV1Image::ImageSize() const
|
||||
{
|
||||
return length_;
|
||||
}
|
||||
|
||||
const char *
|
||||
SmxV1Image::LookupFile(uint32_t addr)
|
||||
{
|
||||
int high = debug_files_.length();
|
||||
int low = -1;
|
||||
|
||||
while (high - low > 1) {
|
||||
int mid = (low + high) / 2;
|
||||
if (debug_files_[mid].addr <= addr)
|
||||
low = mid;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
|
||||
if (low == -1)
|
||||
return nullptr;
|
||||
if (debug_files_[low].name >= debug_names_section_->size)
|
||||
return nullptr;
|
||||
|
||||
return debug_names_ + debug_files_[low].name;
|
||||
}
|
||||
|
||||
template <typename SymbolType, typename DimType>
|
||||
const char *
|
||||
SmxV1Image::lookupFunction(const SymbolType *syms, uint32_t addr)
|
||||
{
|
||||
const uint8_t *cursor = reinterpret_cast<const uint8_t *>(syms);
|
||||
const uint8_t *cursor_end = cursor + debug_symbols_section_->size;
|
||||
for (uint32_t i = 0; i < debug_info_->num_syms; i++) {
|
||||
if (cursor + sizeof(SymbolType) > cursor_end)
|
||||
break;
|
||||
|
||||
const SymbolType *sym = reinterpret_cast<const SymbolType *>(cursor);
|
||||
if (sym->ident == sp::IDENT_FUNCTION &&
|
||||
sym->codestart <= addr &&
|
||||
sym->codeend > addr)
|
||||
{
|
||||
if (sym->name >= debug_names_section_->size)
|
||||
return nullptr;
|
||||
return debug_names_ + sym->name;
|
||||
}
|
||||
|
||||
if (sym->dimcount > 0)
|
||||
cursor += sizeof(DimType) * sym->dimcount;
|
||||
cursor += sizeof(SymbolType);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *
|
||||
SmxV1Image::LookupFunction(uint32_t code_offset)
|
||||
{
|
||||
if (debug_syms_) {
|
||||
return lookupFunction<sp_fdbg_symbol_t, sp_fdbg_arraydim_t>(
|
||||
debug_syms_, code_offset);
|
||||
}
|
||||
return lookupFunction<sp_u_fdbg_symbol_t, sp_u_fdbg_arraydim_t>(
|
||||
debug_syms_unpacked_, code_offset);
|
||||
}
|
||||
|
||||
bool
|
||||
SmxV1Image::LookupLine(uint32_t addr, uint32_t *line)
|
||||
{
|
||||
int high = debug_lines_.length();
|
||||
int low = -1;
|
||||
|
||||
while (high - low > 1) {
|
||||
int mid = (low + high) / 2;
|
||||
if (debug_lines_[mid].addr <= addr)
|
||||
low = mid;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
|
||||
if (low == -1)
|
||||
return false;
|
||||
|
||||
// Since the CIP occurs BEFORE the line, we have to add one.
|
||||
*line = debug_lines_[low].line + 1;
|
||||
return true;
|
||||
}
|
204
sourcepawn/jit/smx-v1-image.h
Normal file
204
sourcepawn/jit/smx-v1-image.h
Normal file
@ -0,0 +1,204 @@
|
||||
// vim: set sts=2 ts=8 sw=2 tw=99 et:
|
||||
//
|
||||
// Copyright (C) 2004-2015 AlliedModers LLC
|
||||
//
|
||||
// This file is part of SourcePawn. SourcePawn is licensed under the GNU
|
||||
// General Public License, version 3.0 (GPL). If a copy of the GPL was not
|
||||
// provided with this file, you can obtain it here:
|
||||
// http://www.gnu.org/licenses/gpl.html
|
||||
//
|
||||
#ifndef _include_sourcepawn_smx_parser_h_
|
||||
#define _include_sourcepawn_smx_parser_h_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <smx/smx-headers.h>
|
||||
#include <smx/smx-v1.h>
|
||||
#include <am-utility.h>
|
||||
#include <am-string.h>
|
||||
#include <am-vector.h>
|
||||
#include "file-utils.h"
|
||||
#include "legacy-image.h"
|
||||
|
||||
namespace sp {
|
||||
|
||||
class SmxV1Image
|
||||
: public FileReader,
|
||||
public LegacyImage
|
||||
{
|
||||
public:
|
||||
SmxV1Image(FILE *fp);
|
||||
|
||||
// This must be called to initialize the reader.
|
||||
bool validate();
|
||||
|
||||
const sp_file_hdr_t *hdr() const {
|
||||
return hdr_;
|
||||
}
|
||||
|
||||
const char *errorMessage() const {
|
||||
return error_.chars();
|
||||
}
|
||||
|
||||
public:
|
||||
Code DescribeCode() const KE_OVERRIDE;
|
||||
Data DescribeData() const KE_OVERRIDE;
|
||||
size_t NumNatives() const KE_OVERRIDE;
|
||||
const char *GetNative(size_t index) const KE_OVERRIDE;
|
||||
bool FindNative(const char *name, size_t *indexp) const KE_OVERRIDE;
|
||||
size_t NumPublics() const KE_OVERRIDE;
|
||||
void GetPublic(size_t index, uint32_t *offsetp, const char **namep) const KE_OVERRIDE;
|
||||
bool FindPublic(const char *name, size_t *indexp) const KE_OVERRIDE;
|
||||
size_t NumPubvars() const KE_OVERRIDE;
|
||||
void GetPubvar(size_t index, uint32_t *offsetp, const char **namep) const KE_OVERRIDE;
|
||||
bool FindPubvar(const char *name, size_t *indexp) const KE_OVERRIDE;
|
||||
size_t HeapSize() const KE_OVERRIDE;
|
||||
size_t ImageSize() const KE_OVERRIDE;
|
||||
const char *LookupFile(uint32_t code_offset) KE_OVERRIDE;
|
||||
const char *LookupFunction(uint32_t code_offset) KE_OVERRIDE;
|
||||
bool LookupLine(uint32_t code_offset, uint32_t *line) KE_OVERRIDE;
|
||||
|
||||
private:
|
||||
struct Section
|
||||
{
|
||||
const char *name;
|
||||
uint32_t dataoffs;
|
||||
uint32_t size;
|
||||
};
|
||||
const Section *findSection(const char *name);
|
||||
|
||||
public:
|
||||
template <typename T>
|
||||
class Blob
|
||||
{
|
||||
public:
|
||||
Blob()
|
||||
: header_(nullptr),
|
||||
section_(nullptr),
|
||||
blob_(nullptr),
|
||||
length_(0)
|
||||
{}
|
||||
Blob(const Section *header,
|
||||
const T *section,
|
||||
const uint8_t *blob,
|
||||
size_t length)
|
||||
: header_(header),
|
||||
section_(section),
|
||||
blob_(blob),
|
||||
length_(length)
|
||||
{}
|
||||
|
||||
size_t size() const {
|
||||
return section_->size;
|
||||
}
|
||||
const T * operator ->() const {
|
||||
return section_;
|
||||
}
|
||||
const uint8_t *blob() const {
|
||||
return blob_;
|
||||
}
|
||||
size_t length() const {
|
||||
return length_;
|
||||
}
|
||||
bool exists() const {
|
||||
return !!header_;
|
||||
}
|
||||
|
||||
private:
|
||||
const Section *header_;
|
||||
const T *section_;
|
||||
const uint8_t *blob_;
|
||||
size_t length_;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class List
|
||||
{
|
||||
public:
|
||||
List()
|
||||
: section_(nullptr),
|
||||
length_(0)
|
||||
{}
|
||||
List(const T *section, size_t length)
|
||||
: section_(section),
|
||||
length_(length)
|
||||
{}
|
||||
|
||||
size_t length() const {
|
||||
return length_;
|
||||
}
|
||||
const T &operator[](size_t index) const {
|
||||
assert(index < length());
|
||||
return section_[index];
|
||||
}
|
||||
bool exists() const {
|
||||
return !!section_;
|
||||
}
|
||||
|
||||
private:
|
||||
const T *section_;
|
||||
size_t length_;
|
||||
};
|
||||
|
||||
public:
|
||||
const Blob<sp_file_code_t> &code() const {
|
||||
return code_;
|
||||
}
|
||||
const Blob<sp_file_data_t> &data() const {
|
||||
return data_;
|
||||
}
|
||||
const List<sp_file_publics_t> &publics() const {
|
||||
return publics_;
|
||||
}
|
||||
const List<sp_file_natives_t> &natives() const {
|
||||
return natives_;
|
||||
}
|
||||
const List<sp_file_pubvars_t> &pubvars() const {
|
||||
return pubvars_;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool error(const char *msg) {
|
||||
error_ = msg;
|
||||
return false;
|
||||
}
|
||||
bool validateName(size_t offset);
|
||||
bool validateSection(const Section *section);
|
||||
bool validateCode();
|
||||
bool validateData();
|
||||
bool validatePublics();
|
||||
bool validatePubvars();
|
||||
bool validateNatives();
|
||||
bool validateDebugInfo();
|
||||
|
||||
private:
|
||||
template <typename SymbolType, typename DimType>
|
||||
const char *lookupFunction(const SymbolType *syms, uint32_t addr);
|
||||
|
||||
private:
|
||||
sp_file_hdr_t *hdr_;
|
||||
ke::AString error_;
|
||||
const char *header_strings_;
|
||||
ke::Vector<Section> sections_;
|
||||
|
||||
const Section *names_section_;
|
||||
const char *names_;
|
||||
|
||||
Blob<sp_file_code_t> code_;
|
||||
Blob<sp_file_data_t> data_;
|
||||
List<sp_file_publics_t> publics_;
|
||||
List<sp_file_natives_t> natives_;
|
||||
List<sp_file_pubvars_t> pubvars_;
|
||||
|
||||
const Section *debug_names_section_;
|
||||
const char *debug_names_;
|
||||
const sp_fdbg_info_t *debug_info_;
|
||||
List<sp_fdbg_file_t> debug_files_;
|
||||
List<sp_fdbg_line_t> debug_lines_;
|
||||
const Section *debug_symbols_section_;
|
||||
const sp_fdbg_symbol_t *debug_syms_;
|
||||
const sp_u_fdbg_symbol_t *debug_syms_unpacked_;
|
||||
};
|
||||
|
||||
} // namespace sp
|
||||
|
||||
#endif // _include_sourcepawn_smx_parser_h_
|
@ -17,7 +17,8 @@
|
||||
#include "watchdog_timer.h"
|
||||
#include <string.h>
|
||||
#include "environment.h"
|
||||
//#include "x86/jit_x86.h"
|
||||
|
||||
using namespace sp;
|
||||
|
||||
WatchdogTimer::WatchdogTimer(Environment *env)
|
||||
: env_(env),
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <sp_vm_api.h>
|
||||
#include "code-stubs.h"
|
||||
#include "x86-utils.h"
|
||||
#include "jit_shared.h"
|
||||
#include "jit_x86.h"
|
||||
|
||||
using namespace sp;
|
||||
@ -53,10 +52,8 @@ CodeStubs::CompileInvokeStub()
|
||||
// ecx = code
|
||||
__ movl(ecx, Operand(ebp, 8 + 4 * 1));
|
||||
|
||||
// eax = cx->m_pRuntime->m_plugin.memory
|
||||
__ movl(eax, Operand(ebx, PluginContext::offsetOfRuntime()));
|
||||
__ addl(eax, PluginRuntime::offsetToPlugin());
|
||||
__ movl(eax, Operand(eax, offsetof(sp_plugin_t, memory)));
|
||||
// eax = cx->memory
|
||||
__ movl(eax, Operand(ebx, PluginContext::offsetOfMemory()));
|
||||
|
||||
// Set up run-time registers.
|
||||
__ movl(edi, Operand(ebx, PluginContext::offsetOfSp()));
|
||||
|
@ -76,7 +76,7 @@ OpToCondition(OPCODE op)
|
||||
}
|
||||
}
|
||||
|
||||
#if !defined NDEBUG
|
||||
#if 0 && !defined NDEBUG
|
||||
static const char *
|
||||
GetFunctionName(const sp_plugin_t *plugin, uint32_t offs)
|
||||
{
|
||||
@ -131,7 +131,7 @@ GetFunctionName(const sp_plugin_t *plugin, uint32_t offs)
|
||||
#endif
|
||||
|
||||
CompiledFunction *
|
||||
CompileFunction(PluginRuntime *prt, cell_t pcode_offs, int *err)
|
||||
sp::CompileFunction(PluginRuntime *prt, cell_t pcode_offs, int *err)
|
||||
{
|
||||
Compiler cc(prt, pcode_offs);
|
||||
CompiledFunction *fun = cc.emit(err);
|
||||
@ -181,14 +181,14 @@ Compiler::Compiler(PluginRuntime *rt, cell_t pcode_offs)
|
||||
: env_(Environment::get()),
|
||||
rt_(rt),
|
||||
context_(rt->GetBaseContext()),
|
||||
plugin_(rt->plugin()),
|
||||
image_(rt_->image()),
|
||||
error_(SP_ERROR_NONE),
|
||||
pcode_start_(pcode_offs),
|
||||
code_start_(reinterpret_cast<cell_t *>(plugin_->pcode + pcode_start_)),
|
||||
code_start_(reinterpret_cast<const cell_t *>(rt_->code().bytes + pcode_start_)),
|
||||
cip_(code_start_),
|
||||
code_end_(reinterpret_cast<cell_t *>(plugin_->pcode + plugin_->pcode_size))
|
||||
code_end_(reinterpret_cast<const cell_t *>(rt_->code().bytes + rt_->code().length))
|
||||
{
|
||||
size_t nmaxops = plugin_->pcode_size / sizeof(cell_t) + 1;
|
||||
size_t nmaxops = rt_->code().length / sizeof(cell_t) + 1;
|
||||
jump_map_ = new Label[nmaxops];
|
||||
}
|
||||
|
||||
@ -208,13 +208,13 @@ Compiler::emit(int *errp)
|
||||
#if defined JIT_SPEW
|
||||
g_engine1.GetDebugHook()->OnDebugSpew(
|
||||
"Compiling function %s::%s\n",
|
||||
plugin_->name,
|
||||
rt_->Name(),
|
||||
GetFunctionName(plugin_, pcode_start_));
|
||||
|
||||
SpewOpcode(plugin_, code_start_, cip_);
|
||||
#endif
|
||||
|
||||
cell_t *codeseg = reinterpret_cast<cell_t *>(plugin_->pcode);
|
||||
const cell_t *codeseg = reinterpret_cast<const cell_t *>(rt_->code().bytes);
|
||||
|
||||
cip_++;
|
||||
if (!emitOp(OP_PROC)) {
|
||||
@ -1077,7 +1077,7 @@ Compiler::emitOp(OPCODE op)
|
||||
|
||||
if (amount > 0) {
|
||||
// Check if the stack went beyond the stack top - usually a compiler error.
|
||||
__ cmpl(stk, intptr_t(plugin_->memory + plugin_->mem_size));
|
||||
__ cmpl(stk, intptr_t(context_->memory() + context_->HeapSize()));
|
||||
__ j(not_below, &error_stack_min_);
|
||||
} else {
|
||||
// Check if the stack is going to collide with the heap.
|
||||
@ -1096,7 +1096,7 @@ Compiler::emitOp(OPCODE op)
|
||||
__ addl(Operand(hpAddr()), amount);
|
||||
|
||||
if (amount < 0) {
|
||||
__ cmpl(Operand(hpAddr()), plugin_->data_size);
|
||||
__ cmpl(Operand(hpAddr()), context_->DataSize());
|
||||
__ j(below, &error_heap_min_);
|
||||
} else {
|
||||
__ movl(tmp, Operand(hpAddr()));
|
||||
@ -1198,7 +1198,7 @@ Compiler::emitOp(OPCODE op)
|
||||
|
||||
case OP_BREAK:
|
||||
{
|
||||
cell_t cip = uintptr_t(cip_ - 1) - uintptr_t(plugin_->pcode);
|
||||
cell_t cip = uintptr_t(cip_ - 1) - uintptr_t(rt_->code().bytes);
|
||||
__ movl(Operand(cipAddr()), cip);
|
||||
break;
|
||||
}
|
||||
@ -1262,7 +1262,7 @@ Label *
|
||||
Compiler::labelAt(size_t offset)
|
||||
{
|
||||
if (offset % 4 != 0 ||
|
||||
offset > plugin_->pcode_size ||
|
||||
offset > rt_->code().length ||
|
||||
offset <= pcode_start_)
|
||||
{
|
||||
// If the jump target is misaligned, or out of pcode bounds, or is an
|
||||
@ -1280,7 +1280,7 @@ void
|
||||
Compiler::emitCheckAddress(Register reg)
|
||||
{
|
||||
// Check if we're in memory bounds.
|
||||
__ cmpl(reg, plugin_->mem_size);
|
||||
__ cmpl(reg, context_->HeapSize());
|
||||
__ j(not_below, &error_memaccess_);
|
||||
|
||||
// Check if we're in the invalid region between hp and sp.
|
||||
@ -1335,7 +1335,7 @@ Compiler::emitGenArray(bool autozero)
|
||||
} else {
|
||||
__ push(pri);
|
||||
|
||||
// int GenerateArray(sp_plugin_t, vars[], uint32_t, cell_t *, int, unsigned *);
|
||||
// int GenerateArray(cx, vars[], uint32_t, cell_t *, int, unsigned *);
|
||||
__ push(autozero ? 1 : 0);
|
||||
__ push(stk);
|
||||
__ push(val);
|
||||
@ -1362,7 +1362,7 @@ Compiler::emitCall()
|
||||
|
||||
// If this offset looks crappy, i.e. not aligned or out of bounds, we just
|
||||
// abort.
|
||||
if (offset % 4 != 0 || uint32_t(offset) >= plugin_->pcode_size) {
|
||||
if (offset % 4 != 0 || uint32_t(offset) >= rt_->code().length) {
|
||||
error_ = SP_ERROR_INSTRUCTION_PARAM;
|
||||
return false;
|
||||
}
|
||||
@ -1377,7 +1377,7 @@ Compiler::emitCall()
|
||||
__ j(not_below, &error_stack_low_);
|
||||
|
||||
// Add to the return stack.
|
||||
uintptr_t cip = uintptr_t(cip_ - 2) - uintptr_t(plugin_->pcode);
|
||||
uintptr_t cip = uintptr_t(cip_ - 2) - uintptr_t(rt_->code().bytes);
|
||||
__ movl(Operand(eax, ecx, ScaleFour, PluginContext::offsetOfRstkCips()), cip);
|
||||
|
||||
// Increment the return stack pointer.
|
||||
@ -1461,7 +1461,7 @@ Compiler::emitNativeCall(OPCODE op)
|
||||
{
|
||||
uint32_t native_index = readCell();
|
||||
|
||||
if (native_index >= plugin_->num_natives) {
|
||||
if (native_index >= image_->NumNatives()) {
|
||||
error_ = SP_ERROR_INSTRUCTION_PARAM;
|
||||
return false;
|
||||
}
|
||||
@ -1537,7 +1537,7 @@ Compiler::emitSwitch()
|
||||
if (!labelAt(offset))
|
||||
return false;
|
||||
|
||||
cell_t *tbl = (cell_t *)((char *)plugin_->pcode + offset + sizeof(cell_t));
|
||||
cell_t *tbl = (cell_t *)((char *)rt_->code().bytes + offset + sizeof(cell_t));
|
||||
|
||||
struct Entry {
|
||||
cell_t val;
|
||||
|
@ -21,7 +21,6 @@
|
||||
#include <sp_vm_api.h>
|
||||
#include <macro-assembler-x86.h>
|
||||
#include <am-vector.h>
|
||||
#include "jit_shared.h"
|
||||
#include "plugin-runtime.h"
|
||||
#include "plugin-context.h"
|
||||
#include "compiled-function.h"
|
||||
@ -30,8 +29,9 @@
|
||||
using namespace SourcePawn;
|
||||
|
||||
namespace sp {
|
||||
class LegacyImage;
|
||||
class Environment;
|
||||
}
|
||||
class CompiledFunction;
|
||||
|
||||
#define JIT_INLINE_ERRORCHECKS (1<<0)
|
||||
#define JIT_INLINE_NATIVES (1<<1)
|
||||
@ -71,17 +71,17 @@ class Compiler
|
||||
Compiler(PluginRuntime *rt, cell_t pcode_offs);
|
||||
~Compiler();
|
||||
|
||||
CompiledFunction *emit(int *errp);
|
||||
sp::CompiledFunction *emit(int *errp);
|
||||
|
||||
private:
|
||||
bool setup(cell_t pcode_offs);
|
||||
bool emitOp(OPCODE op);
|
||||
bool emitOp(sp::OPCODE op);
|
||||
cell_t readCell();
|
||||
|
||||
private:
|
||||
Label *labelAt(size_t offset);
|
||||
bool emitCall();
|
||||
bool emitNativeCall(OPCODE op);
|
||||
bool emitNativeCall(sp::OPCODE op);
|
||||
bool emitSwitch();
|
||||
void emitGenArray(bool autozero);
|
||||
void emitCallThunks();
|
||||
@ -102,15 +102,15 @@ class Compiler
|
||||
|
||||
private:
|
||||
AssemblerX86 masm;
|
||||
sp::Environment *env_;
|
||||
Environment *env_;
|
||||
PluginRuntime *rt_;
|
||||
PluginContext *context_;
|
||||
const sp_plugin_t *plugin_;
|
||||
LegacyImage *image_;
|
||||
int error_;
|
||||
uint32_t pcode_start_;
|
||||
cell_t *code_start_;
|
||||
cell_t *cip_;
|
||||
cell_t *code_end_;
|
||||
const cell_t *code_start_;
|
||||
const cell_t *cip_;
|
||||
const cell_t *code_end_;
|
||||
Label *jump_map_;
|
||||
ke::Vector<size_t> backward_jumps_;
|
||||
|
||||
@ -138,5 +138,7 @@ const Register frm = ebx;
|
||||
CompiledFunction *
|
||||
CompileFunction(PluginRuntime *prt, cell_t pcode_offs, int *err);
|
||||
|
||||
}
|
||||
|
||||
#endif //_INCLUDE_SOURCEPAWN_JIT_X86_H_
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user