// Turn on libc extensions so that we can use dladdr() on Unix-like systems #if defined(__has_include) && \ !(defined(_GNU_SOURCE) || defined(_DARWIN_C_SOURCE)) #if __has_include() // We're using a glibc-compatible library #define _GNU_SOURCE #elif __has_include() // We're on Apple/Darwin #define _DARWIN_C_SOURCE #endif #else // No __has_include #if __GNUC__ < 5 // Best guess on older GCC is that we are using glibc #define _GNU_SOURCE #endif #endif #include "../mongocrypt-dll-private.h" #ifndef _WIN32 #include #include #include #include mcr_dll mcr_dll_open (const char *filepath) { void *handle = dlopen (filepath, RTLD_LAZY | RTLD_LOCAL); if (handle == NULL) { // Failed to open. Return NULL and copy the error message return (mcr_dll){ ._native_handle = NULL, .error_string = mstr_copy_cstr (dlerror ()), }; } else { // Okay return (mcr_dll){ ._native_handle = handle, .error_string = MSTR_NULL, }; } } void mcr_dll_close_handle (mcr_dll dll) { if (dll._native_handle) { dlclose (dll._native_handle); } } void * mcr_dll_sym (mcr_dll dll, const char *sym) { return dlsym (dll._native_handle, sym); } #endif #ifdef __APPLE__ #include #include mcr_dll_path_result mcr_dll_path (mcr_dll dll) { // Clear the three low bits of the module handle: uintptr_t needle = ((uintptr_t) dll._native_handle & ~UINT64_C (0x3)); // Iterate each loaded dyld image /// NOTE: Not thread safe. Is there a thread-safe way to do this? for (uint32_t idx = 0; idx < _dyld_image_count (); ++idx) { // Get the filepath: /// NOTE: Between here and `dlopen`, `dyld_name` could be invalidated by /// a concurrent call to `dlclose()`. Is there a better way? const char *dyld_name = _dyld_get_image_name (idx); // Try and open it. This will return an equivalent pointer to the original // handle to the loaded image since they are deduplicated and reference // counted. void *try_handle = dlopen (dyld_name, RTLD_LAZY); if (!dyld_name) { // Ouch: `idx` was invalidated before we called _dyld_get_image_name. // This will have caused `dlopen()` to return the default handle: assert (try_handle == RTLD_DEFAULT); continue; } // Copy the string before closing, to shrink the chance of `dyld_name` // being used-after-freed. mstr ret_name = mstr_copy_cstr (dyld_name); // Mask off the mode bits: uintptr_t cur = (uintptr_t) try_handle & ~UINT64_C (0x3); // Close our reference to the image. We only care about the handle value. dlclose (try_handle); if (needle == cur) { // We've found the handle return (mcr_dll_path_result){.path = ret_name}; } // Not this name. mstr_free (ret_name); } return (mcr_dll_path_result){ .error_string = mstr_copy_cstr ("Handle not found in loaded modules")}; } #elif defined(__linux__) || defined(__FreeBSD__) #include mcr_dll_path_result mcr_dll_path (mcr_dll dll) { struct link_map *map; int rc = dlinfo (dll._native_handle, RTLD_DI_LINKMAP, &map); if (rc == 0) { return (mcr_dll_path_result){.path = mstr_copy_cstr (map->l_name)}; } else { return (mcr_dll_path_result){.error_string = mstr_copy_cstr (dlerror ())}; } } #elif defined(_WIN32) // Handled in os_win/os_dll.c #else #error "Don't know how to do mcr_dll_path() on this platform" #endif