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