Ruby: dln.c Source File

Ruby 3.2.5p208 (2024-07-26 revision 31d0f1a2e7dbfb60731d1f05b868e1d578cda493)

dln.c
1/**********************************************************************
2
3 dln.c -
4
5 $Author$
6 created at: Tue Jan 18 17:05:06 JST 1994
7
8 Copyright (C) 1993-2007 Yukihiro Matsumoto
9
10**********************************************************************/
11
12#ifdef RUBY_EXPORT
13#include "ruby/ruby.h"
14#define dln_notimplement rb_notimplement
15#define dln_memerror rb_memerror
16#define dln_exit rb_exit
17#define dln_loaderror rb_loaderror
18#define dln_fatalerror rb_fatal
19#else
20#define dln_notimplement --->>> dln not implemented <<<---
21#define dln_memerror abort
22#define dln_exit exit
23static void dln_loaderror(const char *format, ...);
24#define dln_fatalerror dln_loaderror
25#endif
26#include "dln.h"
27#include "internal.h"
28#include "internal/compilers.h"
29
30#ifdef HAVE_STDLIB_H
31# include <stdlib.h>
32#endif
33
34#if defined(HAVE_ALLOCA_H)
35#include <alloca.h>
36#endif
37
38#ifdef HAVE_STRING_H
39# include <string.h>
40#else
41# include <strings.h>
42#endif
43
44#if defined __APPLE__
45# include <AvailabilityMacros.h>
46#endif
47
48#ifndef xmalloc
49void *xmalloc();
50void *xcalloc();
51void *xrealloc();
52#endif
53
54#undef free
55#define free(x) xfree(x)
56
57#include <stdio.h>
58#if defined(_WIN32)
59#include "missing/file.h"
60#endif
61#include <sys/types.h>
62#include <sys/stat.h>
63
64#ifndef S_ISDIR
65# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
66#endif
67
68#ifdef HAVE_SYS_PARAM_H
69# include <sys/param.h>
70#endif
71#ifndef MAXPATHLEN
72# define MAXPATHLEN 1024
73#endif
74
75#ifdef HAVE_UNISTD_H
76# include <unistd.h>
77#endif
78
79#ifndef dln_loaderror
80static void
81dln_loaderror(const char *format, ...)
82{
83 va_list ap;
84 va_start(ap, format);
85 vfprintf(stderr, format, ap);
86 va_end(ap);
87 abort();
88}
89#endif
90
91#if defined(HAVE_DLOPEN) && !defined(_AIX) && !defined(_UNICOSMP)
92/* dynamic load with dlopen() */
93# define USE_DLN_DLOPEN
94#endif
95
96#if defined(__hp9000s300) || ((defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__)) && !defined(__ELF__)) || defined(NeXT)
97# define EXTERNAL_PREFIX "_"
98#else
99# define EXTERNAL_PREFIX ""
100#endif
101#define FUNCNAME_PREFIX EXTERNAL_PREFIX"Init_"
102
103#if defined __CYGWIN__ || defined DOSISH
104#define isdirsep(x) ((x) == '/' || (x) == '\\')
105#else
106#define isdirsep(x) ((x) == '/')
107#endif
108
109#if defined(_WIN32) || defined(USE_DLN_DLOPEN)
110static size_t
111init_funcname_len(const char **file)
112{
113 const char *p = *file, *base, *dot = NULL;
114
115 /* Load the file as an object one */
116 for (base = p; *p; p++) { /* Find position of last '/' */
117 if (*p == '.' && !dot) dot = p;
118 if (isdirsep(*p)) base = p+1, dot = NULL;
119 }
120 *file = base;
121 /* Delete suffix if it exists */
122 return (dot ? dot : p) - base;
123}
124
125static const char funcname_prefix[sizeof(FUNCNAME_PREFIX) - 1] = FUNCNAME_PREFIX;
126
127#define init_funcname(buf, file) do {\
128 const char *base = (file);\
129 const size_t flen = init_funcname_len(&base);\
130 const size_t plen = sizeof(funcname_prefix);\
131 char *const tmp = ALLOCA_N(char, plen+flen+1);\
132 if (!tmp) {\
133 dln_memerror();\
134 }\
135 memcpy(tmp, funcname_prefix, plen);\
136 memcpy(tmp+plen, base, flen);\
137 tmp[plen+flen] = '\0';\
138 *(buf) = tmp;\
139} while (0)
140#endif
141
142#ifdef USE_DLN_DLOPEN
143# include <dlfcn.h>
144#endif
145
146#if defined(_AIX)
147#include <ctype.h> /* for isdigit() */
148#include <errno.h> /* for global errno */
149#include <sys/ldr.h>
150#endif
151
152#ifdef NeXT
153#if NS_TARGET_MAJOR < 4
154#include <mach-o/rld.h>
155#else
156#include <mach-o/dyld.h>
157#ifndef NSLINKMODULE_OPTION_BINDNOW
158#define NSLINKMODULE_OPTION_BINDNOW 1
159#endif
160#endif
161#endif
162
163#ifdef _WIN32
164#include <windows.h>
165#include <imagehlp.h>
166#endif
167
168#ifdef _WIN32
169static const char *
170dln_strerror(char *message, size_t size)
171{
172 int error = GetLastError();
173 char *p = message;
174 size_t len = snprintf(message, size, "%d: ", error);
175
176#define format_message(sublang) FormatMessage(\
177 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, \
178 NULL, error, MAKELANGID(LANG_NEUTRAL, (sublang)), \
179 message + len, size - len, NULL)
180 if (format_message(SUBLANG_ENGLISH_US) == 0)
181 format_message(SUBLANG_DEFAULT);
182 for (p = message + len; *p; p++) {
183 if (*p == '\n' || *p == '\r')
184 *p = ' ';
185 }
186 return message;
187}
188#define dln_strerror() dln_strerror(message, sizeof message)
189#elif defined USE_DLN_DLOPEN
190static const char *
191dln_strerror(void)
192{
193 return (char*)dlerror();
194}
195#endif
196
197#if defined(_AIX)
198static void
199aix_loaderror(const char *pathname)
200{
201 char *message[1024], errbuf[1024];
202 int i;
203#define ERRBUF_APPEND(s) strlcat(errbuf, (s), sizeof(errbuf))
204 snprintf(errbuf, sizeof(errbuf), "load failed - %s. ", pathname);
205
206 if (loadquery(L_GETMESSAGES, &message[0], sizeof(message)) != -1) {
207 ERRBUF_APPEND("Please issue below command for detailed reasons:\n\t");
208 ERRBUF_APPEND("/usr/sbin/execerror ruby ");
209 for (i=0; message[i]; i++) {
210 ERRBUF_APPEND("\"");
211 ERRBUF_APPEND(message[i]);
212 ERRBUF_APPEND("\" ");
213 }
214 ERRBUF_APPEND("\n");
215 }
216 else {
217 ERRBUF_APPEND(strerror(errno));
218 ERRBUF_APPEND("[loadquery failed]");
219 }
220 dln_loaderror("%s", errbuf);
221}
222#endif
223
224#if defined _WIN32 && defined RUBY_EXPORT
225HANDLE rb_libruby_handle(void);
226
227static int
228rb_w32_check_imported(HMODULE ext, HMODULE mine)
229{
230 ULONG size;
231 const IMAGE_IMPORT_DESCRIPTOR *desc;
232
233 desc = ImageDirectoryEntryToData(ext, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size);
234 if (!desc) return 0;
235 while (desc->Name) {
236 PIMAGE_THUNK_DATA pint = (PIMAGE_THUNK_DATA)((char *)ext + desc->Characteristics);
237 PIMAGE_THUNK_DATA piat = (PIMAGE_THUNK_DATA)((char *)ext + desc->FirstThunk);
238 for (; piat->u1.Function; piat++, pint++) {
239 static const char prefix[] = "rb_";
240 PIMAGE_IMPORT_BY_NAME pii;
241 const char *name;
242
243 if (IMAGE_SNAP_BY_ORDINAL(pint->u1.Ordinal)) continue;
244 pii = (PIMAGE_IMPORT_BY_NAME)((char *)ext + (size_t)pint->u1.AddressOfData);
245 name = (const char *)pii->Name;
246 if (strncmp(name, prefix, sizeof(prefix) - 1) == 0) {
247 FARPROC addr = GetProcAddress(mine, name);
248 if (addr) return (FARPROC)piat->u1.Function == addr;
249 }
250 }
251 desc++;
252 }
253 return 1;
254}
255#endif
256
257#if defined(DLN_NEEDS_ALT_SEPARATOR) && DLN_NEEDS_ALT_SEPARATOR
258#define translit_separator(src) do { \
259 char *tmp = ALLOCA_N(char, strlen(src) + 1), *p = tmp, c; \
260 do { \
261 *p++ = ((c = *file++) == '/') ? DLN_NEEDS_ALT_SEPARATOR : c; \
262 } while (c); \
263 (src) = tmp; \
264 } while (0)
265#else
266#define translit_separator(str) (void)(str)
267#endif
268
269#ifdef USE_DLN_DLOPEN
270# include "ruby/internal/stdbool.h"
271# include "internal/warnings.h"
272static bool
273dln_incompatible_func(void *handle, const char *funcname, void *const fp, const char **libname)
274{
275 Dl_info dli;
276 void *ex = dlsym(handle, funcname);
277 if (!ex) return false;
278 if (ex == fp) return false;
279 if (dladdr(ex, &dli)) {
280 *libname = dli.dli_fname;
281 }
282 return true;
283}
284
285COMPILER_WARNING_PUSH
286#if defined(__clang__) || GCC_VERSION_SINCE(4, 2, 0)
287COMPILER_WARNING_IGNORED(-Wpedantic)
288#endif
289static bool
290dln_incompatible_library_p(void *handle, const char **libname)
291{
292#define check_func(func) \
293 if (dln_incompatible_func(handle, EXTERNAL_PREFIX #func, (void *)&func, libname)) \
294 return true
295 check_func(ruby_xmalloc);
296 return false;
297}
298COMPILER_WARNING_POP
299#endif
300
301#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED)
302/* assume others than old Mac OS X have no problem */
303# define dln_disable_dlclose() false
304
305#elif !defined(MAC_OS_X_VERSION_10_11) || \
306 (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11)
307/* targeting older versions only */
308# define dln_disable_dlclose() true
309
310#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11
311/* targeting newer versions only */
312# define dln_disable_dlclose() false
313
314#else
315/* support both versions, and check at runtime */
316# include <sys/sysctl.h>
317
318static bool
319dln_disable_dlclose(void)
320{
321 int mib[] = {CTL_KERN, KERN_OSREV};
322 int32_t rev;
323 size_t size = sizeof(rev);
324 if (sysctl(mib, numberof(mib), &rev, &size, NULL, 0)) return true;
325 if (rev < MAC_OS_X_VERSION_10_11) return true;
326 return false;
327}
328#endif
329
330#if defined(_WIN32) || defined(USE_DLN_DLOPEN)
331static void *
332dln_open(const char *file)
333{
334 static const char incompatible[] = "incompatible library version";
335 const char *error = NULL;
336 void *handle;
337
338#if defined(_WIN32)
339 char message[1024];
340
341 /* Convert the file path to wide char */
342 WCHAR *winfile = rb_w32_mbstr_to_wstr(CP_UTF8, file, -1, NULL);
343 if (!winfile) {
344 dln_memerror();
345 }
346
347 /* Load file */
348 handle = LoadLibraryW(winfile);
349 free(winfile);
350
351 if (!handle) {
352 error = dln_strerror();
353 goto failed;
354 }
355
356# if defined(RUBY_EXPORT)
357 if (!rb_w32_check_imported(handle, rb_libruby_handle())) {
358 FreeLibrary(handle);
359 error = incompatible;
360 goto failed;
361 }
362# endif
363
364#elif defined(USE_DLN_DLOPEN)
365
366# ifndef RTLD_LAZY
367# define RTLD_LAZY 1
368# endif
369# ifdef __INTERIX
370# undef RTLD_GLOBAL
371# endif
372# ifndef RTLD_GLOBAL
373# define RTLD_GLOBAL 0
374# endif
375
376 /* Load file */
377 handle = dlopen(file, RTLD_LAZY|RTLD_GLOBAL);
378 if (handle == NULL) {
379 error = dln_strerror();
380 goto failed;
381 }
382
383# if defined(RUBY_EXPORT)
384 {
385 const char *libruby_name = NULL;
386 if (dln_incompatible_library_p(handle, &libruby_name)) {
387 if (dln_disable_dlclose()) {
388 /* dlclose() segfaults */
389 if (libruby_name) {
390 dln_fatalerror("linked to incompatible %s - %s", libruby_name, file);
391 }
392 dln_fatalerror("%s - %s", incompatible, file);
393 }
394 else {
395 dlclose(handle);
396 if (libruby_name) {
397 dln_loaderror("linked to incompatible %s - %s", libruby_name, file);
398 }
399 error = incompatible;
400 goto failed;
401 }
402 }
403 }
404# endif
405#endif
406
407 return handle;
408
409 failed:
410 dln_loaderror("%s - %s", error, file);
411}
412
413static void *
414dln_sym(void *handle, const char *symbol)
415{
416 void *func;
417 const char *error;
418
419#if defined(_WIN32)
420 char message[1024];
421
422 func = GetProcAddress(handle, symbol);
423 if (func == NULL) {
424 error = dln_strerror();
425 goto failed;
426 }
427
428#elif defined(USE_DLN_DLOPEN)
429 func = dlsym(handle, symbol);
430 if (func == NULL) {
431 const size_t errlen = strlen(error = dln_strerror()) + 1;
432 error = memcpy(ALLOCA_N(char, errlen), error, errlen);
433 goto failed;
434 }
435#endif
436
437 return func;
438
439 failed:
440 dln_loaderror("%s - %s", error, symbol);
441}
442#endif
443
444#if defined(RUBY_DLN_CHECK_ABI) && defined(USE_DLN_DLOPEN)
445static bool
446abi_check_enabled_p(void)
447{
448 const char *val = getenv("RUBY_ABI_CHECK");
449 return val == NULL || !(val[0] == '0' && val[1] == '\0');
450}
451#endif
452
453void *
454dln_load(const char *file)
455{
456#if defined(_WIN32) || defined(USE_DLN_DLOPEN)
457 void *handle = dln_open(file);
458
459#ifdef RUBY_DLN_CHECK_ABI
460 unsigned long long (*abi_version_fct)(void) = (unsigned long long(*)(void))dln_sym(handle, "ruby_abi_version");
461 unsigned long long binary_abi_version = (*abi_version_fct)();
462 if (binary_abi_version != ruby_abi_version() && abi_check_enabled_p()) {
463 dln_loaderror("incompatible ABI version of binary - %s", file);
464 }
465#endif
466
467 char *init_fct_name;
468 init_funcname(&init_fct_name, file);
469 void (*init_fct)(void) = (void(*)(void))dln_sym(handle, init_fct_name);
470
471 /* Call the init code */
472 (*init_fct)();
473
474 return handle;
475
476#elif defined(_AIX)
477 {
478 void (*init_fct)(void);
479
480 init_fct = (void(*)(void))load((char*)file, 1, 0);
481 if (init_fct == NULL) {
482 aix_loaderror(file);
483 }
484 if (loadbind(0, (void*)dln_load, (void*)init_fct) == -1) {
485 aix_loaderror(file);
486 }
487 (*init_fct)();
488 return (void*)init_fct;
489 }
490#else
491 dln_notimplement();
492#endif
493
494 return 0; /* dummy return */
495}
xrealloc
#define xrealloc
Old name of ruby_xrealloc.
Definition xmalloc.h:56
xmalloc
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
xcalloc
#define xcalloc
Old name of ruby_xcalloc.
Definition xmalloc.h:55
ALLOCA_N
#define ALLOCA_N(type, n)
Definition memory.h:286
ruby.h
stdbool.h
C99 shim for <stdbool.h>