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:
David Anderson
2015-02-25 22:28:10 -08:00
parent afbcdc8a20
commit 04827466b0
29 changed files with 1570 additions and 768 deletions
+2 -5
View File
@@ -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()));
+18 -18
View File
@@ -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;
+12 -10
View File
@@ -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_