2 ** FFI C library loader.
3 ** Copyright (C) 2005-2023 Mike Pall. See Copyright Notice in luajit.h
19 #include "lj_strfmt.h"
21 /* -- OS-specific functions ----------------------------------------------- */
28 #if defined(RTLD_DEFAULT) && !defined(NO_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)
44 #define CLIB_SOPREFIX "cyg"
46 #define CLIB_SOPREFIX "lib"
50 #define CLIB_SOEXT "%s.dylib"
51 #elif LJ_TARGET_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
&& *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 if (!err
) err
= "dlopen failed";
129 lj_err_callermsg(L
, err
);
134 static void clib_unloadlib(CLibrary
*cl
)
136 if (cl
->handle
&& cl
->handle
!= CLIB_DEFHANDLE
)
140 static void *clib_getsym(CLibrary
*cl
, const char *name
)
142 void *p
= dlsym(cl
->handle
, name
);
146 #elif LJ_TARGET_WINDOWS
148 #define WIN32_LEAN_AND_MEAN
151 #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
152 #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4
153 #define GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT 2
154 BOOL WINAPI
GetModuleHandleExA(DWORD
, LPCSTR
, HMODULE
*);
157 #define CLIB_DEFHANDLE ((void *)-1)
159 /* Default libraries. */
165 CLIB_HANDLE_KERNEL32
,
172 static void *clib_def_handle
[CLIB_HANDLE_MAX
];
174 LJ_NORET LJ_NOINLINE
static void clib_error(lua_State
*L
, const char *fmt
,
177 DWORD err
= GetLastError();
178 #if LJ_TARGET_XBOXONE
181 if (!FormatMessageW(FORMAT_MESSAGE_IGNORE_INSERTS
|FORMAT_MESSAGE_FROM_SYSTEM
,
182 NULL
, err
, 0, wbuf
, sizeof(wbuf
)/sizeof(wchar_t), NULL
) ||
183 !WideCharToMultiByte(CP_ACP
, 0, wbuf
, 128, buf
, 128*2, NULL
, NULL
))
186 if (!FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS
|FORMAT_MESSAGE_FROM_SYSTEM
,
187 NULL
, err
, 0, buf
, sizeof(buf
), NULL
))
190 lj_err_callermsg(L
, lj_strfmt_pushf(L
, fmt
, name
, buf
));
193 static int clib_needext(const char *s
)
196 if (*s
== '/' || *s
== '\\' || *s
== '.') return 0;
202 static const char *clib_extname(lua_State
*L
, const char *name
)
204 if (clib_needext(name
)) {
205 name
= lj_strfmt_pushf(L
, "%s.dll", name
);
211 static void *clib_loadlib(lua_State
*L
, const char *name
, int global
)
213 DWORD oldwerr
= GetLastError();
214 void *h
= LJ_WIN_LOADLIBA(clib_extname(L
, name
));
215 if (!h
) clib_error(L
, "cannot load module " LUA_QS
": %s", name
);
216 SetLastError(oldwerr
);
221 static void clib_unloadlib(CLibrary
*cl
)
223 if (cl
->handle
== CLIB_DEFHANDLE
) {
226 for (i
= CLIB_HANDLE_KERNEL32
; i
< CLIB_HANDLE_MAX
; i
++) {
227 void *h
= clib_def_handle
[i
];
229 clib_def_handle
[i
] = NULL
;
230 FreeLibrary((HINSTANCE
)h
);
234 } else if (cl
->handle
) {
235 FreeLibrary((HINSTANCE
)cl
->handle
);
240 EXTERN_C IMAGE_DOS_HEADER __ImageBase
;
243 static void *clib_getsym(CLibrary
*cl
, const char *name
)
246 if (cl
->handle
== CLIB_DEFHANDLE
) { /* Search default libraries. */
248 for (i
= 0; i
< CLIB_HANDLE_MAX
; i
++) {
249 HINSTANCE h
= (HINSTANCE
)clib_def_handle
[i
];
250 if (!(void *)h
) { /* Resolve default library handles (once). */
252 h
= (HINSTANCE
)&__ImageBase
;
255 case CLIB_HANDLE_EXE
: GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
, NULL
, &h
); break;
256 case CLIB_HANDLE_DLL
:
257 GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
,
258 (const char *)clib_def_handle
, &h
);
260 case CLIB_HANDLE_CRT
:
261 GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
|GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
,
262 (const char *)&_fmode
, &h
);
264 case CLIB_HANDLE_KERNEL32
: h
= LJ_WIN_LOADLIBA("kernel32.dll"); break;
265 case CLIB_HANDLE_USER32
: h
= LJ_WIN_LOADLIBA("user32.dll"); break;
266 case CLIB_HANDLE_GDI32
: h
= LJ_WIN_LOADLIBA("gdi32.dll"); break;
270 clib_def_handle
[i
] = (void *)h
;
272 p
= (void *)GetProcAddress(h
, name
);
276 p
= (void *)GetProcAddress((HINSTANCE
)cl
->handle
, name
);
283 #define CLIB_DEFHANDLE NULL
285 LJ_NORET LJ_NOINLINE
static void clib_error(lua_State
*L
, const char *fmt
,
288 lj_err_callermsg(L
, lj_strfmt_pushf(L
, fmt
, name
, "no support for this OS"));
291 static void *clib_loadlib(lua_State
*L
, const char *name
, int global
)
293 lj_err_callermsg(L
, "no support for loading dynamic libraries for this OS");
294 UNUSED(name
); UNUSED(global
);
298 static void clib_unloadlib(CLibrary
*cl
)
303 static void *clib_getsym(CLibrary
*cl
, const char *name
)
305 UNUSED(cl
); UNUSED(name
);
311 /* -- C library indexing -------------------------------------------------- */
313 #if LJ_TARGET_X86 && LJ_ABI_WIN
314 /* Compute argument size for fastcall/stdcall functions. */
315 static CTSize
clib_func_argsize(CTState
*cts
, CType
*ct
)
320 ct
= ctype_get(cts
, ct
->sib
);
321 if (ctype_isfield(ct
->info
)) {
322 d
= ctype_rawchild(cts
, ct
);
323 n
+= ((d
->size
+ 3) & ~3);
330 /* Get redirected or mangled external symbol. */
331 static const char *clib_extsym(CTState
*cts
, CType
*ct
, GCstr
*name
)
334 CType
*ctf
= ctype_get(cts
, ct
->sib
);
335 if (ctype_isxattrib(ctf
->info
, CTA_REDIR
))
336 return strdata(gco2str(gcref(ctf
->name
)));
338 return strdata(name
);
341 /* Index a C library by name. */
342 TValue
*lj_clib_index(lua_State
*L
, CLibrary
*cl
, GCstr
*name
)
344 TValue
*tv
= lj_tab_setstr(L
, cl
->cache
, name
);
345 if (LJ_UNLIKELY(tvisnil(tv
))) {
346 CTState
*cts
= ctype_cts(L
);
348 CTypeID id
= lj_ctype_getname(cts
, &ct
, name
, CLNS_INDEX
);
350 lj_err_callerv(L
, LJ_ERR_FFI_NODECL
, strdata(name
));
351 if (ctype_isconstval(ct
->info
)) {
352 CType
*ctt
= ctype_child(cts
, ct
);
353 lj_assertCTS(ctype_isinteger(ctt
->info
) && ctt
->size
<= 4,
354 "only 32 bit const supported"); /* NYI */
355 if ((ctt
->info
& CTF_UNSIGNED
) && (int32_t)ct
->size
< 0)
356 setnumV(tv
, (lua_Number
)(uint32_t)ct
->size
);
358 setintV(tv
, (int32_t)ct
->size
);
360 const char *sym
= clib_extsym(cts
, ct
, name
);
361 #if LJ_TARGET_WINDOWS
362 DWORD oldwerr
= GetLastError();
364 void *p
= clib_getsym(cl
, sym
);
366 lj_assertCTS(ctype_isfunc(ct
->info
) || ctype_isextern(ct
->info
),
367 "unexpected ctype %08x in clib", ct
->info
);
368 #if LJ_TARGET_X86 && LJ_ABI_WIN
369 /* Retry with decorated name for fastcall/stdcall functions. */
370 if (!p
&& ctype_isfunc(ct
->info
)) {
371 CTInfo cconv
= ctype_cconv(ct
->info
);
372 if (cconv
== CTCC_FASTCALL
|| cconv
== CTCC_STDCALL
) {
373 CTSize sz
= clib_func_argsize(cts
, ct
);
374 const char *symd
= lj_strfmt_pushf(L
,
375 cconv
== CTCC_FASTCALL
? "@%s@%d" : "_%s@%d",
378 p
= clib_getsym(cl
, symd
);
383 clib_error(L
, "cannot resolve symbol " LUA_QS
": %s", sym
);
384 #if LJ_TARGET_WINDOWS
385 SetLastError(oldwerr
);
387 cd
= lj_cdata_new(cts
, id
, CTSIZE_PTR
);
388 *(void **)cdataptr(cd
) = p
;
389 setcdataV(L
, tv
, cd
);
390 lj_gc_anybarriert(L
, cl
->cache
);
396 /* -- C library management ------------------------------------------------ */
398 /* Create a new CLibrary object and push it on the stack. */
399 static CLibrary
*clib_new(lua_State
*L
, GCtab
*mt
)
401 GCtab
*t
= lj_tab_new(L
, 0, 0);
402 GCudata
*ud
= lj_udata_new(L
, sizeof(CLibrary
), t
);
403 CLibrary
*cl
= (CLibrary
*)uddata(ud
);
405 ud
->udtype
= UDTYPE_FFI_CLIB
;
406 /* NOBARRIER: The GCudata is new (marked white). */
407 setgcref(ud
->metatable
, obj2gco(mt
));
408 setudataV(L
, L
->top
++, ud
);
412 /* Load a C library. */
413 void lj_clib_load(lua_State
*L
, GCtab
*mt
, GCstr
*name
, int global
)
415 void *handle
= clib_loadlib(L
, strdata(name
), global
);
416 CLibrary
*cl
= clib_new(L
, mt
);
420 /* Unload a C library. */
421 void lj_clib_unload(CLibrary
*cl
)
427 /* Create the default C library object. */
428 void lj_clib_default(lua_State
*L
, GCtab
*mt
)
430 CLibrary
*cl
= clib_new(L
, mt
);
431 cl
->handle
= CLIB_DEFHANDLE
;