sourcemod/sourcepawn/jit/plugin-runtime.cpp

494 lines
11 KiB
C++
Raw Normal View History

2015-02-24 01:40:36 +01:00
// 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/.
//
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "plugin-runtime.h"
#include "x86/jit_x86.h"
#include "plugin-context.h"
#include "environment.h"
#include "md5/md5.h"
2014-08-22 07:16:07 +02:00
using namespace sp;
using namespace SourcePawn;
static inline bool
IsPointerCellAligned(void *p)
{
return uintptr_t(p) % 4 == 0;
}
PluginRuntime::PluginRuntime(LegacyImage *image)
: image_(image),
paused_(false),
computed_code_hash_(false),
computed_data_hash_(false)
{
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);
}
2015-02-24 01:40:36 +01:00
PluginRuntime::~PluginRuntime()
{
// The watchdog thread takes the global JIT lock while it patches all
// runtimes. It is not enough to ensure that the unlinking of the runtime is
// protected; we cannot delete functions or code while the watchdog might be
// executing. Therefore, the entire destructor is guarded.
ke::AutoLock lock(Environment::get()->lock());
Environment::get()->DeregisterRuntime(this);
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];
}
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 {
const char *name;
unsigned opcode;
};
static const NativeMapping sNativeMap[] = {
{ "FloatAbs", OP_FABS },
{ "FloatAdd", OP_FLOATADD },
{ "FloatSub", OP_FLOATSUB },
{ "FloatMul", OP_FLOATMUL },
{ "FloatDiv", OP_FLOATDIV },
{ "float", OP_FLOAT },
{ "FloatCompare", OP_FLOATCMP },
{ "RoundToCeil", OP_RND_TO_CEIL },
{ "RoundToZero", OP_RND_TO_ZERO },
{ "RoundToFloor", OP_RND_TO_FLOOR },
{ "RoundToNearest", OP_RND_TO_NEAREST },
{ "__FLOAT_GT__", OP_FLOAT_GT },
{ "__FLOAT_GE__", OP_FLOAT_GE },
{ "__FLOAT_LT__", OP_FLOAT_LT },
{ "__FLOAT_LE__", OP_FLOAT_LE },
{ "__FLOAT_EQ__", OP_FLOAT_EQ },
{ "__FLOAT_NE__", OP_FLOAT_NE },
{ "__FLOAT_NOT__", OP_FLOAT_NOT },
{ NULL, 0 },
};
void
2015-02-24 01:40:36 +01:00
PluginRuntime::SetupFloatNativeRemapping()
{
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) {
float_table_[i].found = true;
float_table_[i].index = iter->opcode;
break;
}
iter++;
}
}
}
unsigned
2015-02-24 01:40:36 +01:00
PluginRuntime::GetNativeReplacement(size_t index)
{
if (!float_table_[index].found)
return OP_NOP;
return float_table_[index].index;
}
void
2015-02-24 01:40:36 +01:00
PluginRuntime::SetName(const char *name)
{
size_t len = strlen(name);
name_ = new char[len + 1];
strcpy(name_, name);
}
static cell_t
InvalidNative(IPluginContext *pCtx, const cell_t *params)
{
return pCtx->ThrowNativeErrorEx(SP_ERROR_INVALID_NATIVE, "Invalid native");
}
void
2015-02-24 01:40:36 +01:00
PluginRuntime::AddJittedFunction(CompiledFunction *fn)
{
m_JitFunctions.append(fn);
2015-02-25 08:37:23 +01:00
ucell_t pcode_offset = fn->GetCodeOffset();
FunctionMap::Insert p = function_map_.findForAdd(pcode_offset);
assert(!p.found());
2015-02-25 08:37:23 +01:00
function_map_.add(p, pcode_offset, fn);
}
2015-02-24 01:27:57 +01:00
CompiledFunction *
2015-02-24 01:40:36 +01:00
PluginRuntime::GetJittedFunctionByOffset(cell_t pcode_offset)
{
2015-02-25 08:37:23 +01:00
FunctionMap::Result r = function_map_.find(pcode_offset);
if (r.found())
return r->value;
return nullptr;
}
int
2015-02-24 01:40:36 +01:00
PluginRuntime::FindNativeByName(const char *name, uint32_t *index)
{
size_t idx;
if (!image_->FindNative(name, &idx))
return SP_ERROR_NOT_FOUND;
if (index)
*index = idx;
return SP_ERROR_NONE;
}
int
2015-02-24 01:40:36 +01:00
PluginRuntime::GetNativeByIndex(uint32_t index, sp_native_t **native)
2015-02-25 07:58:31 +01:00
{
return SP_ERROR_PARAM;
}
int
PluginRuntime::UpdateNativeBinding(uint32_t index, SPVM_NATIVE_FUNC pfn, uint32_t flags, void *data)
{
if (index >= image_->NumNatives())
return SP_ERROR_INDEX;
sp_native_t *native = &natives_[index];
2015-02-25 07:58:31 +01:00
native->pfn = pfn;
native->status = pfn
? SP_NATIVE_BOUND
: SP_NATIVE_UNBOUND;
native->flags = flags;
native->user = data;
return SP_ERROR_NONE;
}
2015-02-25 07:58:31 +01:00
const sp_native_t *
PluginRuntime::GetNative(uint32_t index)
{
if (index >= image_->NumNatives())
2015-02-25 07:58:31 +01:00
return nullptr;
if (!natives_[index].name)
natives_[index].name = image_->GetNative(index);
return &natives_[index];
}
uint32_t
2015-02-24 01:40:36 +01:00
PluginRuntime::GetNativesNum()
{
return image_->NumNatives();
}
int
2015-02-24 01:40:36 +01:00
PluginRuntime::FindPublicByName(const char *name, uint32_t *index)
{
size_t idx;
if (!image_->FindPublic(name, &idx))
return SP_ERROR_NOT_FOUND;
if (index)
*index = idx;
return SP_ERROR_NONE;
}
int
PluginRuntime::GetPublicByIndex(uint32_t index, sp_public_t **out)
{
if (index >= image_->NumPublics())
return SP_ERROR_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
2015-02-24 01:40:36 +01:00
PluginRuntime::GetPublicsNum()
{
return image_->NumPublics();
}
int
PluginRuntime::GetPubvarByIndex(uint32_t index, sp_pubvar_t **out)
{
if (index >= image_->NumPubvars())
return SP_ERROR_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
2015-02-24 01:40:36 +01:00
PluginRuntime::FindPubvarByName(const char *name, uint32_t *index)
{
size_t idx;
if (!image_->FindPubvar(name, &idx))
return SP_ERROR_NOT_FOUND;
if (index)
*index = idx;
return SP_ERROR_NONE;
}
int
2015-02-24 01:40:36 +01:00
PluginRuntime::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
{
if (index >= image_->NumPubvars())
return SP_ERROR_INDEX;
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
2015-02-24 01:40:36 +01:00
PluginRuntime::GetPubVarsNum()
{
return image_->NumPubvars();
}
IPluginContext *
2015-02-24 01:40:36 +01:00
PluginRuntime::GetDefaultContext()
{
return context_;
}
IPluginDebugInfo *
2015-02-24 01:40:36 +01:00
PluginRuntime::GetDebugInfo()
{
return this;
}
IPluginFunction *
2015-02-24 01:40:36 +01:00
PluginRuntime::GetFunctionById(funcid_t func_id)
{
ScriptedInvoker *pFunc = NULL;
if (func_id & 1) {
func_id >>= 1;
if (func_id >= image_->NumPublics())
return NULL;
pFunc = entrypoints_[func_id];
if (!pFunc) {
entrypoints_[func_id] = new ScriptedInvoker(this, (func_id << 1) | 1, func_id);
pFunc = entrypoints_[func_id];
}
}
return pFunc;
}
ScriptedInvoker *
2015-02-24 01:40:36 +01:00
PluginRuntime::GetPublicFunction(size_t index)
{
assert(index < image_->NumPublics());
ScriptedInvoker *pFunc = entrypoints_[index];
if (!pFunc) {
sp_public_t *pub = NULL;
GetPublicByIndex(index, &pub);
if (pub)
entrypoints_[index] = new ScriptedInvoker(this, (index << 1) | 1, index);
pFunc = entrypoints_[index];
}
return pFunc;
}
IPluginFunction *
2015-02-24 01:40:36 +01:00
PluginRuntime::GetFunctionByName(const char *public_name)
{
uint32_t index;
if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
return NULL;
return GetPublicFunction(index);
}
bool
PluginRuntime::IsDebugging()
{
return true;
}
void
PluginRuntime::SetPauseState(bool 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;
}
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_;
}
PluginContext *
PluginRuntime::GetBaseContext()
{
return context_;
}
int
2015-02-24 01:40:36 +01:00
PluginRuntime::ApplyCompilationOptions(ICompilation *co)
{
return SP_ERROR_NONE;
}
int
PluginRuntime::LookupLine(ucell_t addr, uint32_t *line)
{
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;
}