ljx

FORK: LuaJIT with native 5.2 and 5.3 support
git clone https://git.neptards.moe/neptards/ljx.git
Log | Files | Refs | README

lj_clib.c (11057B)


      1 /*
      2 ** FFI C library loader.
      3 ** Copyright (C) 2005-2016 Mike Pall. See Copyright Notice in luajit.h
      4 */
      5 
      6 #include "lj_obj.h"
      7 
      8 #if LJ_HASFFI
      9 
     10 #include "lj_gc.h"
     11 #include "lj_err.h"
     12 #include "lj_tab.h"
     13 #include "lj_str.h"
     14 #include "lj_udata.h"
     15 #include "lj_ctype.h"
     16 #include "lj_cconv.h"
     17 #include "lj_cdata.h"
     18 #include "lj_clib.h"
     19 #include "lj_strfmt.h"
     20 
     21 /* -- OS-specific functions ----------------------------------------------- */
     22 
     23 #if LJ_TARGET_DLOPEN
     24 
     25 #include <dlfcn.h>
     26 #include <stdio.h>
     27 
     28 #if defined(RTLD_DEFAULT)
     29 #define CLIB_DEFHANDLE	RTLD_DEFAULT
     30 #elif LJ_TARGET_OSX || LJ_TARGET_BSD
     31 #define CLIB_DEFHANDLE	((void *)(intptr_t)-2)
     32 #else
     33 #define CLIB_DEFHANDLE	NULL
     34 #endif
     35 
     36 LJ_NORET LJ_NOINLINE static void clib_error_(lua_State *L)
     37 {
     38   lj_err_callermsg(L, dlerror());
     39 }
     40 
     41 #define clib_error(L, fmt, name)	clib_error_(L)
     42 
     43 #if defined(__CYGWIN__)
     44 #define CLIB_SOPREFIX	"cyg"
     45 #else
     46 #define CLIB_SOPREFIX	"lib"
     47 #endif
     48 
     49 #define CLIB_FMT        "%s"
     50 
     51 #if LJ_TARGET_OSX
     52 #define CLIB_SOEXT	".dylib"
     53 #elif defined(__CYGWIN__)
     54 #define CLIB_SOEXT	".dll"
     55 #else
     56 #define CLIB_SOEXT	".so"
     57 #endif
     58 
     59 static const char *clib_extname(lua_State *L, const char *name)
     60 {
     61   if (!strchr(name, '/')
     62 #ifdef __CYGWIN__
     63       && !strchr(name, '\\')
     64 #endif
     65      ) {
     66     int n = strlen(name);
     67     if ((n < (sizeof(CLIB_SOEXT) - 1)) ||
     68         (strcmp(name + n - sizeof(CLIB_SOEXT) + 1, CLIB_SOEXT))) {
     69       name = lj_strfmt_pushf(L, CLIB_FMT CLIB_SOEXT, name);
     70       L->top--;
     71 #ifdef __CYGWIN__
     72     } else {
     73       return name;
     74 #endif
     75     }
     76     if (!(name[0] == CLIB_SOPREFIX[0] && name[1] == CLIB_SOPREFIX[1] &&
     77 	  name[2] == CLIB_SOPREFIX[2])) {
     78       name = lj_strfmt_pushf(L, CLIB_SOPREFIX "%s", name);
     79       L->top--;
     80     }
     81   }
     82   return name;
     83 }
     84 
     85 /* Check for a recognized ld script line. */
     86 static const char *clib_check_lds(lua_State *L, const char *buf)
     87 {
     88   char *p, *e;
     89   if ((!strncmp(buf, "GROUP", 5) || !strncmp(buf, "INPUT", 5)) &&
     90       (p = strchr(buf, '('))) {
     91     while (*++p == ' ') ;
     92     for (e = p; *e && *e != ' ' && *e != ')'; e++) ;
     93     return strdata(lj_str_new(L, p, e-p));
     94   }
     95   return NULL;
     96 }
     97 
     98 /* Quick and dirty solution to resolve shared library name from ld script. */
     99 static const char *clib_resolve_lds(lua_State *L, const char *name)
    100 {
    101   FILE *fp = fopen(name, "r");
    102   const char *p = NULL;
    103   if (fp) {
    104     char buf[256];
    105     if (fgets(buf, sizeof(buf), fp)) {
    106       if (!strncmp(buf, "/* GNU ld script", 16)) {  /* ld script magic? */
    107 	while (fgets(buf, sizeof(buf), fp)) {  /* Check all lines. */
    108 	  p = clib_check_lds(L, buf);
    109 	  if (p) break;
    110 	}
    111       } else {  /* Otherwise check only the first line. */
    112 	p = clib_check_lds(L, buf);
    113       }
    114     }
    115     fclose(fp);
    116   }
    117   return p;
    118 }
    119 
    120 static void *clib_loadlib(lua_State *L, const char *name, int global)
    121 {
    122   void *h = dlopen(clib_extname(L, name),
    123 		   RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL));
    124   if (!h) {
    125     const char *e, *err = dlerror();
    126     if (*err == '/' && (e = strchr(err, ':')) &&
    127 	(name = clib_resolve_lds(L, strdata(lj_str_new(L, err, e-err))))) {
    128       h = dlopen(name, RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL));
    129       if (h) return h;
    130       err = dlerror();
    131     }
    132     lj_err_callermsg(L, err);
    133   }
    134   return h;
    135 }
    136 
    137 static void clib_unloadlib(CLibrary *cl)
    138 {
    139   if (cl->handle && cl->handle != CLIB_DEFHANDLE)
    140     dlclose(cl->handle);
    141 }
    142 
    143 static void *clib_getsym(CLibrary *cl, const char *name)
    144 {
    145   void *p = dlsym(cl->handle, name);
    146 #ifdef __MSYS__
    147   if (!p && cl->system)
    148     p = dlsym(NULL, name);
    149 #endif
    150   return p;
    151 }
    152 
    153 #elif LJ_TARGET_WINDOWS
    154 
    155 #define WIN32_LEAN_AND_MEAN
    156 #include <windows.h>
    157 
    158 #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
    159 #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS	4
    160 #define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT	2
    161 BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*);
    162 #endif
    163 
    164 #define CLIB_DEFHANDLE	((void *)-1)
    165 
    166 /* Default libraries. */
    167 enum {
    168   CLIB_HANDLE_EXE,
    169   CLIB_HANDLE_DLL,
    170   CLIB_HANDLE_CRT,
    171   CLIB_HANDLE_KERNEL32,
    172   CLIB_HANDLE_USER32,
    173   CLIB_HANDLE_GDI32,
    174   CLIB_HANDLE_MAX
    175 };
    176 
    177 static void *clib_def_handle[CLIB_HANDLE_MAX];
    178 
    179 LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt,
    180 					    const char *name)
    181 {
    182   DWORD err = GetLastError();
    183 #if LJ_TARGET_XBOXONE
    184   wchar_t wbuf[128];
    185   char buf[128*2];
    186   if (!FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM,
    187 		      NULL, err, 0, wbuf, sizeof(wbuf)/sizeof(wchar_t), NULL) ||
    188       !WideCharToMultiByte(CP_ACP, 0, wbuf, 128, buf, 128*2, NULL, NULL))
    189 #else
    190   char buf[128];
    191   if (!FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM,
    192 		      NULL, err, 0, buf, sizeof(buf), NULL))
    193 #endif
    194     buf[0] = '\0';
    195   lj_err_callermsg(L, lj_strfmt_pushf(L, fmt, name, buf));
    196 }
    197 
    198 static int clib_needext(const char *s)
    199 {
    200   while (*s) {
    201     if (*s == '/' || *s == '\\' || *s == '.') return 0;
    202     s++;
    203   }
    204   return 1;
    205 }
    206 
    207 static const char *clib_extname(lua_State *L, const char *name)
    208 {
    209   if (clib_needext(name)) {
    210     name = lj_strfmt_pushf(L, "%s.dll", name);
    211     L->top--;
    212   }
    213   return name;
    214 }
    215 
    216 static void *clib_loadlib(lua_State *L, const char *name, int global)
    217 {
    218   DWORD oldwerr = GetLastError();
    219   void *h = (void *)LoadLibraryExA(clib_extname(L, name), NULL, 0);
    220   if (!h) clib_error(L, "cannot load module " LUA_QS ": %s", name);
    221   SetLastError(oldwerr);
    222   UNUSED(global);
    223   return h;
    224 }
    225 
    226 static void clib_unloadlib(CLibrary *cl)
    227 {
    228   if (cl->handle == CLIB_DEFHANDLE) {
    229     MSize i;
    230     for (i = CLIB_HANDLE_KERNEL32; i < CLIB_HANDLE_MAX; i++) {
    231       void *h = clib_def_handle[i];
    232       if (h) {
    233 	clib_def_handle[i] = NULL;
    234 	FreeLibrary((HINSTANCE)h);
    235       }
    236     }
    237   } else if (cl->handle) {
    238     FreeLibrary((HINSTANCE)cl->handle);
    239   }
    240 }
    241 
    242 static void *clib_getsym(CLibrary *cl, const char *name)
    243 {
    244   void *p = NULL;
    245   if (cl->handle == CLIB_DEFHANDLE) {  /* Search default libraries. */
    246     MSize i;
    247     for (i = 0; i < CLIB_HANDLE_MAX; i++) {
    248       HINSTANCE h = (HINSTANCE)clib_def_handle[i];
    249       if (!(void *)h) {  /* Resolve default library handles (once). */
    250 	switch (i) {
    251 	case CLIB_HANDLE_EXE: GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, NULL, &h); break;
    252 	case CLIB_HANDLE_DLL:
    253 	  GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
    254 			     (const char *)clib_def_handle, &h);
    255 	  break;
    256 	case CLIB_HANDLE_CRT:
    257 	  GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
    258 			     (const char *)&_fmode, &h);
    259 	  break;
    260 	case CLIB_HANDLE_KERNEL32: h = LoadLibraryExA("kernel32.dll", NULL, 0); break;
    261 	case CLIB_HANDLE_USER32: h = LoadLibraryExA("user32.dll", NULL, 0); break;
    262 	case CLIB_HANDLE_GDI32: h = LoadLibraryExA("gdi32.dll", NULL, 0); break;
    263 	}
    264 	if (!h) continue;
    265 	clib_def_handle[i] = (void *)h;
    266       }
    267       p = (void *)GetProcAddress(h, name);
    268       if (p) break;
    269     }
    270   } else {
    271     p = (void *)GetProcAddress((HINSTANCE)cl->handle, name);
    272   }
    273   return p;
    274 }
    275 
    276 #else
    277 
    278 #define CLIB_DEFHANDLE	NULL
    279 
    280 LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt,
    281 					    const char *name)
    282 {
    283   lj_err_callermsg(L, lj_strfmt_pushf(L, fmt, name, "no support for this OS"));
    284 }
    285 
    286 static void *clib_loadlib(lua_State *L, const char *name, int global)
    287 {
    288   lj_err_callermsg(L, "no support for loading dynamic libraries for this OS");
    289   UNUSED(name); UNUSED(global);
    290   return NULL;
    291 }
    292 
    293 static void clib_unloadlib(CLibrary *cl)
    294 {
    295   UNUSED(cl);
    296 }
    297 
    298 static void *clib_getsym(CLibrary *cl, const char *name)
    299 {
    300   UNUSED(cl); UNUSED(name);
    301   return NULL;
    302 }
    303 
    304 #endif
    305 
    306 /* -- C library indexing -------------------------------------------------- */
    307 
    308 #if LJ_TARGET_X86 && LJ_ABI_WIN
    309 /* Compute argument size for fastcall/stdcall functions. */
    310 static CTSize clib_func_argsize(CTState *cts, CType *ct)
    311 {
    312   CTSize n = 0;
    313   while (ct->sib) {
    314     CType *d;
    315     ct = ctype_get(cts, ct->sib);
    316     if (ctype_isfield(ct->info)) {
    317       d = ctype_rawchild(cts, ct);
    318       n += ((d->size + 3) & ~3);
    319     }
    320   }
    321   return n;
    322 }
    323 #endif
    324 
    325 /* Get redirected or mangled external symbol. */
    326 static const char *clib_extsym(CTState *cts, CType *ct, GCstr *name)
    327 {
    328   if (ct->sib) {
    329     CType *ctf = ctype_get(cts, ct->sib);
    330     if (ctype_isxattrib(ctf->info, CTA_REDIR))
    331       return strdata(gco2str(gcref(ctf->name)));
    332   }
    333   return strdata(name);
    334 }
    335 
    336 /* Index a C library by name. */
    337 TValue *lj_clib_index(lua_State *L, CLibrary *cl, GCstr *name)
    338 {
    339   TValue *tv = lj_tab_setstr(L, cl->cache, name);
    340   if (LJ_UNLIKELY(tvisnil(tv))) {
    341     CTState *cts = ctype_cts(L);
    342     CType *ct;
    343     CTypeID id = lj_ctype_getname(cts, &ct, name, CLNS_INDEX);
    344     if (!id)
    345       lj_err_callerv(L, LJ_ERR_FFI_NODECL, strdata(name));
    346     if (ctype_isconstval(ct->info)) {
    347       CType *ctt = ctype_child(cts, ct);
    348       lua_assert(ctype_isinteger(ctt->info) && ctt->size <= 4);
    349       if ((ctt->info & CTF_UNSIGNED) && (int32_t)ct->size < 0)
    350 	setnumV(tv, (lua_Number)(uint32_t)ct->size);
    351       else
    352 	setintV(tv, (int32_t)ct->size);
    353     } else {
    354       const char *sym = clib_extsym(cts, ct, name);
    355 #if LJ_TARGET_WINDOWS
    356       DWORD oldwerr = GetLastError();
    357 #endif
    358       void *p = clib_getsym(cl, sym);
    359       GCcdata *cd;
    360       lua_assert(ctype_isfunc(ct->info) || ctype_isextern(ct->info));
    361 #if LJ_TARGET_X86 && LJ_ABI_WIN
    362       /* Retry with decorated name for fastcall/stdcall functions. */
    363       if (!p && ctype_isfunc(ct->info)) {
    364 	CTInfo cconv = ctype_cconv(ct->info);
    365 	if (cconv == CTCC_FASTCALL || cconv == CTCC_STDCALL) {
    366 	  CTSize sz = clib_func_argsize(cts, ct);
    367 	  const char *symd = lj_strfmt_pushf(L,
    368 			       cconv == CTCC_FASTCALL ? "@%s@%d" : "_%s@%d",
    369 			       sym, sz);
    370 	  L->top--;
    371 	  p = clib_getsym(cl, symd);
    372 	}
    373       }
    374 #endif
    375       if (!p)
    376 	clib_error(L, "cannot resolve symbol " LUA_QS ": %s", sym);
    377 #if LJ_TARGET_WINDOWS
    378       SetLastError(oldwerr);
    379 #endif
    380       cd = lj_cdata_new(cts, id, CTSIZE_PTR);
    381       *(void **)cdataptr(cd) = p;
    382       setcdataV(L, tv, cd);
    383     }
    384   }
    385   return tv;
    386 }
    387 
    388 /* -- C library management ------------------------------------------------ */
    389 
    390 /* Create a new CLibrary object and push it on the stack. */
    391 static CLibrary *clib_new(lua_State *L, GCtab *mt)
    392 {
    393   GCtab *t = lj_tab_new(L, 0, 0);
    394   GCudata *ud = lj_udata_new(L, sizeof(CLibrary), t);
    395   CLibrary *cl = (CLibrary *)uddata(ud);
    396   cl->cache = t;
    397   ud->udtype = UDTYPE_FFI_CLIB;
    398   /* NOBARRIER: The GCudata is new (marked white). */
    399   setgcref(ud->metatable, obj2gco(mt));
    400   setudataV(L, L->top++, ud);
    401   return cl;
    402 }
    403 
    404 /* Load a C library. */
    405 void lj_clib_load(lua_State *L, GCtab *mt, GCstr *name, int global)
    406 {
    407   void *handle = clib_loadlib(L, strdata(name), global);
    408   CLibrary *cl = clib_new(L, mt);
    409   cl->handle = handle;
    410 }
    411 
    412 /* Unload a C library. */
    413 void lj_clib_unload(CLibrary *cl)
    414 {
    415   clib_unloadlib(cl);
    416   cl->handle = NULL;
    417 }
    418 
    419 /* Create the default C library object. */
    420 void lj_clib_default(lua_State *L, GCtab *mt)
    421 {
    422   CLibrary *cl = clib_new(L, mt);
    423 #if __MSYS__
    424   cl->handle = dlopen("msys-2.0.dll", RTLD_LOCAL);
    425   cl->system = 1;
    426   lua_assert(cl->handle);
    427 #else
    428   cl->handle = CLIB_DEFHANDLE;
    429 #endif
    430 }
    431 
    432 #endif