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