diff --git a/sourcepawn/jit/AMBuilder b/sourcepawn/jit/AMBuilder index 88ba7434..ec77f382 100644 --- a/sourcepawn/jit/AMBuilder +++ b/sourcepawn/jit/AMBuilder @@ -18,7 +18,10 @@ def setup(binary): compiler = binary.compiler compiler.includes += Includes if compiler.vendor == 'gcc' or compiler.vendor == 'clang': - compiler.cxxflags += ['-fno-rtti'] + compiler.cxxflags += [ + '-fno-rtti', + '-Wno-invalid-offsetof', + ] elif binary.compiler.vendor == 'msvc': compiler.cxxflags += ['/GR-'] diff --git a/sourcepawn/jit/code-stubs.h b/sourcepawn/jit/code-stubs.h index 093ec7aa..045280de 100644 --- a/sourcepawn/jit/code-stubs.h +++ b/sourcepawn/jit/code-stubs.h @@ -16,13 +16,13 @@ #include #include -typedef struct sp_context_s sp_context_t; +class PluginContext; namespace sp { class Environment; -typedef int (*InvokeStubFn)(sp_context_t *ctx, uint8_t *memory, void *code); +typedef int (*InvokeStubFn)(PluginContext *cx, void *code, cell_t *rval); class CodeStubs { diff --git a/sourcepawn/jit/debug-trace.cpp b/sourcepawn/jit/debug-trace.cpp index c3a3e60d..32e3fc81 100644 --- a/sourcepawn/jit/debug-trace.cpp +++ b/sourcepawn/jit/debug-trace.cpp @@ -20,12 +20,12 @@ using namespace SourcePawn; CContextTrace::CContextTrace(PluginRuntime *pRuntime, int err, const char *errstr, cell_t start_rp) : m_pRuntime(pRuntime), + context_(pRuntime->GetBaseContext()), m_Error(err), m_pMsg(errstr), m_StartRp(start_rp), m_Level(0) { - m_ctx = pRuntime->m_pCtx->GetCtx(); m_pDebug = m_pRuntime->GetDebugInfo(); } @@ -65,21 +65,19 @@ CContextTrace::GetTraceInfo(CallStackInfo *trace) cell_t cip; if (m_Level == 0) { - cip = m_ctx->cip; - } else if (m_ctx->rp > 0) { + cip = context_->cip(); + } else if (context_->rp() > 0) { /* Entries go from ctx.rp - 1 to m_StartRp */ cell_t offs, start, end; offs = m_Level - 1; - start = m_ctx->rp - 1; + start = context_->rp() - 1; end = m_StartRp; if (start - offs < end) - { return false; - } - cip = m_ctx->rstk_cips[start - offs]; + cip = context_->getReturnStackCip(start - offs); } else { return false; } @@ -106,15 +104,19 @@ CContextTrace::GetTraceInfo(CallStackInfo *trace) const char * CContextTrace::GetLastNative(uint32_t *index) { - if (m_ctx->n_err == SP_ERROR_NONE) + if (context_->GetLastNativeError() == SP_ERROR_NONE) + return NULL; + + int lastNative = context_->lastNative(); + if (lastNative < 0) return NULL; sp_native_t *native; - if (m_pRuntime->GetNativeByIndex(m_ctx->n_idx, &native) != SP_ERROR_NONE) + if (m_pRuntime->GetNativeByIndex(lastNative, &native) != SP_ERROR_NONE) return NULL; if (index) - *index = m_ctx->n_idx; + *index = lastNative; return native->name; } diff --git a/sourcepawn/jit/debug-trace.h b/sourcepawn/jit/debug-trace.h index e6d3f3f2..a59fa11e 100644 --- a/sourcepawn/jit/debug-trace.h +++ b/sourcepawn/jit/debug-trace.h @@ -16,6 +16,7 @@ #include class PluginRuntime; +class PluginContext; namespace sp { @@ -37,7 +38,7 @@ class CContextTrace : public IContextTrace private: PluginRuntime *m_pRuntime; - sp_context_t *m_ctx; + PluginContext *context_; int m_Error; const char *m_pMsg; cell_t m_StartRp; diff --git a/sourcepawn/jit/environment.cpp b/sourcepawn/jit/environment.cpp index 6c2feb67..7c808817 100644 --- a/sourcepawn/jit/environment.cpp +++ b/sourcepawn/jit/environment.cpp @@ -238,17 +238,16 @@ Environment::UnpatchAllJumpsFromTimeout() int Environment::Invoke(PluginRuntime *runtime, CompiledFunction *fn, cell_t *result) { - sp_context_t *ctx = runtime->GetBaseContext()->GetCtx(); + PluginContext *cx = runtime->GetBaseContext(); // Note that cip, hp, sp are saved and restored by Execute2(). - ctx->cip = fn->GetCodeOffset(); + *cx->addressOfCip() = fn->GetCodeOffset(); InvokeStubFn invoke = code_stubs_->InvokeStub(); EnterInvoke(); - int err = invoke(ctx, runtime->plugin()->memory, fn->GetEntryAddress()); + int err = invoke(cx, fn->GetEntryAddress(), result); LeaveInvoke(); - *result = ctx->rval; return err; } diff --git a/sourcepawn/jit/interpreter.cpp b/sourcepawn/jit/interpreter.cpp index 1cc394cd..90404866 100644 --- a/sourcepawn/jit/interpreter.cpp +++ b/sourcepawn/jit/interpreter.cpp @@ -45,187 +45,34 @@ Write(const sp_plugin_t *plugin, cell_t offset, cell_t value) } static inline cell_t * -Jump(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t target) +Jump(const sp_plugin_t *plugin, cell_t target) { - if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) { - ctx->err = SP_ERROR_INVALID_INSTRUCTION; + if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) return NULL; - } - return reinterpret_cast(plugin->pcode + target); } static inline cell_t * -JumpTarget(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *cip, bool cond) +JumpTarget(const sp_plugin_t *plugin, cell_t *cip, bool cond, int *errp) { if (!cond) return cip + 1; cell_t target = *cip; if (!IsValidOffset(target) || uint32_t(target) >= plugin->pcode_size) { - ctx->err = SP_ERROR_INVALID_INSTRUCTION; + *errp = SP_ERROR_INVALID_INSTRUCTION; return NULL; } cell_t *next = reinterpret_cast(plugin->pcode + target); if (next < cip && !Environment::get()->watchdog()->HandleInterrupt()) { - ctx->err = SP_ERROR_TIMEOUT; + *errp = SP_ERROR_TIMEOUT; return NULL; } return next; } -static inline bool -CheckAddress(const sp_plugin_t *plugin, sp_context_t *ctx, cell_t *stk, cell_t addr) -{ - if (uint32_t(addr) >= plugin->mem_size) { - ctx->err = SP_ERROR_MEMACCESS; - return false; - } - - if (addr < ctx->hp) - return true; - - if (reinterpret_cast(plugin->memory + addr) < stk) { - ctx->err = SP_ERROR_MEMACCESS; - return false; - } - - return true; -} - -int -PopTrackerAndSetHeap(PluginRuntime *rt) -{ - sp_context_t *ctx = rt->GetBaseContext()->GetCtx(); - tracker_t *trk = ctx->tracker; - assert(trk->pCur > trk->pBase); - - trk->pCur--; - if (trk->pCur < trk->pBase) - return SP_ERROR_TRACKER_BOUNDS; - - ucell_t amt = *trk->pCur; - if (amt > (ctx->hp - rt->plugin()->data_size)) - return SP_ERROR_HEAPMIN; - - ctx->hp -= amt; - return SP_ERROR_NONE; -} - -int -PushTracker(sp_context_t *ctx, size_t amount) -{ - tracker_t *trk = ctx->tracker; - - if ((size_t)(trk->pCur - trk->pBase) >= trk->size) - return SP_ERROR_TRACKER_BOUNDS; - - if (trk->pCur + 1 - (trk->pBase + trk->size) == 0) { - size_t disp = trk->size - 1; - trk->size *= 2; - trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t)); - - if (!trk->pBase) - return SP_ERROR_TRACKER_BOUNDS; - - trk->pCur = trk->pBase + disp; - } - - *trk->pCur++ = amount; - return SP_ERROR_NONE; -} - -cell_t -NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params) -{ - cell_t save_sp = ctx->sp; - cell_t save_hp = ctx->hp; - - ctx->n_idx = native_idx; - - sp_native_t *native = &ctx->plugin->natives[native_idx]; - - if (native->status == SP_NATIVE_UNBOUND) { - ctx->n_err = SP_ERROR_INVALID_NATIVE; - return 0; - } - - cell_t result = native->pfn(ctx->basecx, params); - - if (ctx->n_err != SP_ERROR_NONE) - return result; - - if (save_sp != ctx->sp) { - ctx->n_err = SP_ERROR_STACKLEAK; - return result; - } - if (save_hp != ctx->hp) { - ctx->n_err = SP_ERROR_HEAPLEAK; - return result; - } - - return result; -} - -cell_t -BoundNativeCallback(sp_context_t *ctx, SPVM_NATIVE_FUNC pfn, cell_t *params) -{ - cell_t save_sp = ctx->sp; - cell_t save_hp = ctx->hp; - - cell_t result = pfn(ctx->basecx, params); - - if (ctx->n_err != SP_ERROR_NONE) - return result; - - if (save_sp != ctx->sp) { - ctx->n_err = SP_ERROR_STACKLEAK; - return result; - } - if (save_hp != ctx->hp) { - ctx->n_err = SP_ERROR_HEAPLEAK; - return result; - } - - return result; -} - -static inline bool -GenerateArray(PluginRuntime *rt, sp_context_t *ctx, cell_t dims, cell_t *stk, bool autozero) -{ - if (dims == 1) { - uint32_t size = *stk; - if (size == 0 || !ke::IsUint32MultiplySafe(size, 4)) { - ctx->err = SP_ERROR_ARRAY_TOO_BIG; - return false; - } - *stk = ctx->hp; - - uint32_t bytes = size * 4; - - ctx->hp += bytes; - if (uintptr_t(ctx->plugin->memory + ctx->hp) >= uintptr_t(stk)) { - ctx->err = SP_ERROR_HEAPLOW; - return false; - } - - if ((ctx->err = PushTracker(ctx, bytes)) != SP_ERROR_NONE) - return false; - - if (autozero) - memset(ctx->plugin->memory + ctx->hp, 0, bytes); - - return true; - } - - if ((ctx->err = GenerateFullArray(rt, dims, stk, autozero)) != SP_ERROR_NONE) - return false; - - return true; -} - int Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) { @@ -236,21 +83,25 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) if (!IsValidOffset(aCodeStart) || aCodeStart > plugin->pcode_size) return SP_ERROR_INVALID_INSTRUCTION; - sp_context_t *ctx = rt->GetBaseContext()->GetCtx(); - ctx->err = SP_ERROR_NONE; + PluginContext *cx = rt->GetBaseContext(); + + int err = SP_ERROR_NONE; // Save the original frm. BaseContext won't, and if we error, we won't hit // the stack unwinding code. - cell_t orig_frm = ctx->frm; + cell_t orig_frm = cx->frm(); + + cell_t &frm = *cx->addressOfFrm(); + cell_t &sp = *cx->addressOfSp(); cell_t pri = 0; cell_t alt = 0; cell_t *cip = code + (aCodeStart / 4); - cell_t *stk = reinterpret_cast(plugin->memory + ctx->sp); + cell_t *stk = reinterpret_cast(plugin->memory + sp); for (;;) { if (cip >= codeend) { - ctx->err = SP_ERROR_INVALID_INSTRUCTION; + err = SP_ERROR_INVALID_INSTRUCTION; goto error; } @@ -281,7 +132,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) break; case OP_ZERO_S: - Write(plugin, ctx->frm + *cip++, 0); + Write(plugin, frm + *cip++, 0); break; case OP_PUSH_PRI: @@ -321,7 +172,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) int i = 1; do { - cell_t addr = ctx->frm + *cip++; + cell_t addr = frm + *cip++; *--stk = addr; } while (i++ < n); break; @@ -339,7 +190,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) int i = 1; do { - cell_t value = Read(plugin, ctx->frm + *cip++); + cell_t value = Read(plugin, frm + *cip++); *--stk = value; } while (i++ < n); break; @@ -384,9 +235,9 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_PROC: { - *--stk = ctx->frm; + *--stk = frm; *--stk = 0; - ctx->frm = uintptr_t(stk) - uintptr_t(plugin->memory); + frm = uintptr_t(stk) - uintptr_t(plugin->memory); break; } @@ -505,14 +356,16 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_INC_S: { cell_t offset = *cip++; - cell_t value = Read(plugin, ctx->frm + offset); - Write(plugin, ctx->frm + offset, value + 1); + cell_t value = Read(plugin, frm + offset); + Write(plugin, frm + offset, value + 1); break; } case OP_INC_I: - if (!CheckAddress(plugin, ctx, stk, pri)) + if (!cx->checkAddress(stk, pri)) { + err = SP_ERROR_MEMACCESS; goto error; + } Write(plugin, pri, Read(plugin, pri) + 1); break; @@ -533,14 +386,16 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_DEC_S: { cell_t offset = *cip++; - cell_t value = Read(plugin, ctx->frm + offset); - Write(plugin, ctx->frm + offset, value - 1); + cell_t value = Read(plugin, frm + offset); + Write(plugin, frm + offset, value - 1); break; } case OP_DEC_I: - if (!CheckAddress(plugin, ctx, stk, pri)) + if (!cx->checkAddress(stk, pri)) { + err = SP_ERROR_MEMACCESS; goto error; + } Write(plugin, pri, Read(plugin, pri) - 1); break; @@ -552,27 +407,27 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) break; case OP_LOAD_S_PRI: - pri = Read(plugin, ctx->frm + *cip++); + pri = Read(plugin, frm + *cip++); break; case OP_LOAD_S_ALT: - alt = Read(plugin, ctx->frm + *cip++); + alt = Read(plugin, frm + *cip++); break; case OP_LOAD_S_BOTH: - pri = Read(plugin, ctx->frm + *cip++); - alt = Read(plugin, ctx->frm + *cip++); + pri = Read(plugin, frm + *cip++); + alt = Read(plugin, frm + *cip++); break; case OP_LREF_S_PRI: { - pri = Read(plugin, ctx->frm + *cip++); + pri = Read(plugin, frm + *cip++); pri = Read(plugin, pri); break; } case OP_LREF_S_ALT: { - alt = Read(plugin, ctx->frm + *cip++); + alt = Read(plugin, frm + *cip++); alt = Read(plugin, alt); break; } @@ -585,10 +440,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) break; case OP_ADDR_PRI: - pri = ctx->frm + *cip++; + pri = frm + *cip++; break; case OP_ADDR_ALT: - alt = ctx->frm + *cip++; + alt = frm + *cip++; break; case OP_STOR_PRI: @@ -599,10 +454,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) break; case OP_STOR_S_PRI: - Write(plugin, ctx->frm + *cip++, pri); + Write(plugin, frm + *cip++, pri); break; case OP_STOR_S_ALT: - Write(plugin, ctx->frm +*cip++, alt); + Write(plugin, frm +*cip++, alt); break; case OP_IDXADDR: @@ -612,7 +467,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_SREF_S_PRI: { cell_t offset = *cip++; - cell_t addr = Read(plugin, ctx->frm + offset); + cell_t addr = Read(plugin, frm + offset); Write(plugin, addr, pri); break; } @@ -620,7 +475,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_SREF_S_ALT: { cell_t offset = *cip++; - cell_t addr = Read(plugin, ctx->frm + offset); + cell_t addr = Read(plugin, frm + offset); Write(plugin, addr, alt); break; } @@ -644,8 +499,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_LIDX: pri = alt + pri * 4; - if (!CheckAddress(plugin, ctx, stk, pri)) + if (!cx->checkAddress(stk, pri)) { + err = SP_ERROR_MEMACCESS; goto error; + } pri = Read(plugin, pri); break; @@ -653,8 +510,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) { cell_t val = *cip++; pri = alt + (pri << val); - if (!CheckAddress(plugin, ctx, stk, pri)) + if (!cx->checkAddress(stk, pri)) { + err = SP_ERROR_MEMACCESS; goto error; + } pri = Read(plugin, pri); break; } @@ -671,19 +530,23 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) { cell_t offset = *cip++; cell_t value = *cip++; - Write(plugin, ctx->frm + offset, value); + Write(plugin, frm + offset, value); break; } case OP_LOAD_I: - if (!CheckAddress(plugin, ctx, stk, pri)) + if (!cx->checkAddress(stk, pri)) { + err = SP_ERROR_MEMACCESS; goto error; + } pri = Read(plugin, pri); break; case OP_STOR_I: - if (!CheckAddress(plugin, ctx, stk, alt)) + if (!cx->checkAddress(stk, alt)) { + err = SP_ERROR_MEMACCESS; goto error; + } Write(plugin, alt, pri); break; @@ -693,11 +556,11 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) cell_t dividend = (op == OP_SDIV) ? pri : alt; cell_t divisor = (op == OP_SDIV) ? alt : pri; if (divisor == 0) { - ctx->err = SP_ERROR_DIVIDE_BY_ZERO; + err = SP_ERROR_DIVIDE_BY_ZERO; goto error; } if (dividend == INT_MIN && divisor == -1) { - ctx->err = SP_ERROR_INTEGER_OVERFLOW; + err = SP_ERROR_INTEGER_OVERFLOW; goto error; } pri = dividend / divisor; @@ -708,8 +571,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_LODB_I: { cell_t val = *cip++; - if (!CheckAddress(plugin, ctx, stk, pri)) + if (!cx->checkAddress(stk, pri)) { + err = SP_ERROR_MEMACCESS; goto error; + } pri = Read(plugin, pri); if (val == 1) pri &= 0xff; @@ -721,8 +586,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_STRB_I: { cell_t val = *cip++; - if (!CheckAddress(plugin, ctx, stk, alt)) + if (!cx->checkAddress(stk, alt)) { + err = SP_ERROR_MEMACCESS; goto error; + } if (val == 1) *reinterpret_cast(plugin->memory + alt) = pri; else if (val == 2) @@ -735,10 +602,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_RETN: { stk++; - ctx->frm = *stk++; + frm = *stk++; stk += *stk + 1; *rval = pri; - ctx->err = SP_ERROR_NONE; + err = SP_ERROR_NONE; goto done; } @@ -766,19 +633,19 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) { cell_t amount = *cip++; if (!IsValidOffset(amount)) { - ctx->err = SP_ERROR_INVALID_INSTRUCTION; + err = SP_ERROR_INVALID_INSTRUCTION; goto error; } stk += amount / 4; if (amount > 0) { if (uintptr_t(stk) >= uintptr_t(plugin->memory + plugin->mem_size)) { - ctx->err = SP_ERROR_STACKMIN; + err = SP_ERROR_STACKMIN; goto error; } } else { - if (uintptr_t(stk) < uintptr_t(plugin->memory + ctx->hp + STACK_MARGIN)) { - ctx->err = SP_ERROR_STACKLOW; + if (uintptr_t(stk) < uintptr_t(plugin->memory + cx->hp() + STACK_MARGIN)) { + err = SP_ERROR_STACKLOW; goto error; } } @@ -789,17 +656,17 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) { cell_t amount = *cip++; - alt = ctx->hp; - ctx->hp += amount; + alt = cx->hp(); + *cx->addressOfHp() += amount; if (amount > 0) { - if (uintptr_t(plugin->memory + ctx->hp) > uintptr_t(stk)) { - ctx->err = SP_ERROR_HEAPLOW; + if (uintptr_t(plugin->memory + cx->hp()) > uintptr_t(stk)) { + err = SP_ERROR_HEAPLOW; goto error; } } else { - if (uint32_t(ctx->hp) < plugin->data_size) { - ctx->err = SP_ERROR_HEAPMIN; + if (uint32_t(cx->hp()) < plugin->data_size) { + err = SP_ERROR_HEAPMIN; goto error; } } @@ -807,50 +674,50 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) } case OP_JUMP: - if ((cip = JumpTarget(plugin, ctx, cip, true)) == NULL) + if ((cip = JumpTarget(plugin, cip, true, &err)) == NULL) goto error; break; case OP_JZER: - if ((cip = JumpTarget(plugin, ctx, cip, pri == 0)) == NULL) + if ((cip = JumpTarget(plugin, cip, pri == 0, &err)) == NULL) goto error; break; case OP_JNZ: - if ((cip = JumpTarget(plugin, ctx, cip, pri != 0)) == NULL) + if ((cip = JumpTarget(plugin, cip, pri != 0, &err)) == NULL) goto error; break; case OP_JEQ: - if ((cip = JumpTarget(plugin, ctx, cip, pri == alt)) == NULL) + if ((cip = JumpTarget(plugin, cip, pri == alt, &err)) == NULL) goto error; break; case OP_JNEQ: - if ((cip = JumpTarget(plugin, ctx, cip, pri != alt)) == NULL) + if ((cip = JumpTarget(plugin, cip, pri != alt, &err)) == NULL) goto error; break; case OP_JSLESS: - if ((cip = JumpTarget(plugin, ctx, cip, pri < alt)) == NULL) + if ((cip = JumpTarget(plugin, cip, pri < alt, &err)) == NULL) goto error; break; case OP_JSLEQ: - if ((cip = JumpTarget(plugin, ctx, cip, pri <= alt)) == NULL) + if ((cip = JumpTarget(plugin, cip, pri <= alt, &err)) == NULL) goto error; break; case OP_JSGRTR: - if ((cip = JumpTarget(plugin, ctx, cip, pri > alt)) == NULL) + if ((cip = JumpTarget(plugin, cip, pri > alt, &err)) == NULL) goto error; break; case OP_JSGEQ: - if ((cip = JumpTarget(plugin, ctx, cip, pri >= alt)) == NULL) + if ((cip = JumpTarget(plugin, cip, pri >= alt, &err)) == NULL) goto error; break; case OP_TRACKER_PUSH_C: { cell_t amount = *cip++; - int error = PushTracker(ctx, amount * 4); + int error = cx->pushTracker(amount * 4); if (error != SP_ERROR_NONE) { - ctx->err = error; + err = error; goto error; } break; @@ -858,23 +725,23 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_TRACKER_POP_SETHEAP: { - int error = PopTrackerAndSetHeap(rt); + int error = cx->popTrackerAndSetHeap(); if (error != SP_ERROR_NONE) { - ctx->err = error; + err = error; goto error; } break; } case OP_BREAK: - ctx->cip = uintptr_t(cip - 1) - uintptr_t(plugin->pcode); + *cx->addressOfCip() = uintptr_t(cip - 1) - uintptr_t(plugin->pcode); break; case OP_BOUNDS: { cell_t value = *cip++; if (uint32_t(pri) > uint32_t(value)) { - ctx->err = SP_ERROR_ARRAY_BOUNDS; + err = SP_ERROR_ARRAY_BOUNDS; goto error; } break; @@ -885,26 +752,24 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) cell_t offset = *cip++; if (!IsValidOffset(offset) || uint32_t(offset) >= plugin->pcode_size) { - ctx->err = SP_ERROR_INSTRUCTION_PARAM; - goto error; - } - - if (ctx->rp >= SP_MAX_RETURN_STACK) { - ctx->err = SP_ERROR_STACKLOW; + err = SP_ERROR_INSTRUCTION_PARAM; goto error; } // For debugging. uintptr_t rcip = uintptr_t(cip - 2) - uintptr_t(plugin->pcode); - ctx->rstk_cips[ctx->rp++] = rcip; - ctx->cip = offset; - ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory); + if (!cx->pushReturnCip(rcip)) { + err = SP_ERROR_STACKLOW; + goto error; + } + *cx->addressOfCip() = offset; + sp = uintptr_t(stk) - uintptr_t(plugin->memory); int err = Interpret(rt, offset, &pri); - stk = reinterpret_cast(plugin->memory + ctx->sp); - ctx->cip = rcip; - ctx->rp--; + stk = reinterpret_cast(plugin->memory + sp); + *cx->addressOfCip() = rcip; + cx->popReturnCip(); if (err != SP_ERROR_NONE) goto error; @@ -915,7 +780,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) case OP_GENARRAY_Z: { cell_t val = *cip++; - if (!GenerateArray(rt, ctx, val, stk, op == OP_GENARRAY_Z)) + if ((err = cx->generateArray(val, stk, op == OP_GENARRAY_Z)) != SP_ERROR_NONE) goto error; stk += (val - 1) * 4; @@ -928,7 +793,7 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) uint32_t native_index = *cip++; if (native_index >= plugin->num_natives) { - ctx->err = SP_ERROR_INSTRUCTION_PARAM; + err = SP_ERROR_INSTRUCTION_PARAM; goto error; } @@ -938,10 +803,10 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) *--stk = num_params; } - ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory); - pri = NativeCallback(ctx, native_index, stk); - if (ctx->n_err != SP_ERROR_NONE) { - ctx->err = ctx->n_err; + sp = uintptr_t(stk) - uintptr_t(plugin->memory); + pri = cx->invokeNative(native_index, stk); + if (cx->GetLastNativeError() != SP_ERROR_NONE) { + err = cx->GetLastNativeError(); goto error; } @@ -965,26 +830,28 @@ Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval) } } - if ((cip = Jump(plugin, ctx, target)) == NULL) + if ((cip = Jump(plugin, target)) == NULL) { + err = SP_ERROR_INVALID_INSTRUCTION; goto error; + } break; } default: { - ctx->err = SP_ERROR_INVALID_INSTRUCTION; + err = SP_ERROR_INVALID_INSTRUCTION; goto error; } } // switch } done: - assert(orig_frm == ctx->frm); - ctx->sp = uintptr_t(stk) - uintptr_t(plugin->memory); - return ctx->err; + assert(orig_frm == frm); + sp = uintptr_t(stk) - uintptr_t(plugin->memory); + return err; error: - ctx->frm = orig_frm; + frm = orig_frm; goto done; } diff --git a/sourcepawn/jit/interpreter.h b/sourcepawn/jit/interpreter.h index b4603c96..c367874d 100644 --- a/sourcepawn/jit/interpreter.h +++ b/sourcepawn/jit/interpreter.h @@ -22,19 +22,6 @@ #include "plugin-runtime.h" #include "plugin-context.h" -struct tracker_t -{ - size_t size; - ucell_t *pBase; - ucell_t *pCur; -}; - int Interpret(PluginRuntime *rt, uint32_t aCodeStart, cell_t *rval); -int GenerateFullArray(PluginRuntime *rt, uint32_t argc, cell_t *argv, int autozero); -cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params); -cell_t BoundNativeCallback(sp_context_t *ctx, SPVM_NATIVE_FUNC pfn, cell_t *params); -int PopTrackerAndSetHeap(PluginRuntime *rt); -int PushTracker(sp_context_t *ctx, size_t amount); - #endif // _include_sourcepawn_interpreter_h_ diff --git a/sourcepawn/jit/jit_shared.h b/sourcepawn/jit/jit_shared.h index 8c615bf4..ef1c7ec1 100644 --- a/sourcepawn/jit/jit_shared.h +++ b/sourcepawn/jit/jit_shared.h @@ -68,27 +68,8 @@ namespace SourcePawn } sp_plugin_t; } -struct tracker_t; class PluginContext; -typedef struct sp_context_s -{ - cell_t hp; /**< Heap pointer */ - cell_t sp; /**< Stack pointer */ - cell_t frm; /**< Frame pointer */ - cell_t rval; /**< Return value from InvokeFunction() */ - int32_t cip; /**< Code pointer last error occurred in */ - int32_t err; /**< Error last set by interpreter */ - int32_t n_err; /**< Error code set by a native */ - uint32_t n_idx; /**< Current native index being executed */ - tracker_t *tracker; - sp_plugin_t *plugin; - PluginContext *basecx; - void * vm[8]; /**< VM-specific pointers */ - cell_t rp; /**< Return stack pointer */ - cell_t rstk_cips[SP_MAX_RETURN_STACK]; -} sp_context_t; - //#define SPFLAG_PLUGIN_DEBUG (1<<0) #define SPFLAG_PLUGIN_PAUSED (1<<1) diff --git a/sourcepawn/jit/plugin-context.cpp b/sourcepawn/jit/plugin-context.cpp index 79d0e451..22dd7ef1 100644 --- a/sourcepawn/jit/plugin-context.cpp +++ b/sourcepawn/jit/plugin-context.cpp @@ -51,25 +51,21 @@ PluginContext::PluginContext(PluginRuntime *pRuntime) m_pNullString = NULL; } - m_ctx.hp = m_pRuntime->plugin()->data_size; - m_ctx.sp = m_pRuntime->plugin()->mem_size - sizeof(cell_t); - m_ctx.frm = m_ctx.sp; - m_ctx.n_err = SP_ERROR_NONE; - m_ctx.n_idx = SP_ERROR_NONE; - m_ctx.rp = 0; + 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; - m_ctx.tracker = new tracker_t; - m_ctx.tracker->pBase = (ucell_t *)malloc(1024); - m_ctx.tracker->pCur = m_ctx.tracker->pBase; - m_ctx.tracker->size = 1024 / sizeof(cell_t); - m_ctx.basecx = this; - m_ctx.plugin = const_cast(pRuntime->plugin()); + tracker_.pBase = (ucell_t *)malloc(1024); + tracker_.pCur = tracker_.pBase; + tracker_.size = 1024 / sizeof(cell_t); } PluginContext::~PluginContext() { - free(m_ctx.tracker->pBase); - delete m_ctx.tracker; + free(tracker_.pBase); } IVirtualMachine * @@ -84,12 +80,6 @@ PluginContext::GetContext() return reinterpret_cast((IPluginContext * )this); } -sp_context_t * -PluginContext::GetCtx() -{ - return &m_ctx; -} - bool PluginContext::IsDebugging() { @@ -137,7 +127,7 @@ PluginContext::ThrowNativeErrorEx(int error, const char *msg, ...) if (!m_InExec) return 0; - m_ctx.n_err = error; + native_error_ = error; if (msg) { va_list ap; @@ -155,7 +145,7 @@ PluginContext::ThrowNativeError(const char *msg, ...) if (!m_InExec) return 0; - m_ctx.n_err = SP_ERROR_NATIVE; + native_error_ = SP_ERROR_NATIVE; if (msg) { va_list ap; @@ -187,21 +177,21 @@ PluginContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys_a /** * Check if the space between the heap and stack is sufficient. */ - if ((cell_t)(m_ctx.sp - m_ctx.hp - realmem) < STACKMARGIN) + if ((cell_t)(sp_ - hp_ - realmem) < STACKMARGIN) return SP_ERROR_HEAPLOW; - addr = (cell_t *)(m_pRuntime->plugin()->memory + m_ctx.hp); + addr = (cell_t *)(m_pRuntime->plugin()->memory + hp_); /* store size of allocation in cells */ *addr = (cell_t)cells; addr++; - m_ctx.hp += sizeof(cell_t); + hp_ += sizeof(cell_t); - *local_addr = m_ctx.hp; + *local_addr = hp_; if (phys_addr) *phys_addr = addr; - m_ctx.hp += realmem; + hp_ += realmem; return SP_ERROR_NONE; } @@ -214,16 +204,16 @@ 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 >= m_ctx.sp) + if (local_addr < (cell_t)m_pRuntime->plugin()->data_size || local_addr >= sp_) return SP_ERROR_INVALID_ADDRESS; addr = (cell_t *)(m_pRuntime->plugin()->memory + local_addr); cellcount = (*addr) * sizeof(cell_t); /* check if this memory count looks valid */ - if ((signed)(m_ctx.hp - cellcount - sizeof(cell_t)) != local_addr) + if ((signed)(hp_ - cellcount - sizeof(cell_t)) != local_addr) return SP_ERROR_INVALID_ADDRESS; - m_ctx.hp = local_addr; + hp_ = local_addr; return SP_ERROR_NONE; } @@ -235,7 +225,7 @@ PluginContext::HeapRelease(cell_t local_addr) if (local_addr < (cell_t)m_pRuntime->plugin()->data_size) return SP_ERROR_INVALID_ADDRESS; - m_ctx.hp = local_addr - sizeof(cell_t); + hp_ = local_addr - sizeof(cell_t); return SP_ERROR_NONE; } @@ -327,7 +317,7 @@ PluginContext::BindNativeToAny(SPVM_NATIVE_FUNC native) int PluginContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr) { - if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) || + if (((local_addr >= hp_) && (local_addr < sp_)) || (local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size)) { return SP_ERROR_INVALID_ADDRESS; @@ -360,7 +350,7 @@ PluginContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t arra int PluginContext::LocalToString(cell_t local_addr, char **addr) { - if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) || + if (((local_addr >= hp_) && (local_addr < sp_)) || (local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size)) { return SP_ERROR_INVALID_ADDRESS; @@ -382,7 +372,7 @@ PluginContext::StringToLocal(cell_t local_addr, size_t bytes, const char *source char *dest; size_t len; - if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) || + if (((local_addr >= hp_) && (local_addr < sp_)) || (local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size)) { return SP_ERROR_INVALID_ADDRESS; @@ -445,7 +435,7 @@ PluginContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const char size_t len; bool needtocheck = false; - if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) || + if (((local_addr >= hp_) && (local_addr < sp_)) || (local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size)) { @@ -550,7 +540,7 @@ PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigne if (m_pRuntime->IsPaused()) return SP_ERROR_NOT_RUNNABLE; - if ((cell_t)(m_ctx.hp + 16*sizeof(cell_t)) > (cell_t)(m_ctx.sp - (sizeof(cell_t) * (num_params + 1)))) + if ((cell_t)(hp_ + 16*sizeof(cell_t)) > (cell_t)(sp_ - (sizeof(cell_t) * (num_params + 1)))) return SP_ERROR_STACKLOW; if (result == NULL) @@ -580,25 +570,25 @@ PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigne uint32_t save_n_idx; cell_t save_sp, save_hp, save_rp, save_cip; - save_sp = m_ctx.sp; - save_hp = m_ctx.hp; + save_sp = sp_; + save_hp = hp_; save_exec = m_InExec; - save_n_idx = m_ctx.n_idx; - save_rp = m_ctx.rp; - save_cip = m_ctx.cip; + save_n_idx = last_native_; + save_rp = rp_; + save_cip = cip_; /* Push parameters */ - m_ctx.sp -= sizeof(cell_t) * (num_params + 1); - sp = (cell_t *)(m_pRuntime->plugin()->memory + m_ctx.sp); + sp_ -= sizeof(cell_t) * (num_params + 1); + sp = (cell_t *)(m_pRuntime->plugin()->memory + sp_); sp[0] = num_params; for (unsigned int i = 0; i < num_params; i++) sp[i + 1] = params[i]; /* Clear internal state */ - m_ctx.n_err = SP_ERROR_NONE; - m_ctx.n_idx = 0; + native_error_ = SP_ERROR_NONE; + last_native_ = -1; m_MsgCache[0] = '\0'; m_CustomMsg = false; m_InExec = true; @@ -615,23 +605,23 @@ PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigne m_InExec = save_exec; if (ir == SP_ERROR_NONE) { - m_ctx.n_err = SP_ERROR_NONE; - if (m_ctx.sp != save_sp) { + native_error_ = SP_ERROR_NONE; + if (sp_ != save_sp) { ir = SP_ERROR_STACKLEAK; _SetErrorMessage("Stack leak detected: sp:%d should be %d!", - m_ctx.sp, + sp_, save_sp); } - if (m_ctx.hp != save_hp) { + if (hp_ != save_hp) { ir = SP_ERROR_HEAPLEAK; _SetErrorMessage("Heap leak detected: hp:%d should be %d!", - m_ctx.hp, + hp_, save_hp); } - if (m_ctx.rp != save_rp) { + if (rp_ != save_rp) { ir = SP_ERROR_STACKLEAK; _SetErrorMessage("Return stack leak detected: rp:%d should be %d!", - m_ctx.rp, + rp_, save_rp); } } @@ -642,13 +632,13 @@ PluginContext::Execute2(IPluginFunction *function, const cell_t *params, unsigne if (ir != SP_ERROR_NONE) Environment::get()->ReportError(m_pRuntime, ir, m_MsgCache, save_rp); - m_ctx.sp = save_sp; - m_ctx.hp = save_hp; - m_ctx.rp = save_rp; + sp_ = save_sp; + hp_ = save_hp; + rp_ = save_rp; - m_ctx.cip = save_cip; - m_ctx.n_idx = save_n_idx; - m_ctx.n_err = SP_ERROR_NONE; + cip_ = save_cip; + last_native_ = save_n_idx; + native_error_ = SP_ERROR_NONE; m_MsgCache[0] = '\0'; m_CustomMsg = false; @@ -780,13 +770,13 @@ DebugInfo::LookupLine(ucell_t addr, uint32_t *line) int PluginContext::GetLastNativeError() { - return m_ctx.n_err; + return native_error_; } cell_t * PluginContext::GetLocalParams() { - return (cell_t *)(m_pRuntime->plugin()->memory + m_ctx.frm + (2 * sizeof(cell_t))); + return (cell_t *)(m_pRuntime->plugin()->memory + frm_ + (2 * sizeof(cell_t))); } void @@ -812,5 +802,250 @@ PluginContext::GetKey(int k, void **value) void PluginContext::ClearLastNativeError() { - m_ctx.n_err = SP_ERROR_NONE; + native_error_ = SP_ERROR_NONE; } + +int +PluginContext::popTrackerAndSetHeap() +{ + assert(tracker_.pCur > tracker_.pBase); + + tracker_.pCur--; + if (tracker_.pCur < tracker_.pBase) + return SP_ERROR_TRACKER_BOUNDS; + + ucell_t amt = *tracker_.pCur; + if (amt > (hp_ - m_pRuntime->plugin()->data_size)) + return SP_ERROR_HEAPMIN; + + hp_ -= amt; + return SP_ERROR_NONE; +} + +int +PluginContext::pushTracker(uint32_t amount) +{ + if ((size_t)(tracker_.pCur - tracker_.pBase) >= tracker_.size) + return SP_ERROR_TRACKER_BOUNDS; + + if (tracker_.pCur + 1 - (tracker_.pBase + tracker_.size) == 0) { + size_t disp = tracker_.size - 1; + tracker_.size *= 2; + tracker_.pBase = (ucell_t *)realloc(tracker_.pBase, tracker_.size * sizeof(cell_t)); + + if (!tracker_.pBase) + return SP_ERROR_TRACKER_BOUNDS; + + tracker_.pCur = tracker_.pBase + disp; + } + + *tracker_.pCur++ = amount; + return SP_ERROR_NONE; +} + +cell_t +PluginContext::invokeNative(ucell_t native_idx, cell_t *params) +{ + cell_t save_sp = sp_; + cell_t save_hp = hp_; + + // 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]; + + if (native->status == SP_NATIVE_UNBOUND) { + native_error_ = SP_ERROR_INVALID_NATIVE; + return 0; + } + + cell_t result = native->pfn(this, params); + + if (native_error_ != SP_ERROR_NONE) + return result; + + if (save_sp != sp_) { + native_error_ = SP_ERROR_STACKLEAK; + return result; + } + if (save_hp != hp_) { + native_error_ = SP_ERROR_HEAPLEAK; + return result; + } + + return result; +} + +cell_t +PluginContext::invokeBoundNative(SPVM_NATIVE_FUNC pfn, cell_t *params) +{ + cell_t save_sp = sp_; + cell_t save_hp = hp_; + + cell_t result = pfn(this, params); + + if (native_error_ != SP_ERROR_NONE) + return result; + + if (save_sp != sp_) { + native_error_ = SP_ERROR_STACKLEAK; + return result; + } + if (save_hp != hp_) { + native_error_ = SP_ERROR_HEAPLEAK; + return result; + } + + return result; +} + +struct array_creation_t +{ + const cell_t *dim_list; /* Dimension sizes */ + cell_t dim_count; /* Number of dimensions */ + cell_t *data_offs; /* Current offset AFTER the indirection vectors (data) */ + cell_t *base; /* array base */ +}; + +static cell_t +GenerateInnerArrayIndirectionVectors(array_creation_t *ar, int dim, cell_t cur_offs) +{ + cell_t write_offs = cur_offs; + cell_t *data_offs = ar->data_offs; + + cur_offs += ar->dim_list[dim]; + + // Dimension n-x where x > 2 will have sub-vectors. + // Otherwise, we just need to reference the data section. + if (ar->dim_count > 2 && dim < ar->dim_count - 2) { + // For each index at this dimension, write offstes to our sub-vectors. + // After we write one sub-vector, we generate its sub-vectors recursively. + // At the end, we're given the next offset we can use. + for (int i = 0; i < ar->dim_list[dim]; i++) { + ar->base[write_offs] = (cur_offs - write_offs) * sizeof(cell_t); + write_offs++; + cur_offs = GenerateInnerArrayIndirectionVectors(ar, dim + 1, cur_offs); + } + } else { + // In this section, there are no sub-vectors, we need to write offsets + // to the data. This is separate so the data stays in one big chunk. + // The data offset will increment by the size of the last dimension, + // because that is where the data is finally computed as. + for (int i = 0; i < ar->dim_list[dim]; i++) { + ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell_t); + write_offs++; + *data_offs = *data_offs + ar->dim_list[dim + 1]; + } + } + + return cur_offs; +} + +static cell_t +calc_indirection(const array_creation_t *ar, cell_t dim) +{ + cell_t size = ar->dim_list[dim]; + if (dim < ar->dim_count - 2) + size += ar->dim_list[dim] * calc_indirection(ar, dim + 1); + return size; +} + +static cell_t +GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], cell_t _dimcount, bool autozero) +{ + array_creation_t ar; + cell_t data_offs; + + /* Reverse the dimensions */ + cell_t dim_list[sDIMEN_MAX]; + int cur_dim = 0; + for (int i = _dimcount - 1; i >= 0; i--) + dim_list[cur_dim++] = dims[i]; + + ar.base = arraybase; + ar.dim_list = dim_list; + ar.dim_count = _dimcount; + ar.data_offs = &data_offs; + + data_offs = calc_indirection(&ar, 0); + GenerateInnerArrayIndirectionVectors(&ar, 0, 0); + return data_offs; +} + +int +PluginContext::generateFullArray(uint32_t argc, cell_t *argv, int autozero) +{ + // Calculate how many cells are needed. + if (argv[0] <= 0) + return SP_ERROR_ARRAY_TOO_BIG; + + uint32_t cells = argv[0]; + + for (uint32_t dim = 1; dim < argc; dim++) { + cell_t dimsize = argv[dim]; + if (dimsize <= 0) + return SP_ERROR_ARRAY_TOO_BIG; + if (!ke::IsUint32MultiplySafe(cells, dimsize)) + return SP_ERROR_ARRAY_TOO_BIG; + cells *= uint32_t(dimsize); + if (!ke::IsUint32AddSafe(cells, dimsize)) + return SP_ERROR_ARRAY_TOO_BIG; + cells += uint32_t(dimsize); + } + + if (!ke::IsUint32MultiplySafe(cells, 4)) + return SP_ERROR_ARRAY_TOO_BIG; + + uint32_t bytes = cells * 4; + if (!ke::IsUint32AddSafe(hp_, bytes)) + return SP_ERROR_ARRAY_TOO_BIG; + + uint32_t new_hp = hp_ + bytes; + cell_t *dat_hp = reinterpret_cast(m_pRuntime->plugin()->memory + new_hp); + + // argv, coincidentally, is STK. + if (dat_hp >= argv - STACK_MARGIN) + return SP_ERROR_HEAPLOW; + + if (int err = pushTracker(bytes)) + return err; + + cell_t *base = reinterpret_cast(m_pRuntime->plugin()->memory + hp_); + cell_t offs = GenerateArrayIndirectionVectors(base, argv, argc, !!autozero); + assert(size_t(offs) == cells); + + argv[argc - 1] = hp_; + hp_ = new_hp; + return SP_ERROR_NONE; +} + +int +PluginContext::generateArray(cell_t dims, cell_t *stk, bool autozero) +{ + if (dims == 1) { + uint32_t size = *stk; + if (size == 0 || !ke::IsUint32MultiplySafe(size, 4)) + return SP_ERROR_ARRAY_TOO_BIG; + *stk = hp_; + + uint32_t bytes = size * 4; + + hp_ += bytes; + if (uintptr_t(m_pRuntime->plugin()->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); + + return SP_ERROR_NONE; + } + + if (int err = generateFullArray(dims, stk, autozero)) + return err; + + return SP_ERROR_NONE; +} + diff --git a/sourcepawn/jit/plugin-context.h b/sourcepawn/jit/plugin-context.h index a96da4a2..278a776a 100644 --- a/sourcepawn/jit/plugin-context.h +++ b/sourcepawn/jit/plugin-context.h @@ -18,6 +18,18 @@ #include "plugin-runtime.h" #include "jit_shared.h" +struct HeapTracker +{ + HeapTracker() + : size(0), + pBase(nullptr), + pCur(nullptr) + {} + size_t size; + ucell_t *pBase; + ucell_t *pCur; +}; + class PluginContext : public IPluginContext { public: @@ -27,7 +39,6 @@ class PluginContext : public IPluginContext public: //IPluginContext IVirtualMachine *GetVirtualMachine(); sp_context_t *GetContext(); - sp_context_t *GetCtx(); bool IsDebugging(); int SetDebugBreak(void *newpfn, void *oldpfn); IPluginDebugInfo *GetDebugInfo(); @@ -76,6 +87,94 @@ class PluginContext : public IPluginContext public: bool IsInExec(); + static inline size_t offsetOfRp() { + return offsetof(PluginContext, rp_); + } + static inline size_t offsetOfRstkCips() { + return offsetof(PluginContext, rstk_cips_); + } + static inline size_t offsetOfTracker() { + return offsetof(PluginContext, tracker_); + } + static inline size_t offsetOfLastNative() { + return offsetof(PluginContext, last_native_); + } + static inline size_t offsetOfNativeError() { + return offsetof(PluginContext, native_error_); + } + static inline size_t offsetOfSp() { + return offsetof(PluginContext, sp_); + } + static inline size_t offsetOfRuntime() { + return offsetof(PluginContext, m_pRuntime); + } + + int32_t *addressOfCip() { + return &cip_; + } + int32_t *addressOfSp() { + return &sp_; + } + cell_t *addressOfFrm() { + return &frm_; + } + cell_t *addressOfHp() { + return &hp_; + } + + int32_t cip() const { + return cip_; + } + cell_t frm() const { + return frm_; + } + cell_t hp() const { + return hp_; + } + + // Return stack logic. + bool pushReturnCip(cell_t cip) { + if (rp_ >= SP_MAX_RETURN_STACK) + return false; + rstk_cips_[rp_++] = cip; + return true; + } + void popReturnCip() { + assert(rp_ > 0); + rp_--; + } + cell_t rp() const { + return rp_; + } + cell_t getReturnStackCip(int index) { + assert(index >= 0 && index < SP_MAX_RETURN_STACK); + return rstk_cips_[index]; + } + + int popTrackerAndSetHeap(); + int pushTracker(uint32_t amount); + + int generateArray(cell_t dims, cell_t *stk, bool autozero); + int generateFullArray(uint32_t argc, cell_t *argv, int autozero); + cell_t invokeNative(ucell_t native_idx, cell_t *params); + cell_t invokeBoundNative(SPVM_NATIVE_FUNC pfn, cell_t *params); + int lastNative() const { + return last_native_; + } + + inline bool checkAddress(cell_t *stk, cell_t addr) { + if (uint32_t(addr) >= m_pRuntime->plugin()->mem_size) + return false; + + if (addr < hp_) + return true; + + if (reinterpret_cast(m_pRuntime->plugin()->memory + addr) < stk) + return false; + + return true; + } + private: void SetErrorMessage(const char *msg, va_list ap); void _SetErrorMessage(const char *msg, ...); @@ -87,9 +186,27 @@ class PluginContext : public IPluginContext bool m_CustomMsg; bool m_InExec; PluginRuntime *m_pRuntime; - sp_context_t m_ctx; void *m_keys[4]; bool m_keys_set[4]; + + // Tracker for local HEA growth. + HeapTracker tracker_; + + // Return stack. + cell_t rp_; + cell_t rstk_cips_[SP_MAX_RETURN_STACK]; + + // Track the currently executing native index, and any error it throws. + int32_t last_native_; + int native_error_; + + // Most recent CIP. + int32_t cip_; + + // Stack, heap, and frame pointer. + cell_t sp_; + cell_t hp_; + cell_t frm_; }; #endif //_INCLUDE_SOURCEPAWN_BASECONTEXT_H_ diff --git a/sourcepawn/jit/plugin-runtime.h b/sourcepawn/jit/plugin-runtime.h index 9f0706a0..1ced62dc 100644 --- a/sourcepawn/jit/plugin-runtime.h +++ b/sourcepawn/jit/plugin-runtime.h @@ -96,6 +96,10 @@ class PluginRuntime return m_JitFunctions[i]; } + static inline size_t offsetToPlugin() { + return offsetof(PluginRuntime, m_plugin); + } + private: void SetupFloatNativeRemapping(); diff --git a/sourcepawn/jit/x86/code-stubs-x86.cpp b/sourcepawn/jit/x86/code-stubs-x86.cpp index 2b7a4b94..9b85bb1a 100644 --- a/sourcepawn/jit/x86/code-stubs-x86.cpp +++ b/sourcepawn/jit/x86/code-stubs-x86.cpp @@ -47,12 +47,19 @@ CodeStubs::CompileInvokeStub() __ push(ebx); // ebp - 12 __ push(esp); // ebp - 16 + // ebx = cx __ movl(ebx, Operand(ebp, 8 + 4 * 0)); - __ movl(eax, Operand(ebp, 8 + 4 * 1)); - __ movl(ecx, Operand(ebp, 8 + 4 * 2)); + + // 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))); // Set up run-time registers. - __ movl(edi, Operand(ebx, offsetof(sp_context_t, sp))); + __ movl(edi, Operand(ebx, PluginContext::offsetOfSp())); __ addl(edi, eax); __ movl(esi, eax); __ movl(ebx, edi); @@ -64,8 +71,8 @@ CodeStubs::CompileInvokeStub() __ call(ecx); // Get input context, store rval. - __ movl(ecx, Operand(ebp, 8 + 4 * 0)); - __ movl(Operand(ecx, offsetof(sp_context_t, rval)), pri); + __ movl(ecx, Operand(ebp, 8 + 4 * 2)); + __ movl(Operand(ecx, 0), pri); // Set no error. __ movl(eax, SP_ERROR_NONE); @@ -75,7 +82,8 @@ CodeStubs::CompileInvokeStub() Label ret; __ bind(&ret); __ subl(stk, dat); - __ movl(Operand(ecx, offsetof(sp_context_t, sp)), stk); + __ movl(ecx, Operand(ebp, 8 + 4 * 0)); + __ movl(Operand(ecx, PluginContext::offsetOfSp()), stk); // Restore stack. __ movl(esp, Operand(ebp, -16)); diff --git a/sourcepawn/jit/x86/jit_x86.cpp b/sourcepawn/jit/x86/jit_x86.cpp index 5c00bcac..b66643f3 100644 --- a/sourcepawn/jit/x86/jit_x86.cpp +++ b/sourcepawn/jit/x86/jit_x86.cpp @@ -77,128 +77,6 @@ OpToCondition(OPCODE op) } } -struct array_creation_t -{ - const cell_t *dim_list; /* Dimension sizes */ - cell_t dim_count; /* Number of dimensions */ - cell_t *data_offs; /* Current offset AFTER the indirection vectors (data) */ - cell_t *base; /* array base */ -}; - -static cell_t -GenerateInnerArrayIndirectionVectors(array_creation_t *ar, int dim, cell_t cur_offs) -{ - cell_t write_offs = cur_offs; - cell_t *data_offs = ar->data_offs; - - cur_offs += ar->dim_list[dim]; - - // Dimension n-x where x > 2 will have sub-vectors. - // Otherwise, we just need to reference the data section. - if (ar->dim_count > 2 && dim < ar->dim_count - 2) { - // For each index at this dimension, write offstes to our sub-vectors. - // After we write one sub-vector, we generate its sub-vectors recursively. - // At the end, we're given the next offset we can use. - for (int i = 0; i < ar->dim_list[dim]; i++) { - ar->base[write_offs] = (cur_offs - write_offs) * sizeof(cell_t); - write_offs++; - cur_offs = GenerateInnerArrayIndirectionVectors(ar, dim + 1, cur_offs); - } - } else { - // In this section, there are no sub-vectors, we need to write offsets - // to the data. This is separate so the data stays in one big chunk. - // The data offset will increment by the size of the last dimension, - // because that is where the data is finally computed as. - for (int i = 0; i < ar->dim_list[dim]; i++) { - ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell_t); - write_offs++; - *data_offs = *data_offs + ar->dim_list[dim + 1]; - } - } - - return cur_offs; -} - -static cell_t -calc_indirection(const array_creation_t *ar, cell_t dim) -{ - cell_t size = ar->dim_list[dim]; - if (dim < ar->dim_count - 2) - size += ar->dim_list[dim] * calc_indirection(ar, dim + 1); - return size; -} - -static cell_t -GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], cell_t _dimcount, bool autozero) -{ - array_creation_t ar; - cell_t data_offs; - - /* Reverse the dimensions */ - cell_t dim_list[sDIMEN_MAX]; - int cur_dim = 0; - for (int i = _dimcount - 1; i >= 0; i--) - dim_list[cur_dim++] = dims[i]; - - ar.base = arraybase; - ar.dim_list = dim_list; - ar.dim_count = _dimcount; - ar.data_offs = &data_offs; - - data_offs = calc_indirection(&ar, 0); - GenerateInnerArrayIndirectionVectors(&ar, 0, 0); - return data_offs; -} - -int -GenerateFullArray(PluginRuntime *rt, uint32_t argc, cell_t *argv, int autozero) -{ - sp_context_t *ctx = rt->GetBaseContext()->GetCtx(); - - // Calculate how many cells are needed. - if (argv[0] <= 0) - return SP_ERROR_ARRAY_TOO_BIG; - - uint32_t cells = argv[0]; - - for (uint32_t dim = 1; dim < argc; dim++) { - cell_t dimsize = argv[dim]; - if (dimsize <= 0) - return SP_ERROR_ARRAY_TOO_BIG; - if (!ke::IsUint32MultiplySafe(cells, dimsize)) - return SP_ERROR_ARRAY_TOO_BIG; - cells *= uint32_t(dimsize); - if (!ke::IsUint32AddSafe(cells, dimsize)) - return SP_ERROR_ARRAY_TOO_BIG; - cells += uint32_t(dimsize); - } - - if (!ke::IsUint32MultiplySafe(cells, 4)) - return SP_ERROR_ARRAY_TOO_BIG; - - uint32_t bytes = cells * 4; - if (!ke::IsUint32AddSafe(ctx->hp, bytes)) - return SP_ERROR_ARRAY_TOO_BIG; - - uint32_t new_hp = ctx->hp + bytes; - cell_t *dat_hp = reinterpret_cast(rt->plugin()->memory + new_hp); - - // argv, coincidentally, is STK. - if (dat_hp >= argv - STACK_MARGIN) - return SP_ERROR_HEAPLOW; - - if (int err = PushTracker(rt->GetBaseContext()->GetCtx(), bytes)) - return err; - - cell_t *base = reinterpret_cast(rt->plugin()->memory + ctx->hp); - cell_t offs = GenerateArrayIndirectionVectors(base, argv, argc, !!autozero); - assert(size_t(offs) == cells); - - argv[argc - 1] = ctx->hp; - ctx->hp = new_hp; - return SP_ERROR_NONE; -} - #if !defined NDEBUG static const char * GetFunctionName(const sp_plugin_t *plugin, uint32_t offs) @@ -303,6 +181,7 @@ CompileFromThunk(PluginRuntime *runtime, cell_t pcode_offs, void **addrp, char * Compiler::Compiler(PluginRuntime *rt, cell_t pcode_offs) : env_(Environment::get()), rt_(rt), + context_(rt->GetBaseContext()), plugin_(rt->plugin()), error_(SP_ERROR_NONE), pcode_start_(pcode_offs), @@ -384,6 +263,37 @@ Compiler::emit(int *errp) return new CompiledFunction(code, pcode_start_, edges.take()); } +// Helpers for invoking context members. +static int +InvokePushTracker(PluginContext *cx, uint32_t amount) +{ + return cx->pushTracker(amount); +} + +static int +InvokePopTrackerAndSetHeap(PluginContext *cx) +{ + return cx->popTrackerAndSetHeap(); +} + +static cell_t +InvokeNativeHelper(PluginContext *cx, ucell_t native_idx, cell_t *params) +{ + return cx->invokeNative(native_idx, params); +} + +static cell_t +InvokeBoundNativeHelper(PluginContext *cx, SPVM_NATIVE_FUNC fn, cell_t *params) +{ + return cx->invokeBoundNative(fn, params); +} + +static int +InvokeGenerateFullArray(PluginContext *cx, uint32_t argc, cell_t *argv, int autozero) +{ + return cx->generateFullArray(argc, argv, autozero); +} + bool Compiler::emitOp(OPCODE op) { @@ -1258,8 +1168,8 @@ Compiler::emitOp(OPCODE op) __ push(alt); __ push(amount * 4); - __ push(intptr_t(rt_->GetBaseContext()->GetCtx())); - __ call(ExternalAddress((void *)PushTracker)); + __ push(intptr_t(rt_->GetBaseContext())); + __ call(ExternalAddress((void *)InvokePushTracker)); __ addl(esp, 8); __ testl(eax, eax); __ j(not_zero, &extern_error_); @@ -1276,8 +1186,8 @@ Compiler::emitOp(OPCODE op) __ push(alt); // Get the context pointer and call the sanity checker. - __ push(intptr_t(rt_)); - __ call(ExternalAddress((void *)PopTrackerAndSetHeap)); + __ push(intptr_t(rt_->GetBaseContext())); + __ call(ExternalAddress((void *)InvokePopTrackerAndSetHeap)); __ addl(esp, 4); __ testl(eax, eax); __ j(not_zero, &extern_error_); @@ -1296,8 +1206,6 @@ Compiler::emitOp(OPCODE op) case OP_HALT: __ align(16); - __ movl(tmp, intptr_t(rt_->GetBaseContext()->GetCtx())); - __ movl(Operand(tmp, offsetof(sp_context_t, rval)), pri); __ movl(pri, readCell()); __ jmp(&extern_error_); break; @@ -1405,8 +1313,8 @@ Compiler::emitGenArray(bool autozero) __ shll(tmp, 2); __ push(tmp); - __ push(intptr_t(rt_->GetBaseContext()->GetCtx())); - __ call(ExternalAddress((void *)PushTracker)); + __ push(intptr_t(rt_->GetBaseContext())); + __ call(ExternalAddress((void *)InvokePushTracker)); __ addl(esp, 4); __ pop(tmp); __ shrl(tmp, 2); @@ -1432,8 +1340,8 @@ Compiler::emitGenArray(bool autozero) __ push(autozero ? 1 : 0); __ push(stk); __ push(val); - __ push(intptr_t(rt_)); - __ call(ExternalAddress((void *)GenerateFullArray)); + __ push(intptr_t(context_)); + __ call(ExternalAddress((void *)InvokeGenerateFullArray)); __ addl(esp, 4 * sizeof(void *)); // restore pri to tmp @@ -1462,8 +1370,8 @@ Compiler::emitCall() // eax = context // ecx = rp - __ movl(eax, intptr_t(rt_->GetBaseContext()->GetCtx())); - __ movl(ecx, Operand(eax, offsetof(sp_context_t, rp))); + __ movl(eax, intptr_t(rt_->GetBaseContext())); + __ movl(ecx, Operand(eax, PluginContext::offsetOfRp())); // Check if the return stack is used up. __ cmpl(ecx, SP_MAX_RETURN_STACK); @@ -1471,10 +1379,10 @@ Compiler::emitCall() // Add to the return stack. uintptr_t cip = uintptr_t(cip_ - 2) - uintptr_t(plugin_->pcode); - __ movl(Operand(eax, ecx, ScaleFour, offsetof(sp_context_t, rstk_cips)), cip); + __ movl(Operand(eax, ecx, ScaleFour, PluginContext::offsetOfRstkCips()), cip); // Increment the return stack pointer. - __ addl(Operand(eax, offsetof(sp_context_t, rp)), 1); + __ addl(Operand(eax, PluginContext::offsetOfRp()), 1); // Store the CIP of the function we're about to call. __ movl(Operand(cipAddr()), offset); @@ -1495,8 +1403,8 @@ Compiler::emitCall() __ movl(Operand(cipAddr()), cip); // Mark us as leaving the last frame. - __ movl(tmp, intptr_t(rt_->GetBaseContext()->GetCtx())); - __ subl(Operand(tmp, offsetof(sp_context_t, rp)), 1); + __ movl(tmp, intptr_t(rt_->GetBaseContext())); + __ subl(Operand(tmp, PluginContext::offsetOfRp()), 1); return true; } @@ -1581,13 +1489,13 @@ Compiler::emitNativeCall(OPCODE op) // Push the last parameter for the C++ function. __ push(stk); + __ movl(eax, intptr_t(rt_->GetBaseContext())); + __ movl(Operand(eax, PluginContext::offsetOfLastNative()), native_index); + // Relocate our absolute stk to be dat-relative, and update the context's // view. - __ movl(eax, intptr_t(rt_->GetBaseContext()->GetCtx())); __ subl(stk, dat); - __ movl(Operand(eax, offsetof(sp_context_t, sp)), stk); - - __ movl(Operand(eax, offsetof(sp_context_t, n_idx)), native_index); + __ movl(Operand(eax, PluginContext::offsetOfSp()), stk); sp_native_t *native = rt_->GetNativeByIndex(native_index); if ((native->status != SP_NATIVE_BOUND) || @@ -1596,18 +1504,18 @@ Compiler::emitNativeCall(OPCODE op) // The native is either unbound, or it could become unbound in the // future. Invoke the slower native callback. __ push(native_index); - __ push(eax); - __ call(ExternalAddress((void *)NativeCallback)); + __ push(intptr_t(rt_->GetBaseContext())); + __ call(ExternalAddress((void *)InvokeNativeHelper)); } else { // The native is bound so we have a few more guarantees. __ push(intptr_t(native->pfn)); - __ push(eax); - __ call(ExternalAddress((void *)BoundNativeCallback)); + __ push(intptr_t(rt_->GetBaseContext())); + __ call(ExternalAddress((void *)InvokeBoundNativeHelper)); } // Check for errors. - __ movl(ecx, intptr_t(rt_->GetBaseContext()->GetCtx())); - __ movl(ecx, Operand(ecx, offsetof(sp_context_t, n_err))); + __ movl(ecx, intptr_t(rt_->GetBaseContext())); + __ movl(ecx, Operand(ecx, PluginContext::offsetOfNativeError())); __ testl(ecx, ecx); __ j(not_zero, &extern_error_); @@ -1798,8 +1706,8 @@ Compiler::emitErrorPaths() if (extern_error_.used()) { __ bind(&extern_error_); - __ movl(eax, intptr_t(rt_->GetBaseContext()->GetCtx())); - __ movl(eax, Operand(eax, offsetof(sp_context_t, n_err))); + __ movl(eax, intptr_t(rt_->GetBaseContext())); + __ movl(eax, Operand(eax, PluginContext::offsetOfNativeError())); __ jmp(ExternalAddress(env_->stubs()->ReturnStub())); } } diff --git a/sourcepawn/jit/x86/jit_x86.h b/sourcepawn/jit/x86/jit_x86.h index 3af246b2..ddea57ed 100644 --- a/sourcepawn/jit/x86/jit_x86.h +++ b/sourcepawn/jit/x86/jit_x86.h @@ -91,22 +91,20 @@ class Compiler void emitFloatCmp(ConditionCode cc); ExternalAddress cipAddr() { - sp_context_t *ctx = rt_->GetBaseContext()->GetCtx(); - return ExternalAddress(&ctx->cip); + return ExternalAddress(context_->addressOfCip()); } ExternalAddress hpAddr() { - sp_context_t *ctx = rt_->GetBaseContext()->GetCtx(); - return ExternalAddress(&ctx->hp); + return ExternalAddress(context_->addressOfHp()); } ExternalAddress frmAddr() { - sp_context_t *ctx = rt_->GetBaseContext()->GetCtx(); - return ExternalAddress(&ctx->frm); + return ExternalAddress(context_->addressOfFrm()); } private: AssemblerX86 masm; sp::Environment *env_; PluginRuntime *rt_; + PluginContext *context_; const sp_plugin_t *plugin_; int error_; uint32_t pcode_start_;