diff options
Diffstat (limited to 'dln.c')
-rw-r--r-- | dln.c | 338 |
1 files changed, 201 insertions, 137 deletions
@@ -41,6 +41,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 +62,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 @@ -103,36 +107,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; + 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 @@ -170,18 +183,17 @@ dln_strerror(char *message, size_t size) size_t len = snprintf(message, size, "%d: ", error); #define format_message(sublang) FormatMessage(\ - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, \ - NULL, error, MAKELANGID(LANG_NEUTRAL, (sublang)), \ - message + len, size - len, NULL) + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, \ + NULL, error, MAKELANGID(LANG_NEUTRAL, (sublang)), \ + message + len, size - len, NULL) if (format_message(SUBLANG_ENGLISH_US) == 0) - format_message(SUBLANG_DEFAULT); + format_message(SUBLANG_DEFAULT); for (p = message + len; *p; p++) { - if (*p == '\n' || *p == '\r') - *p = ' '; + if (*p == '\n' || *p == '\r') + *p = ' '; } return message; } -#define dln_strerror() dln_strerror(message, sizeof message) #elif defined USE_DLN_DLOPEN static const char * dln_strerror(void) @@ -200,18 +212,18 @@ aix_loaderror(const char *pathname) snprintf(errbuf, sizeof(errbuf), "load failed - %s. ", pathname); if (loadquery(L_GETMESSAGES, &message[0], sizeof(message)) != -1) { - ERRBUF_APPEND("Please issue below command for detailed reasons:\n\t"); - ERRBUF_APPEND("/usr/sbin/execerror ruby "); - for (i=0; message[i]; i++) { - ERRBUF_APPEND("\""); - ERRBUF_APPEND(message[i]); - ERRBUF_APPEND("\" "); - } - ERRBUF_APPEND("\n"); + ERRBUF_APPEND("Please issue below command for detailed reasons:\n\t"); + ERRBUF_APPEND("/usr/sbin/execerror ruby "); + for (i=0; message[i]; i++) { + ERRBUF_APPEND("\""); + ERRBUF_APPEND(message[i]); + ERRBUF_APPEND("\" "); + } + ERRBUF_APPEND("\n"); } else { - ERRBUF_APPEND(strerror(errno)); - ERRBUF_APPEND("[loadquery failed]"); + ERRBUF_APPEND(strerror(errno)); + ERRBUF_APPEND("[loadquery failed]"); } dln_loaderror("%s", errbuf); } @@ -229,22 +241,22 @@ rb_w32_check_imported(HMODULE ext, HMODULE mine) desc = ImageDirectoryEntryToData(ext, TRUE, IMAGE_DIRECTORY_ENTRY_IMPORT, &size); if (!desc) return 0; while (desc->Name) { - PIMAGE_THUNK_DATA pint = (PIMAGE_THUNK_DATA)((char *)ext + desc->Characteristics); - PIMAGE_THUNK_DATA piat = (PIMAGE_THUNK_DATA)((char *)ext + desc->FirstThunk); - for (; piat->u1.Function; piat++, pint++) { - static const char prefix[] = "rb_"; - PIMAGE_IMPORT_BY_NAME pii; - const char *name; - - if (IMAGE_SNAP_BY_ORDINAL(pint->u1.Ordinal)) continue; - pii = (PIMAGE_IMPORT_BY_NAME)((char *)ext + (size_t)pint->u1.AddressOfData); - name = (const char *)pii->Name; - if (strncmp(name, prefix, sizeof(prefix) - 1) == 0) { - FARPROC addr = GetProcAddress(mine, name); - if (addr) return (FARPROC)piat->u1.Function == addr; - } - } - desc++; + PIMAGE_THUNK_DATA pint = (PIMAGE_THUNK_DATA)((char *)ext + desc->Characteristics); + PIMAGE_THUNK_DATA piat = (PIMAGE_THUNK_DATA)((char *)ext + desc->FirstThunk); + for (; piat->u1.Function; piat++, pint++) { + static const char prefix[] = "rb_"; + PIMAGE_IMPORT_BY_NAME pii; + const char *name; + + if (IMAGE_SNAP_BY_ORDINAL(pint->u1.Ordinal)) continue; + pii = (PIMAGE_IMPORT_BY_NAME)((char *)ext + (size_t)pint->u1.AddressOfData); + name = (const char *)pii->Name; + if (strncmp(name, prefix, sizeof(prefix) - 1) == 0) { + FARPROC addr = GetProcAddress(mine, name); + if (addr) return (FARPROC)piat->u1.Function == addr; + } + } + desc++; } return 1; } @@ -252,11 +264,11 @@ rb_w32_check_imported(HMODULE ext, HMODULE mine) #if defined(DLN_NEEDS_ALT_SEPARATOR) && DLN_NEEDS_ALT_SEPARATOR #define translit_separator(src) do { \ - char *tmp = ALLOCA_N(char, strlen(src) + 1), *p = tmp, c; \ - do { \ - *p++ = ((c = *file++) == '/') ? DLN_NEEDS_ALT_SEPARATOR : c; \ - } while (c); \ - (src) = tmp; \ + char *tmp = ALLOCA_N(char, strlen(src) + 1), *p = tmp, c; \ + do { \ + *p++ = ((c = *file++) == '/') ? DLN_NEEDS_ALT_SEPARATOR : c; \ + } while (c); \ + (src) = tmp; \ } while (0) #else #define translit_separator(str) (void)(str) @@ -268,13 +280,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) + Dl_info dli; if (dladdr(ex, &dli)) { - *libname = dli.dli_fname; + *libname = dli.dli_fname; } +# endif return true; } @@ -287,15 +301,28 @@ dln_incompatible_library_p(void *handle, const char **libname) { #define check_func(func) \ if (dln_incompatible_func(handle, EXTERNAL_PREFIX #func, (void *)&func, libname)) \ - return true + return true check_func(ruby_xmalloc); return false; } COMPILER_WARNING_POP #endif -#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ - (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11) +#if !defined(MAC_OS_X_VERSION_MIN_REQUIRED) +/* assume others than old Mac OS X have no problem */ +# 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> static bool @@ -308,25 +335,20 @@ dln_disable_dlclose(void) if (rev < MAC_OS_X_VERSION_10_11) return true; return false; } -#else -# define dln_disable_dlclose() false #endif #if defined(_WIN32) || defined(USE_DLN_DLOPEN) -static void * -dln_open(const char *file) +void * +dln_open(const char *file, char *error, size_t size) { static const char incompatible[] = "incompatible library version"; - const char *error = NULL; void *handle; #if defined(_WIN32) - char message[1024]; - /* Convert the file path to wide char */ WCHAR *winfile = rb_w32_mbstr_to_wstr(CP_UTF8, file, -1, NULL); if (!winfile) { - dln_memerror(); + dln_memerror(); } /* Load file */ @@ -334,15 +356,15 @@ dln_open(const char *file) free(winfile); if (!handle) { - error = dln_strerror(); - goto failed; + strlcpy(error, dln_strerror(error, size), size); + return NULL; } # if defined(RUBY_EXPORT) if (!rb_w32_check_imported(handle, rb_libruby_handle())) { - FreeLibrary(handle); - error = incompatible; - goto failed; + FreeLibrary(handle); + strlcpy(error, incompatible, size); + return NULL; } # endif @@ -361,71 +383,108 @@ dln_open(const char *file) /* Load file */ handle = dlopen(file, RTLD_LAZY|RTLD_GLOBAL); if (handle == NULL) { - error = dln_strerror(); - goto failed; + strlcpy(error, dln_strerror(), size); + return NULL; } # if defined(RUBY_EXPORT) { - const char *libruby_name = NULL; - if (dln_incompatible_library_p(handle, &libruby_name)) { - if (dln_disable_dlclose()) { - /* dlclose() segfaults */ - if (libruby_name) { - dln_fatalerror("linked to incompatible %s - %s", libruby_name, file); - } - dln_fatalerror("%s - %s", incompatible, file); - } - else { - dlclose(handle); - if (libruby_name) { - dln_loaderror("linked to incompatible %s - %s", libruby_name, file); - } - error = incompatible; - goto failed; - } - } + const char *libruby_name = NULL; + if (dln_incompatible_library_p(handle, &libruby_name)) { + if (dln_disable_dlclose()) { + /* dlclose() segfaults */ + if (libruby_name) { + dln_fatalerror("linked to incompatible %s - %s", libruby_name, 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) { + snprintf(error, size, "linked to incompatible %s - %s", libruby_name, file); + } + else { + strlcpy(error, incompatible, size); + } + + return NULL; + } + } } # endif #endif return handle; - - failed: - dln_loaderror("%s - %s", error, file); } -static void * +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 +} - func = GetProcAddress(handle, symbol); - if (func == NULL) { - error = dln_strerror(); - goto failed; - } +static void * +dln_sym_func(void *handle, const char *symbol) +{ + void *func = dln_sym(handle, symbol); -#elif defined(USE_DLN_DLOPEN) - func = dlsym(handle, symbol); if (func == NULL) { + const char *error; +#if defined(_WIN32) + char message[1024]; + error = dln_strerror(message, sizeof(message)); +#elif defined(USE_DLN_DLOPEN) 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 func; - - failed: - dln_loaderror("%s - %s", error, symbol); } + +#define dln_sym_callable(rettype, argtype, handle, symbol) \ + (*(rettype (*)argtype)dln_sym_func(handle, symbol)) #endif +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 abi_check_enabled_p(void) @@ -439,38 +498,43 @@ void * dln_load(const char *file) { #if defined(_WIN32) || defined(USE_DLN_DLOPEN) - void *handle = dln_open(file); + char error[1024]; + void *handle = dln_open(file, error, sizeof(error)); + + if (handle == NULL) { + dln_loaderror("%s - %s", error, 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()) { - dln_loaderror("ABI version of binary is incompatible with this Ruby. Try rebuilding this binary."); + 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) { - void (*init_fct)(void); - - init_fct = (void(*)(void))load((char*)file, 1, 0); - if (init_fct == NULL) { - aix_loaderror(file); - } - if (loadbind(0, (void*)dln_load, (void*)init_fct) == -1) { - aix_loaderror(file); - } - (*init_fct)(); - return (void*)init_fct; + void (*init_fct)(void); + + init_fct = (void(*)(void))load((char*)file, 1, 0); + if (init_fct == NULL) { + aix_loaderror(file); + } + if (loadbind(0, (void*)dln_load, (void*)init_fct) == -1) { + aix_loaderror(file); + } + (*init_fct)(); + return (void*)init_fct; } #else dln_notimplement(); |