/src/tarantool/third_party/luajit/src/lj_clib.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | ** FFI C library loader. |
3 | | ** Copyright (C) 2005-2017 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 | 6.70k | #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 | 0 | { |
38 | 0 | lj_err_callermsg(L, dlerror()); |
39 | 0 | } |
40 | | |
41 | 0 | #define clib_error(L, fmt, name) clib_error_(L) |
42 | | |
43 | | #if LJ_TARGET_CYGWIN |
44 | | #define CLIB_SOPREFIX "cyg" |
45 | | #else |
46 | 0 | #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 | 0 | #define CLIB_SOEXT "%s.so" |
55 | | #endif |
56 | | |
57 | | static const char *clib_extname(lua_State *L, const char *name) |
58 | 0 | { |
59 | 0 | if (!strchr(name, '/') |
60 | | #if LJ_TARGET_CYGWIN |
61 | | && !strchr(name, '\\') |
62 | | #endif |
63 | 0 | ) { |
64 | 0 | if (!strchr(name, '.')) { |
65 | 0 | name = lj_strfmt_pushf(L, CLIB_SOEXT, name); |
66 | 0 | L->top--; |
67 | | #if LJ_TARGET_CYGWIN |
68 | | } else { |
69 | | return name; |
70 | | #endif |
71 | 0 | } |
72 | 0 | if (!(name[0] == CLIB_SOPREFIX[0] && name[1] == CLIB_SOPREFIX[1] && |
73 | 0 | name[2] == CLIB_SOPREFIX[2])) { |
74 | 0 | name = lj_strfmt_pushf(L, CLIB_SOPREFIX "%s", name); |
75 | 0 | L->top--; |
76 | 0 | } |
77 | 0 | } |
78 | 0 | return name; |
79 | 0 | } |
80 | | |
81 | | /* Check for a recognized ld script line. */ |
82 | | static const char *clib_check_lds(lua_State *L, const char *buf) |
83 | 0 | { |
84 | 0 | char *p, *e; |
85 | 0 | if ((!strncmp(buf, "GROUP", 5) || !strncmp(buf, "INPUT", 5)) && |
86 | 0 | (p = strchr(buf, '('))) { |
87 | 0 | while (*++p == ' ') ; |
88 | 0 | for (e = p; *e && *e != ' ' && *e != ')'; e++) ; |
89 | 0 | return strdata(lj_str_new(L, p, e-p)); |
90 | 0 | } |
91 | 0 | return NULL; |
92 | 0 | } |
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 | 0 | { |
97 | 0 | FILE *fp = fopen(name, "r"); |
98 | 0 | const char *p = NULL; |
99 | 0 | if (fp) { |
100 | 0 | char buf[256]; |
101 | 0 | if (fgets(buf, sizeof(buf), fp)) { |
102 | 0 | if (!strncmp(buf, "/* GNU ld script", 16)) { /* ld script magic? */ |
103 | 0 | while (fgets(buf, sizeof(buf), fp)) { /* Check all lines. */ |
104 | 0 | p = clib_check_lds(L, buf); |
105 | 0 | if (p) break; |
106 | 0 | } |
107 | 0 | } else { /* Otherwise check only the first line. */ |
108 | 0 | p = clib_check_lds(L, buf); |
109 | 0 | } |
110 | 0 | } |
111 | 0 | fclose(fp); |
112 | 0 | } |
113 | 0 | return p; |
114 | 0 | } |
115 | | |
116 | | static void *clib_loadlib(lua_State *L, const char *name, int global) |
117 | 0 | { |
118 | 0 | void *h = dlopen(clib_extname(L, name), |
119 | 0 | RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL)); |
120 | 0 | if (!h) { |
121 | 0 | const char *e, *err = dlerror(); |
122 | 0 | if (err && *err == '/' && (e = strchr(err, ':')) && |
123 | 0 | (name = clib_resolve_lds(L, strdata(lj_str_new(L, err, e-err))))) { |
124 | 0 | h = dlopen(name, RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL)); |
125 | 0 | if (h) return h; |
126 | 0 | err = dlerror(); |
127 | 0 | } |
128 | 0 | if (!err) err = "dlopen failed"; |
129 | 0 | lj_err_callermsg(L, err); |
130 | 0 | } |
131 | 0 | return h; |
132 | 0 | } |
133 | | |
134 | | static void clib_unloadlib(CLibrary *cl) |
135 | 6.70k | { |
136 | 6.70k | if (cl->handle && cl->handle != CLIB_DEFHANDLE) |
137 | 0 | dlclose(cl->handle); |
138 | 6.70k | } |
139 | | |
140 | | static void *clib_getsym(CLibrary *cl, const char *name) |
141 | 0 | { |
142 | 0 | void *p = dlsym(cl->handle, name); |
143 | 0 | return p; |
144 | 0 | } |
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 | 0 | { |
333 | 0 | if (ct->sib) { |
334 | 0 | CType *ctf = ctype_get(cts, ct->sib); |
335 | 0 | if (ctype_isxattrib(ctf->info, CTA_REDIR)) |
336 | 0 | return strdata(gco2str(gcref(ctf->name))); |
337 | 0 | } |
338 | 0 | return strdata(name); |
339 | 0 | } |
340 | | |
341 | | /* Index a C library by name. */ |
342 | | TValue *lj_clib_index(lua_State *L, CLibrary *cl, GCstr *name) |
343 | 0 | { |
344 | 0 | TValue *tv = lj_tab_setstr(L, cl->cache, name); |
345 | 0 | if (LJ_UNLIKELY(tvisnil(tv))) { |
346 | 0 | CTState *cts = ctype_cts(L); |
347 | 0 | CType *ct; |
348 | 0 | CTypeID id = lj_ctype_getname(cts, &ct, name, CLNS_INDEX); |
349 | 0 | if (!id) |
350 | 0 | lj_err_callerv(L, LJ_ERR_FFI_NODECL, strdata(name)); |
351 | 0 | if (ctype_isconstval(ct->info)) { |
352 | 0 | CType *ctt = ctype_child(cts, ct); |
353 | 0 | lj_assertCTS(ctype_isinteger(ctt->info) && ctt->size <= 4, |
354 | 0 | "only 32 bit const supported"); /* NYI */ |
355 | 0 | if ((ctt->info & CTF_UNSIGNED) && (int32_t)ct->size < 0) |
356 | 0 | setnumV(tv, (lua_Number)(uint32_t)ct->size); |
357 | 0 | else |
358 | 0 | setintV(tv, (int32_t)ct->size); |
359 | 0 | } else { |
360 | 0 | const char *sym = clib_extsym(cts, ct, name); |
361 | | #if LJ_TARGET_WINDOWS |
362 | | DWORD oldwerr = GetLastError(); |
363 | | #endif |
364 | 0 | void *p = clib_getsym(cl, sym); |
365 | 0 | GCcdata *cd; |
366 | 0 | lj_assertCTS(ctype_isfunc(ct->info) || ctype_isextern(ct->info), |
367 | 0 | "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 | 0 | if (!p) |
383 | 0 | clib_error(L, "cannot resolve symbol " LUA_QS ": %s", sym); |
384 | | #if LJ_TARGET_WINDOWS |
385 | | SetLastError(oldwerr); |
386 | | #endif |
387 | 0 | cd = lj_cdata_new(cts, id, CTSIZE_PTR); |
388 | 0 | *(void **)cdataptr(cd) = p; |
389 | 0 | setcdataV(L, tv, cd); |
390 | 0 | lj_gc_anybarriert(L, cl->cache); |
391 | 0 | } |
392 | 0 | } |
393 | 0 | return tv; |
394 | 0 | } |
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 | 6.70k | { |
401 | 6.70k | GCtab *t = lj_tab_new(L, 0, 0); |
402 | 6.70k | GCudata *ud = lj_udata_new(L, sizeof(CLibrary), t); |
403 | 6.70k | CLibrary *cl = (CLibrary *)uddata(ud); |
404 | 6.70k | cl->cache = t; |
405 | 6.70k | ud->udtype = UDTYPE_FFI_CLIB; |
406 | | /* NOBARRIER: The GCudata is new (marked white). */ |
407 | 6.70k | setgcref(ud->metatable, obj2gco(mt)); |
408 | 6.70k | setudataV(L, L->top++, ud); |
409 | 6.70k | return cl; |
410 | 6.70k | } |
411 | | |
412 | | /* Load a C library. */ |
413 | | void lj_clib_load(lua_State *L, GCtab *mt, GCstr *name, int global) |
414 | 0 | { |
415 | 0 | void *handle = clib_loadlib(L, strdata(name), global); |
416 | 0 | CLibrary *cl = clib_new(L, mt); |
417 | 0 | cl->handle = handle; |
418 | 0 | } |
419 | | |
420 | | /* Unload a C library. */ |
421 | | void lj_clib_unload(CLibrary *cl) |
422 | 6.70k | { |
423 | 6.70k | clib_unloadlib(cl); |
424 | 6.70k | cl->handle = NULL; |
425 | 6.70k | } |
426 | | |
427 | | /* Create the default C library object. */ |
428 | | void lj_clib_default(lua_State *L, GCtab *mt) |
429 | 6.70k | { |
430 | 6.70k | CLibrary *cl = clib_new(L, mt); |
431 | 6.70k | cl->handle = CLIB_DEFHANDLE; |
432 | 6.70k | } |
433 | | |
434 | | #endif |