FFI: Save GetLastError() around ffi.load() and symbol resolving, too.
[luajit-2.0.git] / src / lj_clib.c
blob9f69b9697b8b75e923e2f2293683f84fe777c062
1 /*
2 ** FFI C library loader.
3 ** Copyright (C) 2005-2011 Mike Pall. See Copyright Notice in luajit.h
4 */
6 #include "lj_obj.h"
8 #if LJ_HASFFI
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"
20 /* -- OS-specific functions ----------------------------------------------- */
22 #if LJ_TARGET_DLOPEN
24 #include <dlfcn.h>
25 #include <stdio.h>
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
35 LJ_NORET LJ_NOINLINE static void clib_error_(lua_State *L)
37 lj_err_callermsg(L, dlerror());
40 #define clib_error(L, fmt, name) clib_error_(L)
42 #if LJ_TARGET_OSX
43 #define CLIB_SOEXT "%s.dylib"
44 #else
45 #define CLIB_SOEXT "%s.so"
46 #endif
48 static const char *clib_extname(lua_State *L, const char *name)
50 if (!strchr(name, '/')) {
51 if (!strchr(name, '.')) {
52 name = lj_str_pushf(L, CLIB_SOEXT, name);
53 L->top--;
55 if (!(name[0] == 'l' && name[1] == 'i' && name[2] == 'b')) {
56 name = lj_str_pushf(L, "lib%s", name);
57 L->top--;
60 return name;
63 /* Quick and dirty solution to resolve shared library name from ld script. */
64 static const char *clib_resolve_lds(lua_State *L, const char *name)
66 FILE *fp = fopen(name, "r");
67 if (fp) {
68 char *p, *e, buf[256];
69 if (fgets(buf, sizeof(buf), fp) && !strncmp(buf, "/* GNU ld script", 16)) {
70 while (fgets(buf, sizeof(buf), fp)) {
71 if (!strncmp(buf, "GROUP", 5) && (p = strchr(buf, '('))) {
72 while (*++p == ' ') ;
73 for (e = p; *e && *e != ' ' && *e != ')'; e++) ;
74 fclose(fp);
75 return strdata(lj_str_new(L, p, e-p));
79 fclose(fp);
81 return NULL;
84 static void *clib_loadlib(lua_State *L, const char *name, int global)
86 void *h = dlopen(clib_extname(L, name),
87 RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL));
88 if (!h) {
89 const char *e, *err = dlerror();
90 if (*err == '/' && (e = strchr(err, ':')) &&
91 (name = clib_resolve_lds(L, strdata(lj_str_new(L, err, e-err))))) {
92 h = dlopen(name, RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL));
93 if (h) return h;
94 err = dlerror();
96 lj_err_callermsg(L, err);
98 return h;
101 static void clib_unloadlib(CLibrary *cl)
103 if (!cl->handle && cl->handle != CLIB_DEFHANDLE)
104 dlclose(cl->handle);
107 static void *clib_getsym(CLibrary *cl, const char *name)
109 void *p = dlsym(cl->handle, name);
110 return p;
113 #elif LJ_TARGET_WINDOWS
115 #define WIN32_LEAN_AND_MEAN
116 #ifndef WINVER
117 #define WINVER 0x0500
118 #endif
119 #include <windows.h>
121 #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
122 #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4
123 BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*);
124 #endif
126 #define CLIB_DEFHANDLE ((void *)-1)
128 /* Default libraries. */
129 enum {
130 CLIB_HANDLE_EXE,
131 CLIB_HANDLE_DLL,
132 CLIB_HANDLE_CRT,
133 CLIB_HANDLE_KERNEL32,
134 CLIB_HANDLE_USER32,
135 CLIB_HANDLE_GDI32,
136 CLIB_HANDLE_MAX
139 static void *clib_def_handle[CLIB_HANDLE_MAX];
141 LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt,
142 const char *name)
144 DWORD err = GetLastError();
145 char buf[128];
146 if (!FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM,
147 NULL, err, 0, buf, sizeof(buf), NULL))
148 buf[0] = '\0';
149 lj_err_callermsg(L, lj_str_pushf(L, fmt, name, buf));
152 static int clib_needext(const char *s)
154 while (*s) {
155 if (*s == '/' || *s == '\\' || *s == '.') return 0;
156 s++;
158 return 1;
161 static const char *clib_extname(lua_State *L, const char *name)
163 if (clib_needext(name)) {
164 name = lj_str_pushf(L, "%s.dll", name);
165 L->top--;
167 return name;
170 static void *clib_loadlib(lua_State *L, const char *name, int global)
172 DWORD oldwerr = GetLastError();
173 void *h = (void *)LoadLibraryA(clib_extname(L, name));
174 SetLastError(oldwerr);
175 if (!h) clib_error(L, "cannot load module " LUA_QS ": %s", name);
176 UNUSED(global);
177 return h;
180 static void clib_unloadlib(CLibrary *cl)
182 if (cl->handle == CLIB_DEFHANDLE) {
183 MSize i;
184 for (i = 0; i < CLIB_HANDLE_MAX; i++) {
185 void *h = clib_def_handle[i];
186 if (h) {
187 clib_def_handle[i] = NULL;
188 FreeLibrary((HINSTANCE)h);
191 } else if (!cl->handle) {
192 FreeLibrary((HINSTANCE)cl->handle);
196 static void *clib_getsym(CLibrary *cl, const char *name)
198 void *p = NULL;
199 DWORD oldwerr = GetLastError();
200 if (cl->handle == CLIB_DEFHANDLE) { /* Search default libraries. */
201 MSize i;
202 for (i = 0; i < CLIB_HANDLE_MAX; i++) {
203 HINSTANCE h = (HINSTANCE)clib_def_handle[i];
204 if (!(void *)h) { /* Resolve default library handles (once). */
205 switch (i) {
206 case CLIB_HANDLE_EXE: GetModuleHandleExA(0, NULL, &h); break;
207 case CLIB_HANDLE_DLL:
208 GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
209 (const char *)clib_def_handle, &h);
210 break;
211 case CLIB_HANDLE_CRT:
212 GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
213 (const char *)&_fmode, &h);
214 break;
215 case CLIB_HANDLE_KERNEL32: h = LoadLibraryA("kernel32.dll"); break;
216 case CLIB_HANDLE_USER32: h = LoadLibraryA("user32.dll"); break;
217 case CLIB_HANDLE_GDI32: h = LoadLibraryA("gdi32.dll"); break;
219 if (!h) continue;
220 clib_def_handle[i] = (void *)h;
222 p = (void *)GetProcAddress(h, name);
223 if (p) break;
225 } else {
226 p = (void *)GetProcAddress((HINSTANCE)cl->handle, name);
228 SetLastError(oldwerr);
229 return p;
232 #else
234 #define CLIB_DEFHANDLE NULL
236 LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt,
237 const char *name)
239 lj_err_callermsg(L, lj_str_pushf(L, fmt, name, "no support for this OS"));
242 static void *clib_loadlib(lua_State *L, const char *name, int global)
244 lj_err_callermsg(L, "no support for loading dynamic libraries for this OS");
245 UNUSED(name); UNUSED(global);
246 return NULL;
249 static void clib_unloadlib(CLibrary *cl)
251 UNUSED(cl);
254 static void *clib_getsym(CLibrary *cl, const char *name)
256 UNUSED(cl); UNUSED(name);
257 return NULL;
260 #endif
262 /* -- C library indexing -------------------------------------------------- */
264 #if LJ_TARGET_X86 && LJ_ABI_WIN
265 /* Compute argument size for fastcall/stdcall functions. */
266 static CTSize clib_func_argsize(CTState *cts, CType *ct)
268 CTSize n = 0;
269 while (ct->sib) {
270 CType *d;
271 ct = ctype_get(cts, ct->sib);
272 lua_assert(ctype_isfield(ct->info));
273 d = ctype_rawchild(cts, ct);
274 n += ((d->size + 3) & ~3);
276 return n;
278 #endif
280 /* Get redirected or mangled external symbol. */
281 static const char *clib_extsym(CTState *cts, CType *ct, GCstr *name)
283 if (ct->sib) {
284 CType *ctf = ctype_get(cts, ct->sib);
285 if (ctype_isxattrib(ctf->info, CTA_REDIR))
286 return strdata(gco2str(gcref(ctf->name)));
288 return strdata(name);
291 /* Index a C library by name. */
292 TValue *lj_clib_index(lua_State *L, CLibrary *cl, GCstr *name)
294 TValue *tv = lj_tab_setstr(L, cl->cache, name);
295 if (LJ_UNLIKELY(tvisnil(tv))) {
296 CTState *cts = ctype_cts(L);
297 CType *ct;
298 CTypeID id = lj_ctype_getname(cts, &ct, name, CLNS_INDEX);
299 if (!id)
300 lj_err_callerv(L, LJ_ERR_FFI_NODECL, strdata(name));
301 if (ctype_isconstval(ct->info)) {
302 CType *ctt = ctype_child(cts, ct);
303 lua_assert(ctype_isinteger(ctt->info) && ctt->size <= 4);
304 if ((ctt->info & CTF_UNSIGNED) && (int32_t)ct->size < 0)
305 setnumV(tv, (lua_Number)(uint32_t)ct->size);
306 else
307 setintV(tv, (int32_t)ct->size);
308 } else {
309 const char *sym = clib_extsym(cts, ct, name);
310 void *p = clib_getsym(cl, sym);
311 GCcdata *cd;
312 lua_assert(ctype_isfunc(ct->info) || ctype_isextern(ct->info));
313 #if LJ_TARGET_X86 && LJ_ABI_WIN
314 /* Retry with decorated name for fastcall/stdcall functions. */
315 if (!p && ctype_isfunc(ct->info)) {
316 CTInfo cconv = ctype_cconv(ct->info);
317 if (cconv == CTCC_FASTCALL || cconv == CTCC_STDCALL) {
318 CTSize sz = clib_func_argsize(cts, ct);
319 sym = lj_str_pushf(L, cconv == CTCC_FASTCALL ? "@%s@%d" : "_%s@%d",
320 sym, sz);
321 L->top--;
322 p = clib_getsym(cl, sym);
325 #endif
326 if (!p)
327 clib_error(L, "cannot resolve symbol " LUA_QS ": %s", strdata(name));
328 cd = lj_cdata_new(cts, id, CTSIZE_PTR);
329 *(void **)cdataptr(cd) = p;
330 setcdataV(L, tv, cd);
333 return tv;
336 /* -- C library management ------------------------------------------------ */
338 /* Create a new CLibrary object and push it on the stack. */
339 static CLibrary *clib_new(lua_State *L, GCtab *mt)
341 GCtab *t = lj_tab_new(L, 0, 0);
342 GCudata *ud = lj_udata_new(L, sizeof(CLibrary), t);
343 CLibrary *cl = (CLibrary *)uddata(ud);
344 cl->cache = t;
345 ud->udtype = UDTYPE_FFI_CLIB;
346 /* NOBARRIER: The GCudata is new (marked white). */
347 setgcref(ud->metatable, obj2gco(mt));
348 setudataV(L, L->top++, ud);
349 return cl;
352 /* Load a C library. */
353 void lj_clib_load(lua_State *L, GCtab *mt, GCstr *name, int global)
355 void *handle = clib_loadlib(L, strdata(name), global);
356 CLibrary *cl = clib_new(L, mt);
357 cl->handle = handle;
360 /* Unload a C library. */
361 void lj_clib_unload(CLibrary *cl)
363 clib_unloadlib(cl);
364 cl->handle = NULL;
367 /* Create the default C library object. */
368 void lj_clib_default(lua_State *L, GCtab *mt)
370 CLibrary *cl = clib_new(L, mt);
371 cl->handle = CLIB_DEFHANDLE;
374 #endif