diff options
Diffstat (limited to 'dln.c')
| -rw-r--r-- | dln.c | 187 |
1 files changed, 133 insertions, 54 deletions
@@ -25,6 +25,7 @@ static void dln_loaderror(const char *format, ...); #endif #include "dln.h" #include "internal.h" +#include "internal/box.h" #include "internal/compilers.h" #ifdef HAVE_STDLIB_H @@ -41,6 +42,10 @@ static void dln_loaderror(const char *format, ...); # include <strings.h> #endif +#if defined __APPLE__ +# include <AvailabilityMacros.h> +#endif + #ifndef xmalloc void *xmalloc(); void *xcalloc(); @@ -58,7 +63,7 @@ void *xrealloc(); #include <sys/stat.h> #ifndef S_ISDIR -# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +# define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif #ifdef HAVE_SYS_PARAM_H @@ -72,6 +77,10 @@ void *xrealloc(); # include <unistd.h> #endif +#ifndef UNREACHABLE_RETURN +# define UNREACHABLE_RETURN(x) return (x) +#endif + #ifndef dln_loaderror static void dln_loaderror(const char *format, ...) @@ -103,36 +112,45 @@ dln_loaderror(const char *format, ...) #endif #if defined(_WIN32) || defined(USE_DLN_DLOPEN) -static size_t -init_funcname_len(const char **file) +struct string_part { + const char *ptr; + size_t len; +}; + +static struct string_part +init_funcname_len(const char *file) { - const char *p = *file, *base, *dot = NULL; + const char *p = file, *base, *dot = NULL; /* Load the file as an object one */ for (base = p; *p; p++) { /* Find position of last '/' */ if (*p == '.' && !dot) dot = p; if (isdirsep(*p)) base = p+1, dot = NULL; } - *file = base; /* Delete suffix if it exists */ - return (dot ? dot : p) - base; + const size_t len = (dot ? dot : p) - base; + return (struct string_part){base, len}; +} + +static inline char * +concat_funcname(char *buf, const char *prefix, size_t plen, const struct string_part base) +{ + if (!buf) { + dln_memerror(); + } + memcpy(buf, prefix, plen); + memcpy(buf + plen, base.ptr, base.len); + buf[plen + base.len] = '\0'; + return buf; } -static const char funcname_prefix[sizeof(FUNCNAME_PREFIX) - 1] = FUNCNAME_PREFIX; - -#define init_funcname(buf, file) do {\ - const char *base = (file);\ - const size_t flen = init_funcname_len(&base);\ - const size_t plen = sizeof(funcname_prefix);\ - char *const tmp = ALLOCA_N(char, plen+flen+1);\ - if (!tmp) {\ - dln_memerror();\ - }\ - memcpy(tmp, funcname_prefix, plen);\ - memcpy(tmp+plen, base, flen);\ - tmp[plen+flen] = '\0';\ - *(buf) = tmp;\ +#define build_funcname(prefix, buf, file) do {\ + const struct string_part f = init_funcname_len(file);\ + const size_t plen = sizeof(prefix "") - 1;\ + *(buf) = concat_funcname(ALLOCA_N(char, plen+f.len+1), prefix, plen, f);\ } while (0) + +#define init_funcname(buf, file) build_funcname(FUNCNAME_PREFIX, buf, file) #endif #ifdef USE_DLN_DLOPEN @@ -268,13 +286,15 @@ rb_w32_check_imported(HMODULE ext, HMODULE mine) static bool dln_incompatible_func(void *handle, const char *funcname, void *const fp, const char **libname) { - Dl_info dli; void *ex = dlsym(handle, funcname); if (!ex) return false; if (ex == fp) return false; +# if defined(HAVE_DLADDR) && !defined(__CYGWIN__) + Dl_info dli; if (dladdr(ex, &dli)) { *libname = dli.dli_fname; } +# endif return true; } @@ -298,15 +318,15 @@ COMPILER_WARNING_POP /* assume others than old Mac OS X have no problem */ # define dln_disable_dlclose() false -#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 -/* targeting newer versions only */ -# define dln_disable_dlclose() false - #elif !defined(MAC_OS_X_VERSION_10_11) || \ (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11) /* targeting older versions only */ # define dln_disable_dlclose() true +#elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_11 +/* targeting newer versions only */ +# define dln_disable_dlclose() false + #else /* support both versions, and check at runtime */ # include <sys/sysctl.h> @@ -332,6 +352,7 @@ dln_open(const char *file) void *handle; #if defined(_WIN32) +# define DLN_DEFINED char message[1024]; /* Convert the file path to wide char */ @@ -358,6 +379,7 @@ dln_open(const char *file) # endif #elif defined(USE_DLN_DLOPEN) +# define DLN_DEFINED # ifndef RTLD_LAZY # define RTLD_LAZY 1 @@ -368,9 +390,13 @@ dln_open(const char *file) # ifndef RTLD_GLOBAL # define RTLD_GLOBAL 0 # endif +# ifndef RTLD_LOCAL +# define RTLD_LOCAL 0 /* TODO: 0??? some systems (including libc) use 0x00100 for RTLD_GLOBAL, 0x00000 for RTLD_LOCAL */ +# endif /* Load file */ - handle = dlopen(file, RTLD_LAZY|RTLD_GLOBAL); + int mode = rb_box_available() ? RTLD_LAZY|RTLD_LOCAL : RTLD_LAZY|RTLD_GLOBAL; + handle = dlopen(file, mode); if (handle == NULL) { error = dln_strerror(); goto failed; @@ -388,6 +414,12 @@ dln_open(const char *file) dln_fatalerror("%s - %s", incompatible, file); } else { + if (libruby_name) { + const size_t len = strlen(libruby_name); + char *const tmp = ALLOCA_N(char, len + 1); + if (tmp) memcpy(tmp, libruby_name, len + 1); + libruby_name = tmp; + } dlclose(handle); if (libruby_name) { dln_loaderror("linked to incompatible %s - %s", libruby_name, file); @@ -409,33 +441,63 @@ dln_open(const char *file) static void * dln_sym(void *handle, const char *symbol) { - void *func; - const char *error; - #if defined(_WIN32) - char message[1024]; + return GetProcAddress(handle, symbol); +#elif defined(USE_DLN_DLOPEN) + return dlsym(handle, symbol); +#endif +} + +static uintptr_t +dln_sym_func(void *handle, const char *symbol) +{ + void *func = dln_sym(handle, symbol); - func = GetProcAddress(handle, symbol); if (func == NULL) { + const char *error; +#if defined(_WIN32) + char message[1024]; error = dln_strerror(); - goto failed; - } - #elif defined(USE_DLN_DLOPEN) - func = dlsym(handle, symbol); - if (func == NULL) { const size_t errlen = strlen(error = dln_strerror()) + 1; error = memcpy(ALLOCA_N(char, errlen), error, errlen); - goto failed; - } #endif + dln_loaderror("%s - %s", error, symbol); + } + return (uintptr_t)func; +} - return func; +#define dln_sym_callable(rettype, argtype, handle, symbol) \ + (*(rettype (*)argtype)dln_sym_func(handle, symbol)) +#endif - failed: - dln_loaderror("%s - %s", error, symbol); -} +void * +dln_symbol(void *handle, const char *symbol) +{ +#if defined(_WIN32) || defined(USE_DLN_DLOPEN) + if (EXTERNAL_PREFIX[0]) { + const size_t symlen = strlen(symbol); + char *const tmp = ALLOCA_N(char, symlen + sizeof(EXTERNAL_PREFIX)); + if (!tmp) dln_memerror(); + memcpy(tmp, EXTERNAL_PREFIX, sizeof(EXTERNAL_PREFIX) - 1); + memcpy(tmp + sizeof(EXTERNAL_PREFIX) - 1, symbol, symlen + 1); + symbol = tmp; + } + if (handle == NULL) { +# if defined(USE_DLN_DLOPEN) + handle = dlopen(NULL, RTLD_LAZY | RTLD_GLOBAL); +# elif defined(_WIN32) + handle = rb_libruby_handle(); +# else + return NULL; +# endif + } + return dln_sym(handle, symbol); +#else + return NULL; #endif +} + #if defined(RUBY_DLN_CHECK_ABI) && defined(USE_DLN_DLOPEN) static bool @@ -446,33 +508,32 @@ abi_check_enabled_p(void) } #endif -void * -dln_load(const char *file) +static void * +dln_load_and_init(const char *file, const char *init_fct_name) { -#if defined(_WIN32) || defined(USE_DLN_DLOPEN) +#if defined(DLN_DEFINED) void *handle = dln_open(file); #ifdef RUBY_DLN_CHECK_ABI - unsigned long long (*abi_version_fct)(void) = (unsigned long long(*)(void))dln_sym(handle, "ruby_abi_version"); - unsigned long long binary_abi_version = (*abi_version_fct)(); - if (binary_abi_version != ruby_abi_version() && abi_check_enabled_p()) { + typedef unsigned long long abi_version_number; + abi_version_number binary_abi_version = + dln_sym_callable(abi_version_number, (void), handle, EXTERNAL_PREFIX "ruby_abi_version")(); + if (binary_abi_version != RUBY_ABI_VERSION && abi_check_enabled_p()) { dln_loaderror("incompatible ABI version of binary - %s", file); } #endif - char *init_fct_name; - init_funcname(&init_fct_name, file); - void (*init_fct)(void) = (void(*)(void))dln_sym(handle, init_fct_name); - /* Call the init code */ - (*init_fct)(); + dln_sym_callable(void, (void), handle, init_fct_name)(); return handle; #elif defined(_AIX) +# define DLN_DEFINED { void (*init_fct)(void); + /* TODO: check - AIX's load system call will return the first/last symbol/function? */ init_fct = (void(*)(void))load((char*)file, 1, 0); if (init_fct == NULL) { aix_loaderror(file); @@ -485,7 +546,25 @@ dln_load(const char *file) } #else dln_notimplement(); + UNREACHABLE_RETURN(0); #endif +} - return 0; /* dummy return */ +void * +dln_load(const char *file) +{ + return dln_load_feature(file, file); +} + +void * +dln_load_feature(const char *file, const char *fname) +{ +#if defined(DLN_DEFINED) + char *init_fct_name; + init_funcname(&init_fct_name, fname); + return dln_load_and_init(file, init_fct_name); +#else + dln_notimplement(); + UNREACHABLE_RETURN(0); +#endif } |
