1 // vim: set ts=8 sts=2 sw=2 tw=99 et:
6 #include "BaseRuntime.h"
7 #include "sp_vm_engine.h"
8 #include "x86/jit_x86.h"
9 #include "sp_vm_basecontext.h"
14 using namespace SourcePawn;
17 IsPointerCellAligned(void *p)
19 return uintptr_t(p) % 4 == 0;
22 BaseRuntime::BaseRuntime()
30 memset(&m_plugin, 0, sizeof(m_plugin));
38 memset(m_CodeHash, 0, sizeof(m_CodeHash));
39 memset(m_DataHash, 0, sizeof(m_DataHash));
41 ke::AutoLock lock(g_Jit.Mutex());
42 g_Jit.RegisterRuntime(this);
45 BaseRuntime::~BaseRuntime()
47 // The watchdog thread takes the global JIT lock while it patches all
48 // runtimes. It is not enough to ensure that the unlinking of the runtime is
49 // protected; we cannot delete functions or code while the watchdog might be
50 // executing. Therefore, the entire destructor is guarded.
51 ke::AutoLock lock(g_Jit.Mutex());
53 g_Jit.DeregisterRuntime(this);
55 for (uint32_t i = 0; i < m_plugin.num_publics; i++)
58 delete [] m_PubJitFuncs;
59 delete [] float_table_;
60 delete [] function_map_;
63 for (size_t i = 0; i < m_JitFunctions.length(); i++)
64 delete m_JitFunctions[i];
71 delete [] m_plugin.memory;
72 delete [] m_plugin.publics;
73 delete [] m_plugin.pubvars;
74 delete [] m_plugin.natives;
79 BaseRuntime::SetupFloatNativeRemapping()
81 float_table_ = new floattbl_t[m_plugin.num_natives];
82 for (size_t i = 0; i < m_plugin.num_natives; i++) {
83 const char *name = m_plugin.natives[i].name;
84 if (!strcmp(name, "FloatAbs")) {
85 float_table_[i].found = true;
86 float_table_[i].index = OP_FABS;
87 } else if (!strcmp(name, "FloatAdd")) {
88 float_table_[i].found = true;
89 float_table_[i].index = OP_FLOATADD;
90 } else if (!strcmp(name, "FloatSub")) {
91 float_table_[i].found = true;
92 float_table_[i].index = OP_FLOATSUB;
93 } else if (!strcmp(name, "FloatMul")) {
94 float_table_[i].found = true;
95 float_table_[i].index = OP_FLOATMUL;
96 } else if (!strcmp(name, "FloatDiv")) {
97 float_table_[i].found = true;
98 float_table_[i].index = OP_FLOATDIV;
99 } else if (!strcmp(name, "float")) {
100 float_table_[i].found = true;
101 float_table_[i].index = OP_FLOAT;
102 } else if (!strcmp(name, "FloatCompare")) {
103 float_table_[i].found = true;
104 float_table_[i].index = OP_FLOATCMP;
105 } else if (!strcmp(name, "RoundToZero")) {
106 float_table_[i].found = true;
107 float_table_[i].index = OP_RND_TO_ZERO;
108 } else if (!strcmp(name, "RoundToCeil")) {
109 float_table_[i].found = true;
110 float_table_[i].index = OP_RND_TO_CEIL;
111 } else if (!strcmp(name, "RoundToFloor")) {
112 float_table_[i].found = true;
113 float_table_[i].index = OP_RND_TO_FLOOR;
114 } else if (!strcmp(name, "RoundToNearest")) {
115 float_table_[i].found = true;
116 float_table_[i].index = OP_RND_TO_NEAREST;
122 BaseRuntime::GetNativeReplacement(size_t index)
124 if (!float_table_[index].found)
126 return float_table_[index].index;
130 BaseRuntime::SetName(const char *name)
132 m_plugin.name = strdup(name);
135 static cell_t InvalidNative(IPluginContext *pCtx, const cell_t *params)
137 return pCtx->ThrowNativeErrorEx(SP_ERROR_INVALID_NATIVE, "Invalid native");
140 int BaseRuntime::CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base)
144 sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
146 memset(&m_plugin, 0, sizeof(m_plugin));
148 m_plugin.base = base;
149 m_plugin.base_size = hdr->imagesize;
151 if (hdr->version == 0x0101)
152 m_plugin.debug.unpacked = true;
154 /* We have to read the name section first */
155 for (sectnum = 0; sectnum < hdr->sections; sectnum++) {
156 nameptr = (char *)(base + hdr->stringtab + secptr[sectnum].nameoffs);
157 if (strcmp(nameptr, ".names") == 0) {
158 m_plugin.stringbase = (const char *)(base + secptr[sectnum].dataoffs);
165 /* Now read the rest of the sections */
166 while (sectnum < hdr->sections) {
167 nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
169 if (!(m_plugin.pcode) && !strcmp(nameptr, ".code")) {
170 sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
172 if (cod->codeversion < SP_CODEVERS_JIT1)
173 return SP_ERROR_CODE_TOO_OLD;
174 if (cod->codeversion > SP_CODEVERS_JIT2)
175 return SP_ERROR_CODE_TOO_NEW;
177 m_plugin.pcode = base + secptr->dataoffs + cod->code;
178 m_plugin.pcode_size = cod->codesize;
179 m_plugin.flags = cod->flags;
180 m_plugin.pcode_version = cod->codeversion;
181 if (!IsPointerCellAligned(m_plugin.pcode)) {
182 // The JIT requires that pcode is cell-aligned, so if it's not, we
183 // remap the code segment to a new address.
184 alt_pcode_ = new uint8_t[m_plugin.pcode_size];
185 memcpy(alt_pcode_, m_plugin.pcode, m_plugin.pcode_size);
186 assert(IsPointerCellAligned(alt_pcode_));
188 m_plugin.pcode = alt_pcode_;
190 } else if (!(m_plugin.data) && !strcmp(nameptr, ".data")) {
191 sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs);
192 m_plugin.data = base + secptr->dataoffs + dat->data;
193 m_plugin.data_size = dat->datasize;
194 m_plugin.mem_size = dat->memsize;
195 m_plugin.memory = new uint8_t[m_plugin.mem_size];
196 memcpy(m_plugin.memory, m_plugin.data, m_plugin.data_size);
197 } else if ((m_plugin.publics == NULL) && !strcmp(nameptr, ".publics")) {
198 sp_file_publics_t *publics;
200 publics = (sp_file_publics_t *)(base + secptr->dataoffs);
201 m_plugin.num_publics = secptr->size / sizeof(sp_file_publics_t);
203 if (m_plugin.num_publics > 0) {
204 m_plugin.publics = new sp_public_t[m_plugin.num_publics];
206 for (uint32_t i = 0; i < m_plugin.num_publics; i++) {
207 m_plugin.publics[i].code_offs = publics[i].address;
208 m_plugin.publics[i].funcid = (i << 1) | 1;
209 m_plugin.publics[i].name = m_plugin.stringbase + publics[i].name;
212 } else if ((m_plugin.pubvars == NULL) && !strcmp(nameptr, ".pubvars")) {
213 sp_file_pubvars_t *pubvars;
215 pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
216 m_plugin.num_pubvars = secptr->size / sizeof(sp_file_pubvars_t);
218 if (m_plugin.num_pubvars > 0) {
219 m_plugin.pubvars = new sp_pubvar_t[m_plugin.num_pubvars];
221 for (uint32_t i = 0; i < m_plugin.num_pubvars; i++) {
222 m_plugin.pubvars[i].name = m_plugin.stringbase + pubvars[i].name;
223 m_plugin.pubvars[i].offs = (cell_t *)(m_plugin.memory + pubvars[i].address);
226 } else if ((m_plugin.natives == NULL) && !strcmp(nameptr, ".natives")) {
227 sp_file_natives_t *natives;
229 natives = (sp_file_natives_t *)(base + secptr->dataoffs);
230 m_plugin.num_natives = secptr->size / sizeof(sp_file_natives_t);
232 if (m_plugin.num_natives > 0) {
233 m_plugin.natives = new sp_native_t[m_plugin.num_natives];
235 for (uint32_t i = 0; i < m_plugin.num_natives; i++) {
236 m_plugin.natives[i].flags = 0;
237 m_plugin.natives[i].pfn = InvalidNative;
238 m_plugin.natives[i].status = SP_NATIVE_UNBOUND;
239 m_plugin.natives[i].user = NULL;
240 m_plugin.natives[i].name = m_plugin.stringbase + natives[i].name;
243 } else if (!(m_plugin.debug.files) && !strcmp(nameptr, ".dbg.files")) {
244 m_plugin.debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
245 } else if (!(m_plugin.debug.lines) && !strcmp(nameptr, ".dbg.lines")) {
246 m_plugin.debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
247 } else if (!(m_plugin.debug.symbols) && !strcmp(nameptr, ".dbg.symbols")) {
248 m_plugin.debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
249 } else if (!(m_plugin.debug.lines_num) && !strcmp(nameptr, ".dbg.info")) {
250 sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs);
251 m_plugin.debug.files_num = inf->num_files;
252 m_plugin.debug.lines_num = inf->num_lines;
253 m_plugin.debug.syms_num = inf->num_syms;
254 } else if (!(m_plugin.debug.stringbase) && !strcmp(nameptr, ".dbg.strings")) {
255 m_plugin.debug.stringbase = (const char *)(base + secptr->dataoffs);
256 } else if (strcmp(nameptr, ".dbg.natives") == 0) {
257 m_plugin.debug.unpacked = false;
264 if (m_plugin.pcode == NULL || m_plugin.data == NULL)
265 return SP_ERROR_FILE_FORMAT;
267 if ((m_plugin.flags & SP_FLAG_DEBUG) && (
268 !(m_plugin.debug.files) ||
269 !(m_plugin.debug.lines) ||
270 !(m_plugin.debug.symbols) ||
271 !(m_plugin.debug.stringbase) ))
273 return SP_ERROR_FILE_FORMAT;
276 if (m_plugin.num_publics > 0) {
277 m_PubFuncs = new CFunction *[m_plugin.num_publics];
278 memset(m_PubFuncs, 0, sizeof(CFunction *) * m_plugin.num_publics);
279 m_PubJitFuncs = new JitFunction *[m_plugin.num_publics];
280 memset(m_PubJitFuncs, 0, sizeof(JitFunction *) * m_plugin.num_publics);
284 md5_pcode.update(m_plugin.pcode, m_plugin.pcode_size);
285 md5_pcode.finalize();
286 md5_pcode.raw_digest(m_CodeHash);
289 md5_data.update(m_plugin.data, m_plugin.data_size);
291 md5_data.raw_digest(m_DataHash);
293 m_plugin.profiler = g_engine2.GetProfiler();
294 m_pCtx = new BaseContext(this);
295 co_ = g_Jit.StartCompilation(this);
297 SetupFloatNativeRemapping();
298 function_map_size_ = m_plugin.pcode_size / sizeof(cell_t) + 1;
299 function_map_ = new JitFunction *[function_map_size_];
300 memset(function_map_, 0, function_map_size_ * sizeof(JitFunction *));
302 return SP_ERROR_NONE;
306 BaseRuntime::AddJittedFunction(JitFunction *fn)
308 m_JitFunctions.append(fn);
310 cell_t pcode_offset = fn->GetPCodeAddress();
311 assert(pcode_offset % 4 == 0);
313 uint32_t pcode_index = pcode_offset / 4;
314 assert(pcode_index < function_map_size_);
316 function_map_[pcode_index] = fn;
320 BaseRuntime::GetJittedFunctionByOffset(cell_t pcode_offset)
322 assert(pcode_offset % 4 == 0);
324 uint32_t pcode_index = pcode_offset / 4;
325 assert(pcode_index < function_map_size_);
327 return function_map_[pcode_index];
331 BaseRuntime::FindNativeByName(const char *name, uint32_t *index)
333 for (uint32_t i=0; i<m_plugin.num_natives; i++) {
334 if (strcmp(m_plugin.natives[i].name, name) == 0) {
337 return SP_ERROR_NONE;
341 return SP_ERROR_NOT_FOUND;
345 BaseRuntime::GetNativeByIndex(uint32_t index, sp_native_t **native)
347 if (index >= m_plugin.num_natives)
348 return SP_ERROR_INDEX;
351 *native = &(m_plugin.natives[index]);
353 return SP_ERROR_NONE;
358 BaseRuntime::GetNativesNum()
360 return m_plugin.num_natives;
364 BaseRuntime::FindPublicByName(const char *name, uint32_t *index)
369 high = m_plugin.num_publics - 1;
372 while (low <= high) {
373 mid = (low + high) / 2;
374 diff = strcmp(m_plugin.publics[mid].name, name);
378 return SP_ERROR_NONE;
379 } else if (diff < 0) {
386 return SP_ERROR_NOT_FOUND;
390 BaseRuntime::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
392 if (index >= m_plugin.num_publics)
393 return SP_ERROR_INDEX;
396 *pblic = &(m_plugin.publics[index]);
398 return SP_ERROR_NONE;
402 BaseRuntime::GetPublicsNum()
404 return m_plugin.num_publics;
408 BaseRuntime::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
410 if (index >= m_plugin.num_pubvars)
411 return SP_ERROR_INDEX;
414 *pubvar = &(m_plugin.pubvars[index]);
416 return SP_ERROR_NONE;
420 BaseRuntime::FindPubvarByName(const char *name, uint32_t *index)
425 high = m_plugin.num_pubvars - 1;
428 while (low <= high) {
429 mid = (low + high) / 2;
430 diff = strcmp(m_plugin.pubvars[mid].name, name);
434 return SP_ERROR_NONE;
435 } else if (diff < 0) {
442 return SP_ERROR_NOT_FOUND;
446 BaseRuntime::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
448 if (index >= m_plugin.num_pubvars)
449 return SP_ERROR_INDEX;
451 *local_addr = (uint8_t *)m_plugin.pubvars[index].offs - m_plugin.memory;
452 *phys_addr = m_plugin.pubvars[index].offs;
454 return SP_ERROR_NONE;
458 BaseRuntime::GetPubVarsNum()
460 return m_plugin.num_pubvars;
464 BaseRuntime::GetDefaultContext()
470 BaseRuntime::GetDebugInfo()
476 BaseRuntime::GetFunctionById(funcid_t func_id)
478 CFunction *pFunc = NULL;
482 if (func_id >= m_plugin.num_publics)
484 pFunc = m_PubFuncs[func_id];
486 m_PubFuncs[func_id] = new CFunction(this,
489 pFunc = m_PubFuncs[func_id];
497 BaseRuntime::GetFunctionByName(const char *public_name)
501 if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
504 CFunction *pFunc = m_PubFuncs[index];
506 sp_public_t *pub = NULL;
507 GetPublicByIndex(index, &pub);
509 m_PubFuncs[index] = new CFunction(this, (index << 1) | 1, index);
510 pFunc = m_PubFuncs[index];
516 bool BaseRuntime::IsDebugging()
521 void BaseRuntime::SetPauseState(bool paused)
525 m_plugin.run_flags |= SPFLAG_PLUGIN_PAUSED;
529 m_plugin.run_flags &= ~SPFLAG_PLUGIN_PAUSED;
533 bool BaseRuntime::IsPaused()
535 return ((m_plugin.run_flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED);
538 size_t BaseRuntime::GetMemUsage()
543 mem += sizeof(sp_plugin_t);
544 mem += sizeof(BaseContext);
545 mem += m_plugin.base_size;
550 unsigned char *BaseRuntime::GetCodeHash()
555 unsigned char *BaseRuntime::GetDataHash()
560 BaseContext *BaseRuntime::GetBaseContext()
566 BaseRuntime::ApplyCompilationOptions(ICompilation *co)
569 return SP_ERROR_NONE;
571 co_ = g_Jit.ApplyOptions(co_, co);
572 m_plugin.prof_flags = ((CompData *)co_)->profile;
574 return SP_ERROR_NONE;
578 BaseRuntime::CreateBlank(uint32_t heastk)
580 memset(&m_plugin, 0, sizeof(m_plugin));
582 /* Align to cell_t bytes */
583 heastk += sizeof(cell_t);
584 heastk -= heastk % sizeof(cell_t);
586 m_plugin.mem_size = heastk;
587 m_plugin.memory = new uint8_t[heastk];
589 m_plugin.profiler = g_engine2.GetProfiler();
590 m_pCtx = new BaseContext(this);
591 co_ = g_Jit.StartCompilation(this);
593 return SP_ERROR_NONE;