2 ** FFI C library loader.
3 ** Copyright (C) 2005-2015 Mike Pall. See Copyright Notice in luajit.h
19 #include "lj_strfmt.h"
21 /* -- OS-specific functions ----------------------------------------------- */
28 #if defined(RTLD_DEFAULT)
29 #define CLIB_DEFHANDLE RTLD_DEFAULT
30 #elif LJ_TARGET_OSX || LJ_TARGET_BSD
31 #define CLIB_DEFHANDLE ((void *)(intptr_t)-2)
33 #define CLIB_DEFHANDLE NULL
36 LJ_NORET LJ_NOINLINE
static void clib_error_(lua_State
*L
)
38 lj_err_callermsg(L
, dlerror());
41 #define clib_error(L, fmt, name) clib_error_(L)
43 #if defined(__CYGWIN__)
44 #define CLIB_SOPREFIX "cyg"
46 #define CLIB_SOPREFIX "lib"
50 #define CLIB_SOEXT "%s.dylib"
51 #elif defined(__CYGWIN__)
52 #define CLIB_SOEXT "%s.dll"
54 #define CLIB_SOEXT "%s.so"
57 static const char *clib_extname(lua_State
*L
, const char *name
)
59 if (!strchr(name
, '/')
61 && !strchr(name
, '\\')
64 if (!strchr(name
, '.')) {
65 name
= lj_strfmt_pushf(L
, CLIB_SOEXT
, name
);
72 if (!(name
[0] == CLIB_SOPREFIX
[0] && name
[1] == CLIB_SOPREFIX
[1] &&
73 name
[2] == CLIB_SOPREFIX
[2])) {
74 name
= lj_strfmt_pushf(L
, CLIB_SOPREFIX
"%s", name
);
81 /* Check for a recognized ld script line. */
82 static const char *clib_check_lds(lua_State
*L
, const char *buf
)
85 if ((!strncmp(buf
, "GROUP", 5) || !strncmp(buf
, "INPUT", 5)) &&
86 (p
= strchr(buf
, '('))) {
88 for (e
= p
; *e
&& *e
!= ' ' && *e
!= ')'; e
++) ;
89 return strdata(lj_str_new(L
, p
, e
-p
));
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
)
97 FILE *fp
= fopen(name
, "r");
101 if (fgets(buf
, sizeof(buf
), fp
)) {
102 if (!strncmp(buf
, "/* GNU ld script", 16)) { /* ld script magic? */
103 while (fgets(buf
, sizeof(buf
), fp
)) { /* Check all lines. */
104 p
= clib_check_lds(L
, buf
);
107 } else { /* Otherwise check only the first line. */
108 p
= clib_check_lds(L
, buf
);
116 static void *clib_loadlib(lua_State
*L
, const char *name
, int global
)
118 void *h
= dlopen(clib_extname(L
, name
),
119 RTLD_LAZY
| (global
?RTLD_GLOBAL
:RTLD_LOCAL
));
121 const char *e
, *err
= dlerror();
122 if (*err
== '/' && (e
= strchr(err
, ':')) &&
123 (name
= clib_resolve_lds(L
, strdata(lj_str_new(L
, err
, e
-err
))))) {
124 h
= dlopen(name
, RTLD_LAZY
| (global
?RTLD_GLOBAL
:RTLD_LOCAL
));
128 lj_err_callermsg(L
, err
);
133 static void clib_unloadlib(CLibrary
*cl
)
135 if (cl
->handle
&& cl
->handle
!= CLIB_DEFHANDLE
)
139 static void *clib_getsym(CLibrary
*cl
, const char *name
)
141 void *p
= dlsym(cl
->handle
, name
);
145 #elif LJ_TARGET_WINDOWS
147 #define WIN32_LEAN_AND_MEAN
150 #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
151 #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4
152 #define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2
153 BOOL WINAPI
GetModuleHandleExA(DWORD
, LPCSTR
, HMODULE
*);
156 #define CLIB_DEFHANDLE ((void *)-1)
158 /* Default libraries. */
163 CLIB_HANDLE_KERNEL32
,
169 static void *clib_def_handle
[CLIB_HANDLE_MAX
];
171 LJ_NORET LJ_NOINLINE
static void clib_error(lua_State
*L
, const char *fmt
,
174 DWORD err
= GetLastError();
175 #if LJ_TARGET_XBOXONE
178 if (!FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS
|FORMAT_MESSAGE_FROM_SYSTEM
,
179 NULL
, err
, 0, wbuf
, sizeof(wbuf
)/sizeof(wchar_t), NULL
) ||
180 !WideCharToMultiByte(CP_ACP
, 0, wbuf
, 128, buf
, 128*2, NULL
, NULL
))
183 if (!FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS
|FORMAT_MESSAGE_FROM_SYSTEM
,
184 NULL
, err
, 0, buf
, sizeof(buf
), NULL
))
187 lj_err_callermsg(L
, lj_strfmt_pushf(L
, fmt
, name
, buf
));
190 static int clib_needext(const char *s
)
193 if (*s
== '/' || *s
== '\\' || *s
== '.') return 0;
199 static const char *clib_extname(lua_State
*L
, const char *name
)
201 if (clib_needext(name
)) {
202 name
= lj_strfmt_pushf(L
, "%s.dll", name
);
208 static void *clib_loadlib(lua_State
*L
, const char *name
, int global
)
210 DWORD oldwerr
= GetLastError();
211 void *h
= (void *)LoadLibraryExA(clib_extname(L
, name
), NULL
, 0);
212 if (!h
) clib_error(L
, "cannot load module " LUA_QS
": %s", name
);
213 SetLastError(oldwerr
);
218 static void clib_unloadlib(CLibrary
*cl
)
220 if (cl
->handle
== CLIB_DEFHANDLE
) {
222 for (i
= CLIB_HANDLE_KERNEL32
; i
< CLIB_HANDLE_MAX
; i
++) {
223 void *h
= clib_def_handle
[i
];
225 clib_def_handle
[i
] = NULL
;
226 FreeLibrary((HINSTANCE
)h
);
229 } else if (cl
->handle
) {
230 FreeLibrary((HINSTANCE
)cl
->handle
);
234 static void *clib_getsym(CLibrary
*cl
, const char *name
)
237 if (cl
->handle
== CLIB_DEFHANDLE
) { /* Search default libraries. */
239 for (i
= 0; i
< CLIB_HANDLE_MAX
; i
++) {
240 HINSTANCE h
= (HINSTANCE
)clib_def_handle
[i
];
241 if (!(void *)h
) { /* Resolve default library handles (once). */
243 case CLIB_HANDLE_EXE
: GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
, NULL
, &h
); break;
244 case CLIB_HANDLE_DLL
:
245 GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
,
246 (const char *)clib_def_handle
, &h
);
248 case CLIB_HANDLE_CRT
:
249 GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
,
250 (const char *)&_fmode
, &h
);
252 case CLIB_HANDLE_KERNEL32
: h
= LoadLibraryExA("kernel32.dll", NULL
, 0); break;
253 case CLIB_HANDLE_USER32
: h
= LoadLibraryExA("user32.dll", NULL
, 0); break;
254 case CLIB_HANDLE_GDI32
: h
= LoadLibraryExA("gdi32.dll", NULL
, 0); break;
257 clib_def_handle
[i
] = (void *)h
;
259 p
= (void *)GetProcAddress(h
, name
);
263 p
= (void *)GetProcAddress((HINSTANCE
)cl
->handle
, name
);
270 #define CLIB_DEFHANDLE NULL
272 LJ_NORET LJ_NOINLINE
static void clib_error(lua_State
*L
, const char *fmt
,
275 lj_err_callermsg(L
, lj_strfmt_pushf(L
, fmt
, name
, "no support for this OS"));
278 static void *clib_loadlib(lua_State
*L
, const char *name
, int global
)
280 lj_err_callermsg(L
, "no support for loading dynamic libraries for this OS");
281 UNUSED(name
); UNUSED(global
);
285 static void clib_unloadlib(CLibrary
*cl
)
290 static void *clib_getsym(CLibrary
*cl
, const char *name
)
292 UNUSED(cl
); UNUSED(name
);
298 /* -- C library indexing -------------------------------------------------- */
300 #if LJ_TARGET_X86 && LJ_ABI_WIN
301 /* Compute argument size for fastcall/stdcall functions. */
302 static CTSize
clib_func_argsize(CTState
*cts
, CType
*ct
)
307 ct
= ctype_get(cts
, ct
->sib
);
308 if (ctype_isfield(ct
->info
)) {
309 d
= ctype_rawchild(cts
, ct
);
310 n
+= ((d
->size
+ 3) & ~3);
317 /* Get redirected or mangled external symbol. */
318 static const char *clib_extsym(CTState
*cts
, CType
*ct
, GCstr
*name
)
321 CType
*ctf
= ctype_get(cts
, ct
->sib
);
322 if (ctype_isxattrib(ctf
->info
, CTA_REDIR
))
323 return strdata(gco2str(gcref(ctf
->name
)));
325 return strdata(name
);
328 /* Index a C library by name. */
329 TValue
*lj_clib_index(lua_State
*L
, CLibrary
*cl
, GCstr
*name
)
331 TValue
*tv
= lj_tab_setstr(L
, cl
->cache
, name
);
332 if (LJ_UNLIKELY(tvisnil(tv
))) {
333 CTState
*cts
= ctype_cts(L
);
335 CTypeID id
= lj_ctype_getname(cts
, &ct
, name
, CLNS_INDEX
);
337 lj_err_callerv(L
, LJ_ERR_FFI_NODECL
, strdata(name
));
338 if (ctype_isconstval(ct
->info
)) {
339 CType
*ctt
= ctype_child(cts
, ct
);
340 lua_assert(ctype_isinteger(ctt
->info
) && ctt
->size
<= 4);
341 if ((ctt
->info
& CTF_UNSIGNED
) && (int32_t)ct
->size
< 0)
342 setnumV(tv
, (lua_Number
)(uint32_t)ct
->size
);
344 setintV(tv
, (int32_t)ct
->size
);
346 const char *sym
= clib_extsym(cts
, ct
, name
);
347 #if LJ_TARGET_WINDOWS
348 DWORD oldwerr
= GetLastError();
350 void *p
= clib_getsym(cl
, sym
);
352 lua_assert(ctype_isfunc(ct
->info
) || ctype_isextern(ct
->info
));
353 #if LJ_TARGET_X86 && LJ_ABI_WIN
354 /* Retry with decorated name for fastcall/stdcall functions. */
355 if (!p
&& ctype_isfunc(ct
->info
)) {
356 CTInfo cconv
= ctype_cconv(ct
->info
);
357 if (cconv
== CTCC_FASTCALL
|| cconv
== CTCC_STDCALL
) {
358 CTSize sz
= clib_func_argsize(cts
, ct
);
359 const char *symd
= lj_strfmt_pushf(L
,
360 cconv
== CTCC_FASTCALL
? "@%s@%d" : "_%s@%d",
363 p
= clib_getsym(cl
, symd
);
368 clib_error(L
, "cannot resolve symbol " LUA_QS
": %s", sym
);
369 #if LJ_TARGET_WINDOWS
370 SetLastError(oldwerr
);
372 cd
= lj_cdata_new(cts
, id
, CTSIZE_PTR
);
373 *(void **)cdataptr(cd
) = p
;
374 setcdataV(L
, tv
, cd
);
380 /* -- C library management ------------------------------------------------ */
382 /* Create a new CLibrary object and push it on the stack. */
383 static CLibrary
*clib_new(lua_State
*L
, GCtab
*mt
)
385 GCtab
*t
= lj_tab_new(L
, 0, 0);
386 GCudata
*ud
= lj_udata_new(L
, sizeof(CLibrary
), t
);
387 CLibrary
*cl
= (CLibrary
*)uddata(ud
);
389 ud
->udtype
= UDTYPE_FFI_CLIB
;
390 /* NOBARRIER: The GCudata is new (marked white). */
391 setgcref(ud
->metatable
, obj2gco(mt
));
392 setudataV(L
, L
->top
++, ud
);
396 /* Load a C library. */
397 void lj_clib_load(lua_State
*L
, GCtab
*mt
, GCstr
*name
, int global
)
399 void *handle
= clib_loadlib(L
, strdata(name
), global
);
400 CLibrary
*cl
= clib_new(L
, mt
);
404 /* Unload a C library. */
405 void lj_clib_unload(CLibrary
*cl
)
411 /* Create the default C library object. */
412 void lj_clib_default(lua_State
*L
, GCtab
*mt
)
414 CLibrary
*cl
= clib_new(L
, mt
);
415 cl
->handle
= CLIB_DEFHANDLE
;