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