2 ** FFI C library loader.
3 ** Copyright (C) 2005-2011 Mike Pall. See Copyright Notice in luajit.h
20 /* -- OS-specific functions ----------------------------------------------- */
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)
32 #define CLIB_DEFHANDLE NULL
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)
43 #define CLIB_SOEXT "%s.dylib"
45 #define CLIB_SOEXT "%s.so"
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
);
55 if (!(name
[0] == 'l' && name
[1] == 'i' && name
[2] == 'b')) {
56 name
= lj_str_pushf(L
, "lib%s", 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");
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
, '('))) {
73 for (e
= p
; *e
&& *e
!= ' ' && *e
!= ')'; e
++) ;
75 return strdata(lj_str_new(L
, p
, e
-p
));
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
));
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
));
96 lj_err_callermsg(L
, err
);
101 static void clib_unloadlib(CLibrary
*cl
)
103 if (!cl
->handle
&& cl
->handle
!= CLIB_DEFHANDLE
)
107 static void *clib_getsym(CLibrary
*cl
, const char *name
)
109 void *p
= dlsym(cl
->handle
, name
);
113 #elif LJ_TARGET_WINDOWS
115 #define WIN32_LEAN_AND_MEAN
117 #define WINVER 0x0500
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
*);
126 #define CLIB_DEFHANDLE ((void *)-1)
128 /* Default libraries. */
133 CLIB_HANDLE_KERNEL32
,
139 static void *clib_def_handle
[CLIB_HANDLE_MAX
];
141 LJ_NORET LJ_NOINLINE
static void clib_error(lua_State
*L
, const char *fmt
,
144 DWORD err
= GetLastError();
146 if (!FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS
|FORMAT_MESSAGE_FROM_SYSTEM
,
147 NULL
, err
, 0, buf
, sizeof(buf
), NULL
))
149 lj_err_callermsg(L
, lj_str_pushf(L
, fmt
, name
, buf
));
152 static int clib_needext(const char *s
)
155 if (*s
== '/' || *s
== '\\' || *s
== '.') return 0;
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
);
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
);
180 static void clib_unloadlib(CLibrary
*cl
)
182 if (cl
->handle
== CLIB_DEFHANDLE
) {
184 for (i
= 0; i
< CLIB_HANDLE_MAX
; i
++) {
185 void *h
= clib_def_handle
[i
];
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
)
199 DWORD oldwerr
= GetLastError();
200 if (cl
->handle
== CLIB_DEFHANDLE
) { /* Search default libraries. */
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). */
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
);
211 case CLIB_HANDLE_CRT
:
212 GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
,
213 (const char *)&_fmode
, &h
);
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;
220 clib_def_handle
[i
] = (void *)h
;
222 p
= (void *)GetProcAddress(h
, name
);
226 p
= (void *)GetProcAddress((HINSTANCE
)cl
->handle
, name
);
228 SetLastError(oldwerr
);
234 #define CLIB_DEFHANDLE NULL
236 LJ_NORET LJ_NOINLINE
static void clib_error(lua_State
*L
, const char *fmt
,
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
);
249 static void clib_unloadlib(CLibrary
*cl
)
254 static void *clib_getsym(CLibrary
*cl
, const char *name
)
256 UNUSED(cl
); UNUSED(name
);
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
)
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);
280 /* Get redirected or mangled external symbol. */
281 static const char *clib_extsym(CTState
*cts
, CType
*ct
, GCstr
*name
)
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
);
298 CTypeID id
= lj_ctype_getname(cts
, &ct
, name
, CLNS_INDEX
);
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
);
307 setintV(tv
, (int32_t)ct
->size
);
309 const char *sym
= clib_extsym(cts
, ct
, name
);
310 void *p
= clib_getsym(cl
, sym
);
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",
322 p
= clib_getsym(cl
, sym
);
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
);
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
);
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
);
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
);
360 /* Unload a C library. */
361 void lj_clib_unload(CLibrary
*cl
)
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
;