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
35LJ_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
56static 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. */
81static 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. */
94static 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
115static 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
132static void clib_unloadlib(CLibrary *cl)
133{
134 if (cl->handle && cl->handle != CLIB_DEFHANDLE)
135 dlclose(cl->handle);
136}
137
138static 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
152BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*);
153#endif
154
155#define CLIB_DEFHANDLE ((void *)-1)
156
157/* Default libraries. */
158enum {
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
168static void *clib_def_handle[CLIB_HANDLE_MAX];
169
170LJ_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
181static 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
190static 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
199static 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
209static 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
225static 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
263LJ_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
269static 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
276static void clib_unloadlib(CLibrary *cl)
277{
278 UNUSED(cl);
279}
280
281static 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. */
293static 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. */
309static 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. */
320TValue *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. */
374static 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. */
388void 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. */
396void lj_clib_unload(CLibrary *cl)
397{
398 clib_unloadlib(cl);
399 cl->handle = NULL;
400}
401
402/* Create the default C library object. */
403void 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