LCOV - code coverage report
Current view: top level - src - dlload.c (source / functions) Hit Total Coverage
Test: [build process] commit ef510b1f346f4c9f9d86eaceace5ca54961a1dbc Lines: 60 92 65.2 %
Date: 2022-07-17 01:01:28 Functions: 5 5 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 46 76 60.5 %

           Branch data     Line data    Source code
       1                 :            : // This file is a part of Julia. License is MIT: https://julialang.org/license
       2                 :            : 
       3                 :            : #include <stdio.h>
       4                 :            : #include <stdlib.h>
       5                 :            : #include <string.h>
       6                 :            : #include <sys/stat.h>
       7                 :            : 
       8                 :            : #include "platform.h"
       9                 :            : #include "julia.h"
      10                 :            : #include "julia_internal.h"
      11                 :            : #ifdef _OS_WINDOWS_
      12                 :            : #include <direct.h>
      13                 :            : #else
      14                 :            : #include <unistd.h>
      15                 :            : #include <dlfcn.h>
      16                 :            : #endif
      17                 :            : #include "julia_assert.h"
      18                 :            : 
      19                 :            : #ifdef __cplusplus
      20                 :            : extern "C" {
      21                 :            : #endif
      22                 :            : 
      23                 :            : #if defined(__APPLE__)
      24                 :            : static char const *const extensions[] = { "", ".dylib" };
      25                 :            : #elif defined(_OS_WINDOWS_)
      26                 :            : static char const *const extensions[] = { "", ".dll" };
      27                 :            : extern volatile int needsSymRefreshModuleList;
      28                 :            : #else
      29                 :            : static char const *const extensions[] = { "", ".so" };
      30                 :            : #endif
      31                 :            : #define N_EXTENSIONS (sizeof(extensions) / sizeof(char*))
      32                 :            : 
      33                 :        234 : static int endswith_extension(const char *path) JL_NOTSAFEPOINT
      34                 :            : {
      35         [ +  + ]:        234 :     if (!path)
      36                 :         15 :         return 0;
      37                 :        219 :     size_t len = strlen(path);
      38                 :            :     // Skip the first one since it is empty
      39         [ +  + ]:        267 :     for (size_t i = 1; i < N_EXTENSIONS; i++) {
      40                 :        219 :         const char *ext = extensions[i];
      41                 :        219 :         size_t extlen = strlen(ext);
      42         [ -  + ]:        219 :         if (len < extlen)
      43                 :          0 :             return 0;
      44                 :            :         // Skip version extensions if present
      45                 :        219 :         size_t j = len - 1;
      46         [ +  - ]:        483 :         while (j > 0) {
      47   [ +  +  +  +  :        483 :             if (path[j] == '.' || (path[j] >= '0' && path[j] <= '9'))
                   +  + ]
      48                 :        264 :                 j--;
      49                 :            :             else
      50                 :            :                 break;
      51                 :            :         }
      52   [ +  +  +  +  :        219 :         if ((j == len-1 || path[j+1] == '.') && memcmp(ext, path + j - extlen + 1, extlen) == 0) {
                   +  + ]
      53                 :        171 :             return 1;
      54                 :            :         }
      55                 :            :     }
      56                 :         48 :     return 0;
      57                 :            : }
      58                 :            : 
      59                 :            : #ifdef _OS_WINDOWS_
      60                 :            : #define CRTDLL_BASENAME "msvcrt"
      61                 :            : 
      62                 :            : JL_DLLEXPORT const char *jl_crtdll_basename = CRTDLL_BASENAME;
      63                 :            : const char *jl_crtdll_name = CRTDLL_BASENAME ".dll";
      64                 :            : 
      65                 :            : #undef CRTDLL_BASENAME
      66                 :            : #endif
      67                 :            : 
      68                 :            : #define PATHBUF 4096
      69                 :            : 
      70                 :            : #define JL_RTLD(flags, FLAG) (flags & JL_RTLD_ ## FLAG ? RTLD_ ## FLAG : 0)
      71                 :            : 
      72                 :            : #ifdef _OS_WINDOWS_
      73                 :            : static void win32_formatmessage(DWORD code, char *reason, int len) JL_NOTSAFEPOINT
      74                 :            : {
      75                 :            :     DWORD res;
      76                 :            :     LPWSTR errmsg;
      77                 :            :     res = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
      78                 :            :                          FORMAT_MESSAGE_FROM_SYSTEM |
      79                 :            :                          FORMAT_MESSAGE_IGNORE_INSERTS |
      80                 :            :                          FORMAT_MESSAGE_MAX_WIDTH_MASK,
      81                 :            :                          NULL, code,
      82                 :            :                          MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
      83                 :            :                          (LPWSTR)&errmsg, 0, NULL);
      84                 :            :     if (!res && (GetLastError() == ERROR_MUI_FILE_NOT_FOUND ||
      85                 :            :                  GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND)) {
      86                 :            :       res = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
      87                 :            :                            FORMAT_MESSAGE_FROM_SYSTEM |
      88                 :            :                            FORMAT_MESSAGE_IGNORE_INSERTS |
      89                 :            :                            FORMAT_MESSAGE_MAX_WIDTH_MASK,
      90                 :            :                            NULL, code,
      91                 :            :                            0, (LPWSTR)&errmsg, 0, NULL);
      92                 :            :     }
      93                 :            :     res = WideCharToMultiByte(CP_UTF8, 0, errmsg, -1, reason, len, NULL, NULL);
      94                 :            :     assert(res > 0 || GetLastError() == ERROR_INSUFFICIENT_BUFFER);
      95                 :            :     reason[len - 1] = '\0';
      96                 :            :     LocalFree(errmsg);
      97                 :            : }
      98                 :            : #endif
      99                 :            : 
     100                 :        282 : JL_DLLEXPORT void *jl_dlopen(const char *filename, unsigned flags) JL_NOTSAFEPOINT
     101                 :            : {
     102                 :            : #if defined(_OS_WINDOWS_)
     103                 :            :     size_t len = MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
     104                 :            :     if (!len) return NULL;
     105                 :            :     WCHAR *wfilename = (WCHAR*)alloca(len * sizeof(WCHAR));
     106                 :            :     if (!MultiByteToWideChar(CP_UTF8, 0, filename, -1, wfilename, len)) return NULL;
     107                 :            :     HANDLE lib = LoadLibraryExW(wfilename, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
     108                 :            :     if (lib)
     109                 :            :         needsSymRefreshModuleList = 1;
     110                 :            :     return lib;
     111                 :            : #else
     112                 :        564 :     return dlopen(filename,
     113                 :        282 :                   (flags & JL_RTLD_NOW ? RTLD_NOW : RTLD_LAZY)
     114         [ +  + ]:        282 :                   | JL_RTLD(flags, LOCAL)
     115                 :        282 :                   | JL_RTLD(flags, GLOBAL)
     116                 :            : #ifdef RTLD_NODELETE
     117                 :        282 :                   | JL_RTLD(flags, NODELETE)
     118                 :            : #endif
     119                 :            : #ifdef RTLD_NOLOAD
     120                 :        282 :                   | JL_RTLD(flags, NOLOAD)
     121                 :            : #endif
     122                 :            : #if defined(RTLD_DEEPBIND) && !(defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_TSAN_ENABLED_) || defined(_COMPILER_MSAN_ENABLED_))
     123                 :        282 :                   | JL_RTLD(flags, DEEPBIND)
     124                 :            : #endif
     125                 :            : #ifdef RTLD_FIRST
     126                 :            :                   | JL_RTLD(flags, FIRST)
     127                 :            : #endif
     128                 :            :                   );
     129                 :            : #endif
     130                 :            : }
     131                 :            : 
     132                 :         52 : JL_DLLEXPORT int jl_dlclose(void *handle) JL_NOTSAFEPOINT
     133                 :            : {
     134                 :            : #ifdef _OS_WINDOWS_
     135                 :            :     if (!handle) {
     136                 :            :         return -1;
     137                 :            :     }
     138                 :            :     return !FreeLibrary((HMODULE) handle);
     139                 :            : #else
     140         [ -  + ]:         52 :     if (!handle) {
     141                 :          0 :         dlerror(); /* Reset error status. */
     142                 :          0 :         return -1;
     143                 :            :     }
     144                 :         52 :     return dlclose(handle);
     145                 :            : #endif
     146                 :            : }
     147                 :            : 
     148                 :        234 : JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, int throw_err)
     149                 :            : {
     150                 :            :     char path[PATHBUF], relocated[PATHBUF];
     151                 :            :     int i;
     152                 :            : #ifdef _OS_WINDOWS_
     153                 :            :     int err;
     154                 :            : #endif
     155                 :            :     uv_stat_t stbuf;
     156                 :            :     void *handle;
     157                 :            :     int abspath;
     158                 :            :     int is_atpath;
     159                 :            :     // number of extensions to try — if modname already ends with the
     160                 :            :     // standard extension, then we don't try adding additional extensions
     161         [ +  + ]:        234 :     int n_extensions = endswith_extension(modname) ? 1 : N_EXTENSIONS;
     162                 :            :     int ret;
     163                 :            : 
     164                 :            :     /*
     165                 :            :       this branch returns handle of libjulia-internal
     166                 :            :     */
     167         [ +  + ]:        234 :     if (modname == NULL) {
     168                 :            : #ifdef _OS_WINDOWS_
     169                 :            :         if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
     170                 :            :                                 (LPCWSTR)(uintptr_t)(&jl_load_dynamic_library),
     171                 :            :                                 (HMODULE*)&handle)) {
     172                 :            :             jl_error("could not load base module");
     173                 :            :         }
     174                 :            : #else
     175                 :            :         Dl_info info;
     176   [ +  -  -  + ]:         15 :         if (!dladdr((void*)(uintptr_t)&jl_load_dynamic_library, &info) || !info.dli_fname) {
     177                 :          0 :             jl_error("could not load base module");
     178                 :            :         }
     179                 :         15 :         handle = dlopen(info.dli_fname, RTLD_NOW);
     180                 :            : #endif
     181                 :         15 :         goto done;
     182                 :            :     }
     183                 :            : 
     184                 :        219 :     abspath = jl_isabspath(modname);
     185                 :        219 :     is_atpath = 0;
     186                 :            : 
     187                 :            :     // Detect if our `modname` is something like `@rpath/libfoo.dylib`
     188                 :            : #ifdef _OS_DARWIN_
     189                 :            :     size_t nameLen = strlen(modname);
     190                 :            :     const char *const atPaths[] = {"@executable_path/", "@loader_path/", "@rpath/"};
     191                 :            :     for (i = 0; i < sizeof(atPaths)/sizeof(char*); ++i) {
     192                 :            :         size_t atLen = strlen(atPaths[i]);
     193                 :            :         if (nameLen >= atLen && 0 == strncmp(modname, atPaths[i], atLen)) {
     194                 :            :             is_atpath = 1;
     195                 :            :         }
     196                 :            :     }
     197                 :            : #endif
     198                 :            : 
     199                 :            :     /*
     200                 :            :       this branch permutes all base paths in DL_LOAD_PATH with all extensions
     201                 :            :       note: skip when !jl_base_module to avoid UndefVarError(:DL_LOAD_PATH),
     202                 :            :             and also skip for absolute paths
     203                 :            :             and also skip for `@`-paths on macOS
     204                 :            :       We also do simple string replacement here for elements starting with `@executable_path/`.
     205                 :            :       While these exist as OS concepts on Darwin, we want to use them on other platforms
     206                 :            :       such as Windows, so we emulate them here.
     207                 :            :     */
     208   [ +  +  +  -  :        219 :     if (!abspath && !is_atpath && jl_base_module != NULL) {
                   +  + ]
     209                 :        162 :         jl_binding_t *b = jl_get_module_binding(jl_base_module, jl_symbol("DL_LOAD_PATH"));
     210         [ +  - ]:        162 :         jl_array_t *DL_LOAD_PATH = (jl_array_t*)(b ? jl_atomic_load_relaxed(&b->value) : NULL);
     211         [ +  - ]:        162 :         if (DL_LOAD_PATH != NULL) {
     212                 :            :             size_t j;
     213         [ -  + ]:        162 :             for (j = 0; j < jl_array_len(DL_LOAD_PATH); j++) {
     214                 :          0 :                 char *dl_path = jl_string_data(jl_array_ptr_data(DL_LOAD_PATH)[j]);
     215                 :          0 :                 size_t len = strlen(dl_path);
     216         [ #  # ]:          0 :                 if (len == 0)
     217                 :          0 :                     continue;
     218                 :            : 
     219                 :            :                 // Is this entry supposed to be relative to the bindir?
     220   [ #  #  #  # ]:          0 :                 if (len >= 16 && strncmp(dl_path, "@executable_path", 16) == 0) {
     221                 :          0 :                     snprintf(relocated, PATHBUF, "%s%s", jl_options.julia_bindir, dl_path + 16);
     222                 :          0 :                     len = len - 16 + strlen(jl_options.julia_bindir);
     223                 :            :                 } else {
     224                 :          0 :                     strncpy(relocated, dl_path, PATHBUF);
     225                 :          0 :                     relocated[PATHBUF-1] = '\0';
     226                 :            :                 }
     227         [ #  # ]:          0 :                 for (i = 0; i < n_extensions; i++) {
     228                 :          0 :                     const char *ext = extensions[i];
     229                 :          0 :                     path[0] = '\0';
     230         [ #  # ]:          0 :                     if (relocated[len-1] == PATHSEPSTRING[0])
     231                 :          0 :                         snprintf(path, PATHBUF, "%s%s%s", relocated, modname, ext);
     232                 :            :                     else {
     233                 :          0 :                         ret = snprintf(path, PATHBUF, "%s" PATHSEPSTRING "%s%s", relocated, modname, ext);
     234         [ #  # ]:          0 :                         if (ret < 0)
     235                 :          0 :                             jl_errorf("path is longer than %d\n", PATHBUF);
     236                 :            :                     }
     237                 :            : 
     238                 :            : #ifdef _OS_WINDOWS_
     239                 :            :                     if (i == 0) { // LoadLibrary already tested the extensions, we just need to check the `stat` result
     240                 :            : #endif
     241                 :          0 :                         handle = jl_dlopen(path, flags);
     242         [ #  # ]:          0 :                         if (handle)
     243                 :          0 :                             goto done;
     244                 :            : #ifdef _OS_WINDOWS_
     245                 :            :                         err = GetLastError();
     246                 :            :                     }
     247                 :            : #endif
     248                 :            :                     // bail out and show the error if file actually exists
     249         [ #  # ]:          0 :                     if (jl_stat(path, (char*)&stbuf) == 0)
     250                 :          0 :                         goto notfound;
     251                 :            :                 }
     252                 :            :             }
     253                 :            :         }
     254                 :            :     }
     255                 :            : 
     256                 :            :     // now fall back and look in default library paths, for all extensions
     257         [ +  - ]:        267 :     for (i = 0; i < n_extensions; i++) {
     258                 :        267 :         const char *ext = extensions[i];
     259                 :        267 :         path[0] = '\0';
     260                 :        267 :         snprintf(path, PATHBUF, "%s%s", modname, ext);
     261                 :        267 :         handle = jl_dlopen(path, flags);
     262         [ +  + ]:        267 :         if (handle)
     263                 :        219 :             goto done;
     264                 :            : #ifdef _OS_WINDOWS_
     265                 :            :         err = GetLastError();
     266                 :            :         break; // LoadLibrary already tested the rest
     267                 :            : #endif
     268                 :            :     }
     269                 :            : 
     270                 :          0 : notfound:
     271         [ #  # ]:          0 :     if (throw_err) {
     272                 :            : #ifdef _OS_WINDOWS_
     273                 :            :         char reason[256];
     274                 :            :         win32_formatmessage(err, reason, sizeof(reason));
     275                 :            : #else
     276                 :          0 :         const char *reason = dlerror();
     277                 :            : #endif
     278                 :          0 :         jl_errorf("could not load library \"%s\"\n%s", modname, reason);
     279                 :            :     }
     280                 :          0 :     handle = NULL;
     281                 :            : 
     282                 :        234 : done:
     283                 :        234 :     return handle;
     284                 :            : }
     285                 :            : 
     286                 :     203847 : JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int throw_err) JL_NOTSAFEPOINT
     287                 :            : {
     288                 :     203847 :     int symbol_found = 0;
     289                 :            : 
     290                 :            :     /* First, get the symbol value */
     291                 :            : #ifdef _OS_WINDOWS_
     292                 :            :     *value = GetProcAddress((HMODULE) handle, symbol);
     293                 :            : #else
     294                 :     203847 :     *value = dlsym(handle, symbol);
     295                 :            : #endif
     296                 :            : 
     297                 :            :     /* Next, check for errors. On Windows, a NULL pointer means the symbol was
     298                 :            :      * not found. On everything else, we can have NULL symbols, so we check for
     299                 :            :      * non-NULL returns from dlerror(). Since POSIX doesn't require `dlerror`
     300                 :            :      * to be implemented safely, FreeBSD doesn't (unlike everyone else, who
     301                 :            :      * realized decades ago that threads are here to stay), so we avoid calling
     302                 :            :      * `dlerror` unless we need to get the error message.
     303                 :            :      * https://github.com/freebsd/freebsd-src/blob/12db51d20823a5e3b9e5f8a2ea73156fe1cbfc28/libexec/rtld-elf/rtld.c#L198
     304                 :            :      */
     305                 :     203847 :     symbol_found = *value != NULL;
     306                 :            : #ifndef _OS_WINDOWS_
     307                 :     203847 :     const char *err = "";
     308         [ +  + ]:     203847 :     if (!symbol_found) {
     309                 :      32662 :         dlerror(); /* Reset error status. */
     310                 :      32662 :         *value = dlsym(handle, symbol);
     311                 :      32662 :         err = dlerror();
     312   [ +  -  +  + ]:      32662 :         symbol_found = *value != NULL || err == NULL;
     313                 :            :     }
     314                 :            : #endif
     315                 :            : 
     316   [ +  +  -  + ]:     203847 :     if (!symbol_found && throw_err) {
     317                 :            : #ifdef _OS_WINDOWS_
     318                 :            :         char err[256];
     319                 :            :         win32_formatmessage(GetLastError(), err, sizeof(err));
     320                 :            : #endif
     321                 :            : #ifndef __clang_gcanalyzer__
     322                 :            :         // Hide the error throwing from the analyser since there isn't a way to express
     323                 :            :         // "safepoint only when throwing error" currently.
     324                 :          0 :         jl_errorf("could not load symbol \"%s\":\n%s", symbol, err);
     325                 :            : #endif
     326                 :            :     }
     327                 :     203847 :     return symbol_found;
     328                 :            : }
     329                 :            : 
     330                 :            : #ifdef _OS_WINDOWS_
     331                 :            : //Look for symbols in win32 libraries
     332                 :            : JL_DLLEXPORT const char *jl_dlfind_win32(const char *f_name)
     333                 :            : {
     334                 :            :     void * dummy;
     335                 :            :     if (jl_dlsym(jl_exe_handle, f_name, &dummy, 0))
     336                 :            :         return JL_EXE_LIBNAME;
     337                 :            :     if (jl_dlsym(jl_libjulia_internal_handle, f_name, &dummy, 0))
     338                 :            :         return JL_LIBJULIA_INTERNAL_DL_LIBNAME;
     339                 :            :     if (jl_dlsym(jl_libjulia_handle, f_name, &dummy, 0))
     340                 :            :         return JL_LIBJULIA_DL_LIBNAME;
     341                 :            :     if (jl_dlsym(jl_kernel32_handle, f_name, &dummy, 0))
     342                 :            :         return "kernel32";
     343                 :            :     if (jl_dlsym(jl_crtdll_handle, f_name, &dummy, 0)) // Prefer crtdll over ntdll
     344                 :            :         return jl_crtdll_basename;
     345                 :            :     if (jl_dlsym(jl_ntdll_handle, f_name, &dummy, 0))
     346                 :            :         return "ntdll";
     347                 :            :     if (jl_dlsym(jl_winsock_handle, f_name, &dummy, 0))
     348                 :            :         return "ws2_32";
     349                 :            :     // additional common libraries (libc?) could be added here, but in general,
     350                 :            :     // it is better to specify the library explicitly in the code. This exists
     351                 :            :     // mainly to ease compatibility with linux, and for libraries that don't
     352                 :            :     // have a name (julia.exe and libjulia.dll)
     353                 :            :     // We could also loop over all libraries that have been used so far, but, again,
     354                 :            :     // explicit is preferred over implicit
     355                 :            :     return NULL;
     356                 :            :     // oops, we didn't find it. NULL defaults to searching jl_RTLD_DEFAULT_handle,
     357                 :            :     // which defaults to jl_libjulia_internal_handle, where we won't find it, and
     358                 :            :     // will throw the appropriate error.
     359                 :            : }
     360                 :            : #endif
     361                 :            : 
     362                 :            : #ifdef __cplusplus
     363                 :            : }
     364                 :            : #endif

Generated by: LCOV version 1.14