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 | |
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 | |
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. */ |
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. */ |
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 | |
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 | |
134 | static void clib_unloadlib(CLibrary *cl) |
135 | { |
136 | if (cl->handle && cl->handle != CLIB_DEFHANDLE) |
137 | dlclose(cl->handle); |
138 | } |
139 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
298 | static void clib_unloadlib(CLibrary *cl) |
299 | { |
300 | UNUSED(cl); |
301 | } |
302 | |
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. */ |
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. */ |
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. */ |
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. */ |
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. */ |
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. */ |
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. */ |
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 | |