diff options
Diffstat (limited to 'src/lj_clib.c')
-rw-r--r-- | src/lj_clib.c | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/src/lj_clib.c b/src/lj_clib.c new file mode 100644 index 00000000..91309eb2 --- /dev/null +++ b/src/lj_clib.c | |||
@@ -0,0 +1,294 @@ | |||
1 | /* | ||
2 | ** FFI C library loader. | ||
3 | ** Copyright (C) 2005-2011 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 | |||
26 | #if defined(RTLD_DEFAULT) | ||
27 | #define CLIB_DEFHANDLE RTLD_DEFAULT | ||
28 | #elif LJ_TARGET_OSX || LJ_TARGET_BSD | ||
29 | #define CLIB_DEFHANDLE ((void *)-2) | ||
30 | #else | ||
31 | #define CLIB_DEFHANDLE NULL | ||
32 | #endif | ||
33 | |||
34 | LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L) | ||
35 | { | ||
36 | lj_err_callermsg(L, dlerror()); | ||
37 | } | ||
38 | |||
39 | #if LJ_TARGET_OSX | ||
40 | #define CLIB_SOEXT "%s.dylib" | ||
41 | #else | ||
42 | #define CLIB_SOEXT "%s.so" | ||
43 | #endif | ||
44 | |||
45 | static const char *clib_extname(lua_State *L, GCstr *name) | ||
46 | { | ||
47 | const char *s = strdata(name); | ||
48 | if (!strchr(s, '/')) { | ||
49 | if (!strchr(s, '.')) { | ||
50 | s = lj_str_pushf(L, CLIB_SOEXT, s); | ||
51 | L->top--; | ||
52 | } | ||
53 | if (!(s[0] == 'l' && s[1] == 'i' && s[2] == 'b')) { | ||
54 | s = lj_str_pushf(L, "lib%s", s); | ||
55 | L->top--; | ||
56 | } | ||
57 | } | ||
58 | return s; | ||
59 | } | ||
60 | |||
61 | static void *clib_loadlib(lua_State *L, GCstr *name, int global) | ||
62 | { | ||
63 | void *h = dlopen(clib_extname(L, name), | ||
64 | RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL)); | ||
65 | if (!h) clib_error(L); | ||
66 | return h; | ||
67 | } | ||
68 | |||
69 | static void clib_unloadlib(CLibrary *cl) | ||
70 | { | ||
71 | if (!cl->handle && cl->handle != CLIB_DEFHANDLE) | ||
72 | dlclose(cl->handle); | ||
73 | } | ||
74 | |||
75 | static void *clib_getsym(lua_State *L, CLibrary *cl, GCstr *name) | ||
76 | { | ||
77 | void *p = dlsym(cl->handle, strdata(name)); | ||
78 | if (!p) clib_error(L); | ||
79 | return p; | ||
80 | } | ||
81 | |||
82 | #elif LJ_TARGET_WINDOWS | ||
83 | |||
84 | #define WIN32_LEAN_AND_MEAN | ||
85 | #ifndef WINVER | ||
86 | #define WINVER 0x0500 | ||
87 | #endif | ||
88 | #include <windows.h> | ||
89 | |||
90 | #ifndef GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | ||
91 | #define GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS 4 | ||
92 | BOOL WINAPI GetModuleHandleExA(DWORD, LPCSTR, HMODULE*); | ||
93 | #endif | ||
94 | |||
95 | #define CLIB_DEFHANDLE ((void *)-1) | ||
96 | |||
97 | /* Default libraries. */ | ||
98 | enum { | ||
99 | CLIB_HANDLE_EXE, | ||
100 | CLIB_HANDLE_DLL, | ||
101 | CLIB_HANDLE_CRT, | ||
102 | CLIB_HANDLE_KERNEL32, | ||
103 | CLIB_HANDLE_USER32, | ||
104 | CLIB_HANDLE_GDI32, | ||
105 | CLIB_HANDLE_MAX | ||
106 | }; | ||
107 | |||
108 | static void *clib_def_handle[CLIB_HANDLE_MAX]; | ||
109 | |||
110 | LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt, | ||
111 | const char *name) | ||
112 | { | ||
113 | DWORD err = GetLastError(); | ||
114 | char buf[128]; | ||
115 | if (!FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM, | ||
116 | NULL, err, 0, buf, sizeof(buf), NULL)) | ||
117 | buf[0] = '\0'; | ||
118 | lj_err_callermsg(L, lj_str_pushf(L, fmt, name, buf)); | ||
119 | } | ||
120 | |||
121 | static int clib_needext(const char *s) | ||
122 | { | ||
123 | while (*s) { | ||
124 | if (*s == '/' || *s == '\\' || *s == '.') return 0; | ||
125 | s++; | ||
126 | } | ||
127 | return 1; | ||
128 | } | ||
129 | |||
130 | static const char *clib_extname(lua_State *L, GCstr *name) | ||
131 | { | ||
132 | const char *s = strdata(name); | ||
133 | if (clib_needext(s)) { | ||
134 | s = lj_str_pushf(L, "%s.dll", s); | ||
135 | L->top--; | ||
136 | } | ||
137 | return s; | ||
138 | } | ||
139 | |||
140 | static void *clib_loadlib(lua_State *L, GCstr *name, int global) | ||
141 | { | ||
142 | void *h = (void *)LoadLibraryA(clib_extname(L, name)); | ||
143 | if (!h) clib_error(L, "cannot load module " LUA_QS ": %s", strdata(name)); | ||
144 | UNUSED(global); | ||
145 | return h; | ||
146 | } | ||
147 | |||
148 | static void clib_unloadlib(CLibrary *cl) | ||
149 | { | ||
150 | if (cl->handle == CLIB_DEFHANDLE) { | ||
151 | MSize i; | ||
152 | for (i = 0; i < CLIB_HANDLE_MAX; i++) | ||
153 | if (clib_def_handle[i]) | ||
154 | FreeLibrary((HINSTANCE)clib_def_handle[i]); | ||
155 | } else if (!cl->handle) { | ||
156 | FreeLibrary((HINSTANCE)cl->handle); | ||
157 | } | ||
158 | } | ||
159 | |||
160 | static void *clib_getsym(lua_State *L, CLibrary *cl, GCstr *name) | ||
161 | { | ||
162 | const char *sym = strdata(name); | ||
163 | void *p; | ||
164 | if (cl->handle == CLIB_DEFHANDLE) { /* Search default libraries. */ | ||
165 | MSize i; | ||
166 | for (i = 0; i < CLIB_HANDLE_MAX; i++) { | ||
167 | HINSTANCE h = (HINSTANCE)clib_def_handle[i]; | ||
168 | if (!(void *)h) { /* Resolve default library handles (once). */ | ||
169 | switch (i) { | ||
170 | case CLIB_HANDLE_EXE: GetModuleHandleExA(0, NULL, &h); break; | ||
171 | case CLIB_HANDLE_DLL: | ||
172 | GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | ||
173 | (const char *)clib_def_handle, &h); | ||
174 | break; | ||
175 | case CLIB_HANDLE_CRT: | ||
176 | GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, | ||
177 | (const char *)&_fmode, &h); | ||
178 | break; | ||
179 | case CLIB_HANDLE_KERNEL32: h = LoadLibraryA("kernel32.dll"); break; | ||
180 | case CLIB_HANDLE_USER32: h = LoadLibraryA("user32.dll"); break; | ||
181 | case CLIB_HANDLE_GDI32: h = LoadLibraryA("gdi32.dll"); break; | ||
182 | } | ||
183 | if (!h) continue; | ||
184 | clib_def_handle[i] = (void *)h; | ||
185 | } | ||
186 | p = (void *)GetProcAddress(h, sym); | ||
187 | if (p) break; | ||
188 | } | ||
189 | } else { | ||
190 | p = (void *)GetProcAddress((HINSTANCE)cl->handle, sym); | ||
191 | } | ||
192 | if (!p) clib_error(L, "cannot resolve symbol " LUA_QS ": %s", sym); | ||
193 | return p; | ||
194 | } | ||
195 | |||
196 | #else | ||
197 | |||
198 | #define CLIB_DEFHANDLE NULL | ||
199 | |||
200 | static void *clib_loadlib(lua_State *L, GCstr *name, int global) | ||
201 | { | ||
202 | lj_err_callermsg(L, "no support for loading dynamic libraries for this OS"); | ||
203 | UNUSED(name); UNUSED(global); | ||
204 | return NULL; | ||
205 | } | ||
206 | |||
207 | static void clib_unloadlib(CLibrary *cl) | ||
208 | { | ||
209 | UNUSED(cl); | ||
210 | } | ||
211 | |||
212 | static void *clib_getsym(lua_State *L, CLibrary *cl, GCstr *name) | ||
213 | { | ||
214 | lj_err_callermsg(L, "no support for resolving symbols for this OS"); | ||
215 | UNUSED(cl); UNUSED(name); | ||
216 | return NULL; | ||
217 | } | ||
218 | |||
219 | #endif | ||
220 | |||
221 | /* -- C library indexing -------------------------------------------------- */ | ||
222 | |||
223 | /* Namespace for C library indexing. */ | ||
224 | #define CLNS_INDEX \ | ||
225 | ((1u<<CT_FUNC)|(1u<<CT_EXTERN)|(1u<<CT_CONSTVAL)) | ||
226 | |||
227 | /* Index a C library by name. */ | ||
228 | TValue *lj_clib_index(lua_State *L, CLibrary *cl, GCstr *name) | ||
229 | { | ||
230 | TValue *tv = lj_tab_setstr(L, cl->cache, name); | ||
231 | if (LJ_UNLIKELY(tvisnil(tv))) { | ||
232 | CTState *cts = ctype_cts(L); | ||
233 | CType *ct; | ||
234 | CTypeID id = lj_ctype_getname(cts, &ct, name, CLNS_INDEX); | ||
235 | if (!id) | ||
236 | lj_err_callerv(L, LJ_ERR_FFI_NODECL, strdata(name)); | ||
237 | if (ctype_isconstval(ct->info)) { | ||
238 | CType *ctt = ctype_child(cts, ct); | ||
239 | lua_assert(ctype_isinteger(ctt->info) && ctt->size <= 4); | ||
240 | if ((ctt->info & CTF_UNSIGNED) && ctt->size == 4) | ||
241 | setnumV(tv, (lua_Number)(uint32_t)ct->size); | ||
242 | else | ||
243 | setnumV(tv, (lua_Number)(int32_t)ct->size); | ||
244 | } else { | ||
245 | void *p = clib_getsym(L, cl, name); | ||
246 | GCcdata *cd; | ||
247 | lua_assert(ctype_isfunc(ct->info) || ctype_isextern(ct->info)); | ||
248 | cd = lj_cdata_new(cts, id, CTSIZE_PTR); | ||
249 | *(void **)cdataptr(cd) = p; | ||
250 | setcdataV(L, tv, cd); | ||
251 | } | ||
252 | } | ||
253 | return tv; | ||
254 | } | ||
255 | |||
256 | /* -- C library management ------------------------------------------------ */ | ||
257 | |||
258 | /* Create a new CLibrary object and push it on the stack. */ | ||
259 | static CLibrary *clib_new(lua_State *L, GCtab *mt) | ||
260 | { | ||
261 | GCtab *t = lj_tab_new(L, 0, 0); | ||
262 | GCudata *ud = lj_udata_new(L, sizeof(CLibrary), t); | ||
263 | CLibrary *cl = (CLibrary *)uddata(ud); | ||
264 | cl->cache = t; | ||
265 | ud->udtype = UDTYPE_FFI_CLIB; | ||
266 | /* NOBARRIER: The GCudata is new (marked white). */ | ||
267 | setgcref(ud->metatable, obj2gco(mt)); | ||
268 | setudataV(L, L->top++, ud); | ||
269 | return cl; | ||
270 | } | ||
271 | |||
272 | /* Load a C library. */ | ||
273 | void lj_clib_load(lua_State *L, GCtab *mt, GCstr *name, int global) | ||
274 | { | ||
275 | void *handle = clib_loadlib(L, name, global); | ||
276 | CLibrary *cl = clib_new(L, mt); | ||
277 | cl->handle = handle; | ||
278 | } | ||
279 | |||
280 | /* Unload a C library. */ | ||
281 | void lj_clib_unload(CLibrary *cl) | ||
282 | { | ||
283 | clib_unloadlib(cl); | ||
284 | cl->handle = NULL; | ||
285 | } | ||
286 | |||
287 | /* Create the default C library object. */ | ||
288 | void lj_clib_default(lua_State *L, GCtab *mt) | ||
289 | { | ||
290 | CLibrary *cl = clib_new(L, mt); | ||
291 | cl->handle = CLIB_DEFHANDLE; | ||
292 | } | ||
293 | |||
294 | #endif | ||