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
36LJ_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
57static 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. */
82static 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. */
95static 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
116static 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
134static void clib_unloadlib(CLibrary *cl)
135{
136 if (cl->handle && cl->handle != CLIB_DEFHANDLE)
137 dlclose(cl->handle);
138}
139
140static 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
154BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*);
155#endif
156
157#define CLIB_DEFHANDLE ((void *)-1)
158
159/* Default libraries. */
160enum {
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
172static void *clib_def_handle[CLIB_HANDLE_MAX];
173
174LJ_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
193static 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
202static 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
211static 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
221static 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
240EXTERN_C IMAGE_DOS_HEADER __ImageBase;
241#endif
242
243static 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
285LJ_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
291static 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
298static void clib_unloadlib(CLibrary *cl)
299{
300 UNUSED(cl);
301}
302
303static 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. */
315static 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. */
331static 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. */
342TValue *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. */
399static 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. */
413void 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. */
421void lj_clib_unload(CLibrary *cl)
422{
423 clib_unloadlib(cl);
424 cl->handle = NULL;
425}
426
427/* Create the default C library object. */
428void 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