Coverage Report

Created: 2025-07-01 06:58

/src/tarantool/third_party/luajit/src/lj_clib.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
** FFI C library loader.
3
** Copyright (C) 2005-2017 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
#include "lj_strfmt.h"
20
21
/* -- OS-specific functions ----------------------------------------------- */
22
23
#if LJ_TARGET_DLOPEN
24
25
#include <dlfcn.h>
26
#include <stdio.h>
27
28
#if defined(RTLD_DEFAULT)
29
6.70k
#define CLIB_DEFHANDLE  RTLD_DEFAULT
30
#elif LJ_TARGET_OSX || LJ_TARGET_BSD
31
#define CLIB_DEFHANDLE  ((void *)(intptr_t)-2)
32
#else
33
#define CLIB_DEFHANDLE  NULL
34
#endif
35
36
LJ_NORET LJ_NOINLINE static void clib_error_(lua_State *L)
37
0
{
38
0
  lj_err_callermsg(L, dlerror());
39
0
}
40
41
0
#define clib_error(L, fmt, name)  clib_error_(L)
42
43
#if LJ_TARGET_CYGWIN
44
#define CLIB_SOPREFIX "cyg"
45
#else
46
0
#define CLIB_SOPREFIX "lib"
47
#endif
48
49
#if LJ_TARGET_OSX
50
#define CLIB_SOEXT  "%s.dylib"
51
#elif LJ_TARGET_CYGWIN
52
#define CLIB_SOEXT  "%s.dll"
53
#else
54
0
#define CLIB_SOEXT  "%s.so"
55
#endif
56
57
static const char *clib_extname(lua_State *L, const char *name)
58
0
{
59
0
  if (!strchr(name, '/')
60
#if LJ_TARGET_CYGWIN
61
      && !strchr(name, '\\')
62
#endif
63
0
     ) {
64
0
    if (!strchr(name, '.')) {
65
0
      name = lj_strfmt_pushf(L, CLIB_SOEXT, name);
66
0
      L->top--;
67
#if LJ_TARGET_CYGWIN
68
    } else {
69
      return name;
70
#endif
71
0
    }
72
0
    if (!(name[0] == CLIB_SOPREFIX[0] && name[1] == CLIB_SOPREFIX[1] &&
73
0
    name[2] == CLIB_SOPREFIX[2])) {
74
0
      name = lj_strfmt_pushf(L, CLIB_SOPREFIX "%s", name);
75
0
      L->top--;
76
0
    }
77
0
  }
78
0
  return name;
79
0
}
80
81
/* Check for a recognized ld script line. */
82
static const char *clib_check_lds(lua_State *L, const char *buf)
83
0
{
84
0
  char *p, *e;
85
0
  if ((!strncmp(buf, "GROUP", 5) || !strncmp(buf, "INPUT", 5)) &&
86
0
      (p = strchr(buf, '('))) {
87
0
    while (*++p == ' ') ;
88
0
    for (e = p; *e && *e != ' ' && *e != ')'; e++) ;
89
0
    return strdata(lj_str_new(L, p, e-p));
90
0
  }
91
0
  return NULL;
92
0
}
93
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)
96
0
{
97
0
  FILE *fp = fopen(name, "r");
98
0
  const char *p = NULL;
99
0
  if (fp) {
100
0
    char buf[256];
101
0
    if (fgets(buf, sizeof(buf), fp)) {
102
0
      if (!strncmp(buf, "/* GNU ld script", 16)) {  /* ld script magic? */
103
0
  while (fgets(buf, sizeof(buf), fp)) {  /* Check all lines. */
104
0
    p = clib_check_lds(L, buf);
105
0
    if (p) break;
106
0
  }
107
0
      } else {  /* Otherwise check only the first line. */
108
0
  p = clib_check_lds(L, buf);
109
0
      }
110
0
    }
111
0
    fclose(fp);
112
0
  }
113
0
  return p;
114
0
}
115
116
static void *clib_loadlib(lua_State *L, const char *name, int global)
117
0
{
118
0
  void *h = dlopen(clib_extname(L, name),
119
0
       RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL));
120
0
  if (!h) {
121
0
    const char *e, *err = dlerror();
122
0
    if (err && *err == '/' && (e = strchr(err, ':')) &&
123
0
  (name = clib_resolve_lds(L, strdata(lj_str_new(L, err, e-err))))) {
124
0
      h = dlopen(name, RTLD_LAZY | (global?RTLD_GLOBAL:RTLD_LOCAL));
125
0
      if (h) return h;
126
0
      err = dlerror();
127
0
    }
128
0
    if (!err) err = "dlopen failed";
129
0
    lj_err_callermsg(L, err);
130
0
  }
131
0
  return h;
132
0
}
133
134
static void clib_unloadlib(CLibrary *cl)
135
6.70k
{
136
6.70k
  if (cl->handle && cl->handle != CLIB_DEFHANDLE)
137
0
    dlclose(cl->handle);
138
6.70k
}
139
140
static void *clib_getsym(CLibrary *cl, const char *name)
141
0
{
142
0
  void *p = dlsym(cl->handle, name);
143
0
  return p;
144
0
}
145
146
#elif LJ_TARGET_WINDOWS
147
148
#define WIN32_LEAN_AND_MEAN
149
#include <windows.h>
150
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*);
155
#endif
156
157
#define CLIB_DEFHANDLE  ((void *)-1)
158
159
/* Default libraries. */
160
enum {
161
  CLIB_HANDLE_EXE,
162
#if !LJ_TARGET_UWP
163
  CLIB_HANDLE_DLL,
164
  CLIB_HANDLE_CRT,
165
  CLIB_HANDLE_KERNEL32,
166
  CLIB_HANDLE_USER32,
167
  CLIB_HANDLE_GDI32,
168
#endif
169
  CLIB_HANDLE_MAX
170
};
171
172
static void *clib_def_handle[CLIB_HANDLE_MAX];
173
174
LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt,
175
              const char *name)
176
{
177
  DWORD err = GetLastError();
178
#if LJ_TARGET_XBOXONE
179
  wchar_t wbuf[128];
180
  char buf[128*2];
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))
184
#else
185
  char buf[128];
186
  if (!FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS|FORMAT_MESSAGE_FROM_SYSTEM,
187
          NULL, err, 0, buf, sizeof(buf), NULL))
188
#endif
189
    buf[0] = '\0';
190
  lj_err_callermsg(L, lj_strfmt_pushf(L, fmt, name, buf));
191
}
192
193
static int clib_needext(const char *s)
194
{
195
  while (*s) {
196
    if (*s == '/' || *s == '\\' || *s == '.') return 0;
197
    s++;
198
  }
199
  return 1;
200
}
201
202
static const char *clib_extname(lua_State *L, const char *name)
203
{
204
  if (clib_needext(name)) {
205
    name = lj_strfmt_pushf(L, "%s.dll", name);
206
    L->top--;
207
  }
208
  return name;
209
}
210
211
static void *clib_loadlib(lua_State *L, const char *name, int global)
212
{
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);
217
  UNUSED(global);
218
  return h;
219
}
220
221
static void clib_unloadlib(CLibrary *cl)
222
{
223
  if (cl->handle == CLIB_DEFHANDLE) {
224
#if !LJ_TARGET_UWP
225
    MSize i;
226
    for (i = CLIB_HANDLE_KERNEL32; i < CLIB_HANDLE_MAX; i++) {
227
      void *h = clib_def_handle[i];
228
      if (h) {
229
  clib_def_handle[i] = NULL;
230
  FreeLibrary((HINSTANCE)h);
231
      }
232
    }
233
#endif
234
  } else if (cl->handle) {
235
    FreeLibrary((HINSTANCE)cl->handle);
236
  }
237
}
238
239
#if LJ_TARGET_UWP
240
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
241
#endif
242
243
static void *clib_getsym(CLibrary *cl, const char *name)
244
{
245
  void *p = NULL;
246
  if (cl->handle == CLIB_DEFHANDLE) {  /* Search default libraries. */
247
    MSize i;
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). */
251
#if LJ_TARGET_UWP
252
  h = (HINSTANCE)&__ImageBase;
253
#else
254
  switch (i) {
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);
259
    break;
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);
263
    break;
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;
267
  }
268
  if (!h) continue;
269
#endif
270
  clib_def_handle[i] = (void *)h;
271
      }
272
      p = (void *)GetProcAddress(h, name);
273
      if (p) break;
274
    }
275
  } else {
276
    p = (void *)GetProcAddress((HINSTANCE)cl->handle, name);
277
  }
278
  return p;
279
}
280
281
#else
282
283
#define CLIB_DEFHANDLE  NULL
284
285
LJ_NORET LJ_NOINLINE static void clib_error(lua_State *L, const char *fmt,
286
              const char *name)
287
{
288
  lj_err_callermsg(L, lj_strfmt_pushf(L, fmt, name, "no support for this OS"));
289
}
290
291
static void *clib_loadlib(lua_State *L, const char *name, int global)
292
{
293
  lj_err_callermsg(L, "no support for loading dynamic libraries for this OS");
294
  UNUSED(name); UNUSED(global);
295
  return NULL;
296
}
297
298
static void clib_unloadlib(CLibrary *cl)
299
{
300
  UNUSED(cl);
301
}
302
303
static void *clib_getsym(CLibrary *cl, const char *name)
304
{
305
  UNUSED(cl); UNUSED(name);
306
  return NULL;
307
}
308
309
#endif
310
311
/* -- C library indexing -------------------------------------------------- */
312
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)
316
{
317
  CTSize n = 0;
318
  while (ct->sib) {
319
    CType *d;
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);
324
    }
325
  }
326
  return n;
327
}
328
#endif
329
330
/* Get redirected or mangled external symbol. */
331
static const char *clib_extsym(CTState *cts, CType *ct, GCstr *name)
332
0
{
333
0
  if (ct->sib) {
334
0
    CType *ctf = ctype_get(cts, ct->sib);
335
0
    if (ctype_isxattrib(ctf->info, CTA_REDIR))
336
0
      return strdata(gco2str(gcref(ctf->name)));
337
0
  }
338
0
  return strdata(name);
339
0
}
340
341
/* Index a C library by name. */
342
TValue *lj_clib_index(lua_State *L, CLibrary *cl, GCstr *name)
343
0
{
344
0
  TValue *tv = lj_tab_setstr(L, cl->cache, name);
345
0
  if (LJ_UNLIKELY(tvisnil(tv))) {
346
0
    CTState *cts = ctype_cts(L);
347
0
    CType *ct;
348
0
    CTypeID id = lj_ctype_getname(cts, &ct, name, CLNS_INDEX);
349
0
    if (!id)
350
0
      lj_err_callerv(L, LJ_ERR_FFI_NODECL, strdata(name));
351
0
    if (ctype_isconstval(ct->info)) {
352
0
      CType *ctt = ctype_child(cts, ct);
353
0
      lj_assertCTS(ctype_isinteger(ctt->info) && ctt->size <= 4,
354
0
       "only 32 bit const supported");  /* NYI */
355
0
      if ((ctt->info & CTF_UNSIGNED) && (int32_t)ct->size < 0)
356
0
  setnumV(tv, (lua_Number)(uint32_t)ct->size);
357
0
      else
358
0
  setintV(tv, (int32_t)ct->size);
359
0
    } else {
360
0
      const char *sym = clib_extsym(cts, ct, name);
361
#if LJ_TARGET_WINDOWS
362
      DWORD oldwerr = GetLastError();
363
#endif
364
0
      void *p = clib_getsym(cl, sym);
365
0
      GCcdata *cd;
366
0
      lj_assertCTS(ctype_isfunc(ct->info) || ctype_isextern(ct->info),
367
0
       "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",
376
             sym, sz);
377
    L->top--;
378
    p = clib_getsym(cl, symd);
379
  }
380
      }
381
#endif
382
0
      if (!p)
383
0
  clib_error(L, "cannot resolve symbol " LUA_QS ": %s", sym);
384
#if LJ_TARGET_WINDOWS
385
      SetLastError(oldwerr);
386
#endif
387
0
      cd = lj_cdata_new(cts, id, CTSIZE_PTR);
388
0
      *(void **)cdataptr(cd) = p;
389
0
      setcdataV(L, tv, cd);
390
0
      lj_gc_anybarriert(L, cl->cache);
391
0
    }
392
0
  }
393
0
  return tv;
394
0
}
395
396
/* -- C library management ------------------------------------------------ */
397
398
/* Create a new CLibrary object and push it on the stack. */
399
static CLibrary *clib_new(lua_State *L, GCtab *mt)
400
6.70k
{
401
6.70k
  GCtab *t = lj_tab_new(L, 0, 0);
402
6.70k
  GCudata *ud = lj_udata_new(L, sizeof(CLibrary), t);
403
6.70k
  CLibrary *cl = (CLibrary *)uddata(ud);
404
6.70k
  cl->cache = t;
405
6.70k
  ud->udtype = UDTYPE_FFI_CLIB;
406
  /* NOBARRIER: The GCudata is new (marked white). */
407
6.70k
  setgcref(ud->metatable, obj2gco(mt));
408
6.70k
  setudataV(L, L->top++, ud);
409
6.70k
  return cl;
410
6.70k
}
411
412
/* Load a C library. */
413
void lj_clib_load(lua_State *L, GCtab *mt, GCstr *name, int global)
414
0
{
415
0
  void *handle = clib_loadlib(L, strdata(name), global);
416
0
  CLibrary *cl = clib_new(L, mt);
417
0
  cl->handle = handle;
418
0
}
419
420
/* Unload a C library. */
421
void lj_clib_unload(CLibrary *cl)
422
6.70k
{
423
6.70k
  clib_unloadlib(cl);
424
6.70k
  cl->handle = NULL;
425
6.70k
}
426
427
/* Create the default C library object. */
428
void lj_clib_default(lua_State *L, GCtab *mt)
429
6.70k
{
430
6.70k
  CLibrary *cl = clib_new(L, mt);
431
6.70k
  cl->handle = CLIB_DEFHANDLE;
432
6.70k
}
433
434
#endif