sourcepawn/jit/BaseRuntime.cpp
author David Anderson <dvander@alliedmods.net>
Wed Aug 14 23:54:25 2013 -0700 (2013-08-14)
changeset 4013 6201c1180e0e
parent 3977 fca2b54741ca
child 4327 60e4042f2589
permissions -rw-r--r--
Implement a watchdog timer for scripts that take too long to execute (bug 5837, r=fyren).
     1 // vim: set ts=8 sts=2 sw=2 tw=99 et:
     2 #include <stdio.h>
     3 #include <stdlib.h>
     4 #include <string.h>
     5 #include <assert.h>
     6 #include "BaseRuntime.h"
     7 #include "sp_vm_engine.h"
     8 #include "x86/jit_x86.h"
     9 #include "sp_vm_basecontext.h"
    10 #include "engine2.h"
    11 
    12 #include "md5/md5.h"
    13 
    14 using namespace SourcePawn;
    15 
    16 static inline bool
    17 IsPointerCellAligned(void *p)
    18 {
    19   return uintptr_t(p) % 4 == 0;
    20 }
    21 
    22 BaseRuntime::BaseRuntime()
    23   : m_Debug(&m_plugin),
    24     m_pCtx(NULL), 
    25     m_PubFuncs(NULL),
    26     m_PubJitFuncs(NULL),
    27     co_(NULL),
    28     m_CompSerial(0)
    29 {
    30   memset(&m_plugin, 0, sizeof(m_plugin));
    31 
    32   m_MaxFuncs = 0;
    33   m_NumFuncs = 0;
    34   float_table_ = NULL;
    35   function_map_ = NULL;
    36   alt_pcode_ = NULL;
    37   
    38   memset(m_CodeHash, 0, sizeof(m_CodeHash));
    39   memset(m_DataHash, 0, sizeof(m_DataHash));
    40 
    41   ke::AutoLock lock(g_Jit.Mutex());
    42   g_Jit.RegisterRuntime(this);
    43 }
    44 
    45 BaseRuntime::~BaseRuntime()
    46 {
    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());
    52 
    53   g_Jit.DeregisterRuntime(this);
    54 
    55   for (uint32_t i = 0; i < m_plugin.num_publics; i++)
    56     delete m_PubFuncs[i];
    57   delete [] m_PubFuncs;
    58   delete [] m_PubJitFuncs;
    59   delete [] float_table_;
    60   delete [] function_map_;
    61   delete [] alt_pcode_;
    62 
    63   for (size_t i = 0; i < m_JitFunctions.length(); i++)
    64     delete m_JitFunctions[i];
    65 
    66   delete m_pCtx;
    67   if (co_)
    68     co_->Abort();
    69 
    70   free(m_plugin.base);
    71   delete [] m_plugin.memory;
    72   delete [] m_plugin.publics;
    73   delete [] m_plugin.pubvars;
    74   delete [] m_plugin.natives;
    75   free(m_plugin.name);
    76 }
    77 
    78 void
    79 BaseRuntime::SetupFloatNativeRemapping()
    80 {
    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;
   117     }
   118   }
   119 }
   120 
   121 unsigned
   122 BaseRuntime::GetNativeReplacement(size_t index)
   123 {
   124   if (!float_table_[index].found)
   125     return OP_NOP;
   126   return float_table_[index].index;
   127 }
   128 
   129 void
   130 BaseRuntime::SetName(const char *name)
   131 {
   132   m_plugin.name = strdup(name);
   133 }
   134 
   135 static cell_t InvalidNative(IPluginContext *pCtx, const cell_t *params)
   136 {
   137   return pCtx->ThrowNativeErrorEx(SP_ERROR_INVALID_NATIVE, "Invalid native");
   138 }
   139 
   140 int BaseRuntime::CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base)
   141 {
   142   char *nameptr;
   143   uint8_t sectnum = 0;
   144   sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
   145 
   146   memset(&m_plugin, 0, sizeof(m_plugin));
   147 
   148   m_plugin.base = base;
   149   m_plugin.base_size = hdr->imagesize;
   150 
   151   if (hdr->version == 0x0101)
   152     m_plugin.debug.unpacked = true;
   153 
   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);
   159       break;
   160     }
   161   }
   162 
   163   sectnum = 0;
   164 
   165   /* Now read the rest of the sections */
   166   while (sectnum < hdr->sections) {
   167     nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
   168 
   169     if (!(m_plugin.pcode) && !strcmp(nameptr, ".code")) {
   170       sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
   171 
   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;
   176 
   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_));
   187 
   188         m_plugin.pcode = alt_pcode_;
   189       }
   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;
   199 
   200       publics = (sp_file_publics_t *)(base + secptr->dataoffs);
   201       m_plugin.num_publics = secptr->size / sizeof(sp_file_publics_t);
   202 
   203       if (m_plugin.num_publics > 0) {
   204         m_plugin.publics = new sp_public_t[m_plugin.num_publics];
   205 
   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;
   210         }
   211       }
   212     } else if ((m_plugin.pubvars == NULL) && !strcmp(nameptr, ".pubvars")) {
   213       sp_file_pubvars_t *pubvars;
   214 
   215       pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
   216       m_plugin.num_pubvars = secptr->size / sizeof(sp_file_pubvars_t);
   217 
   218       if (m_plugin.num_pubvars > 0) {
   219         m_plugin.pubvars = new sp_pubvar_t[m_plugin.num_pubvars];
   220 
   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);
   224         }
   225       }
   226     } else if ((m_plugin.natives == NULL) && !strcmp(nameptr, ".natives")) {
   227       sp_file_natives_t *natives;
   228 
   229       natives = (sp_file_natives_t *)(base + secptr->dataoffs);
   230       m_plugin.num_natives = secptr->size / sizeof(sp_file_natives_t);
   231 
   232       if (m_plugin.num_natives > 0) {
   233         m_plugin.natives = new sp_native_t[m_plugin.num_natives];
   234 
   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;
   241         }
   242       }
   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;
   258     }
   259 
   260     secptr++;
   261     sectnum++;
   262   }
   263 
   264   if (m_plugin.pcode == NULL || m_plugin.data == NULL)
   265     return SP_ERROR_FILE_FORMAT;
   266 
   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) ))
   272   {
   273     return SP_ERROR_FILE_FORMAT;
   274   }
   275 
   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);
   281   }
   282 
   283   MD5 md5_pcode;
   284   md5_pcode.update(m_plugin.pcode, m_plugin.pcode_size);
   285   md5_pcode.finalize();
   286   md5_pcode.raw_digest(m_CodeHash);
   287   
   288   MD5 md5_data;
   289   md5_data.update(m_plugin.data, m_plugin.data_size);
   290   md5_data.finalize();
   291   md5_data.raw_digest(m_DataHash);
   292 
   293   m_plugin.profiler = g_engine2.GetProfiler();
   294   m_pCtx = new BaseContext(this);
   295   co_ = g_Jit.StartCompilation(this);
   296 
   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 *));
   301 
   302   return SP_ERROR_NONE;
   303 }
   304 
   305 void
   306 BaseRuntime::AddJittedFunction(JitFunction *fn)
   307 {
   308   m_JitFunctions.append(fn);
   309 
   310   cell_t pcode_offset = fn->GetPCodeAddress();
   311   assert(pcode_offset % 4 == 0);
   312 
   313   uint32_t pcode_index = pcode_offset / 4;
   314   assert(pcode_index < function_map_size_);
   315 
   316   function_map_[pcode_index] = fn;
   317 }
   318 
   319 JitFunction *
   320 BaseRuntime::GetJittedFunctionByOffset(cell_t pcode_offset)
   321 {
   322   assert(pcode_offset % 4 == 0);
   323 
   324   uint32_t pcode_index = pcode_offset / 4;
   325   assert(pcode_index < function_map_size_);
   326 
   327   return function_map_[pcode_index];
   328 }
   329 
   330 int
   331 BaseRuntime::FindNativeByName(const char *name, uint32_t *index)
   332 {
   333   for (uint32_t i=0; i<m_plugin.num_natives; i++) {
   334     if (strcmp(m_plugin.natives[i].name, name) == 0) {
   335       if (index)
   336         *index = i;
   337       return SP_ERROR_NONE;
   338     }
   339   }
   340 
   341   return SP_ERROR_NOT_FOUND;
   342 }
   343 
   344 int
   345 BaseRuntime::GetNativeByIndex(uint32_t index, sp_native_t **native)
   346 {
   347   if (index >= m_plugin.num_natives)
   348     return SP_ERROR_INDEX;
   349 
   350   if (native)
   351     *native = &(m_plugin.natives[index]);
   352 
   353   return SP_ERROR_NONE;
   354 }
   355 
   356 
   357 uint32_t
   358 BaseRuntime::GetNativesNum()
   359 {
   360   return m_plugin.num_natives;
   361 }
   362 
   363 int
   364 BaseRuntime::FindPublicByName(const char *name, uint32_t *index)
   365 {
   366   int diff, high, low;
   367   uint32_t mid;
   368 
   369   high = m_plugin.num_publics - 1;
   370   low = 0;
   371 
   372   while (low <= high) {
   373     mid = (low + high) / 2;
   374     diff = strcmp(m_plugin.publics[mid].name, name);
   375     if (diff == 0) {
   376       if (index)
   377         *index = mid;
   378       return SP_ERROR_NONE;
   379     } else if (diff < 0) {
   380       low = mid + 1;
   381     } else {
   382       high = mid - 1;
   383     }
   384   }
   385 
   386   return SP_ERROR_NOT_FOUND;
   387 }
   388 
   389 int
   390 BaseRuntime::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
   391 {
   392   if (index >= m_plugin.num_publics)
   393     return SP_ERROR_INDEX;
   394 
   395   if (pblic)
   396     *pblic = &(m_plugin.publics[index]);
   397 
   398   return SP_ERROR_NONE;
   399 }
   400 
   401 uint32_t
   402 BaseRuntime::GetPublicsNum()
   403 {
   404   return m_plugin.num_publics;
   405 }
   406 
   407 int
   408 BaseRuntime::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
   409 {
   410   if (index >= m_plugin.num_pubvars)
   411     return SP_ERROR_INDEX;
   412 
   413   if (pubvar)
   414     *pubvar = &(m_plugin.pubvars[index]);
   415 
   416   return SP_ERROR_NONE;
   417 }
   418 
   419 int
   420 BaseRuntime::FindPubvarByName(const char *name, uint32_t *index)
   421 {
   422   int diff, high, low;
   423   uint32_t mid;
   424 
   425   high = m_plugin.num_pubvars - 1;
   426   low = 0;
   427 
   428   while (low <= high) {
   429     mid = (low + high) / 2;
   430     diff = strcmp(m_plugin.pubvars[mid].name, name);
   431     if (diff == 0) {
   432       if (index)
   433         *index = mid;
   434       return SP_ERROR_NONE;
   435     } else if (diff < 0) {
   436       low = mid + 1;
   437     } else {
   438       high = mid - 1;
   439     }
   440   }
   441 
   442   return SP_ERROR_NOT_FOUND;
   443 }
   444 
   445 int
   446 BaseRuntime::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
   447 {
   448   if (index >= m_plugin.num_pubvars)
   449     return SP_ERROR_INDEX;
   450 
   451   *local_addr = (uint8_t *)m_plugin.pubvars[index].offs - m_plugin.memory;
   452   *phys_addr = m_plugin.pubvars[index].offs;
   453 
   454   return SP_ERROR_NONE;
   455 }
   456 
   457 uint32_t
   458 BaseRuntime::GetPubVarsNum()
   459 {
   460   return m_plugin.num_pubvars;
   461 }
   462 
   463 IPluginContext *
   464 BaseRuntime::GetDefaultContext()
   465 {
   466   return m_pCtx;
   467 }
   468 
   469 IPluginDebugInfo *
   470 BaseRuntime::GetDebugInfo()
   471 {
   472   return &m_Debug;
   473 }
   474 
   475 IPluginFunction *
   476 BaseRuntime::GetFunctionById(funcid_t func_id)
   477 {
   478   CFunction *pFunc = NULL;
   479 
   480   if (func_id & 1) {
   481     func_id >>= 1;
   482     if (func_id >= m_plugin.num_publics)
   483       return NULL;
   484     pFunc = m_PubFuncs[func_id];
   485     if (!pFunc) {
   486       m_PubFuncs[func_id] = new CFunction(this, 
   487         (func_id << 1) | 1,
   488         func_id);
   489       pFunc = m_PubFuncs[func_id];
   490     }
   491   }
   492 
   493   return pFunc;
   494 }
   495 
   496 IPluginFunction *
   497 BaseRuntime::GetFunctionByName(const char *public_name)
   498 {
   499   uint32_t index;
   500 
   501   if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
   502     return NULL;
   503 
   504   CFunction *pFunc = m_PubFuncs[index];
   505   if (!pFunc) {
   506     sp_public_t *pub = NULL;
   507     GetPublicByIndex(index, &pub);
   508     if (pub)
   509       m_PubFuncs[index] = new CFunction(this, (index << 1) | 1, index);
   510     pFunc = m_PubFuncs[index];
   511   }
   512 
   513   return pFunc;
   514 }
   515 
   516 bool BaseRuntime::IsDebugging()
   517 {
   518   return true;
   519 }
   520 
   521 void BaseRuntime::SetPauseState(bool paused)
   522 {
   523   if (paused)
   524   {
   525     m_plugin.run_flags |= SPFLAG_PLUGIN_PAUSED;
   526   }
   527   else
   528   {
   529     m_plugin.run_flags &= ~SPFLAG_PLUGIN_PAUSED;
   530   }
   531 }
   532 
   533 bool BaseRuntime::IsPaused()
   534 {
   535   return ((m_plugin.run_flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED);
   536 }
   537 
   538 size_t BaseRuntime::GetMemUsage()
   539 {
   540   size_t mem = 0;
   541 
   542   mem += sizeof(this);
   543   mem += sizeof(sp_plugin_t);
   544   mem += sizeof(BaseContext);
   545   mem += m_plugin.base_size;
   546 
   547   return mem;
   548 }
   549 
   550 unsigned char *BaseRuntime::GetCodeHash()
   551 {
   552   return m_CodeHash;
   553 }
   554 
   555 unsigned char *BaseRuntime::GetDataHash()
   556 {
   557   return m_DataHash;
   558 }
   559 
   560 BaseContext *BaseRuntime::GetBaseContext()
   561 {
   562   return m_pCtx;
   563 }
   564 
   565 int
   566 BaseRuntime::ApplyCompilationOptions(ICompilation *co)
   567 {
   568   if (co == NULL)
   569     return SP_ERROR_NONE;
   570 
   571   co_ = g_Jit.ApplyOptions(co_, co);
   572   m_plugin.prof_flags = ((CompData *)co_)->profile;
   573 
   574   return SP_ERROR_NONE;
   575 }
   576 
   577 int
   578 BaseRuntime::CreateBlank(uint32_t heastk)
   579 {
   580   memset(&m_plugin, 0, sizeof(m_plugin));
   581 
   582   /* Align to cell_t bytes */
   583   heastk += sizeof(cell_t);
   584   heastk -= heastk % sizeof(cell_t);
   585 
   586   m_plugin.mem_size = heastk;
   587   m_plugin.memory = new uint8_t[heastk];
   588 
   589   m_plugin.profiler = g_engine2.GetProfiler();
   590   m_pCtx = new BaseContext(this);
   591   co_ = g_Jit.StartCompilation(this);
   592 
   593   return SP_ERROR_NONE;
   594 }