LCOV - code coverage report
Current view: top level - cli - loader_lib.c (source / functions) Hit Total Coverage
Test: [build process] commit ef510b1f346f4c9f9d86eaceace5ca54961a1dbc Lines: 68 111 61.3 %
Date: 2022-07-17 01:01:28 Functions: 5 7 71.4 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 32 58 55.2 %

           Branch data     Line data    Source code
       1                 :            : // This file is a part of Julia. License is MIT: https://julialang.org/license
       2                 :            : 
       3                 :            : // This file defines an RPATH-style relative path loader for all platforms
       4                 :            : #include "loader.h"
       5                 :            : 
       6                 :            : #ifdef __cplusplus
       7                 :            : extern "C" {
       8                 :            : #endif
       9                 :            : 
      10                 :            : /* Bring in definitions of symbols exported from libjulia. */
      11                 :            : #include "jl_exports.h"
      12                 :            : 
      13                 :            : /* Bring in helper functions for windows without libgcc. */
      14                 :            : #ifdef _OS_WINDOWS_
      15                 :            : #include "loader_win_utils.c"
      16                 :            : #endif
      17                 :            : 
      18                 :            : // Save DEP_LIBS to a variable that is explicitly sized for expansion
      19                 :            : static char dep_libs[1024] = DEP_LIBS;
      20                 :            : 
      21                 :          0 : JL_DLLEXPORT void jl_loader_print_stderr(const char * msg)
      22                 :            : {
      23                 :          0 :     fputs(msg, stderr);
      24                 :          0 : }
      25                 :            : // I use three arguments a lot.
      26                 :          0 : void jl_loader_print_stderr3(const char * msg1, const char * msg2, const char * msg3)
      27                 :            : {
      28                 :          0 :     jl_loader_print_stderr(msg1);
      29                 :          0 :     jl_loader_print_stderr(msg2);
      30                 :          0 :     jl_loader_print_stderr(msg3);
      31                 :          0 : }
      32                 :            : 
      33                 :            : /* Wrapper around dlopen(), with extra relative pathing thrown in*/
      34                 :         60 : static void * load_library(const char * rel_path, const char * src_dir, int err) {
      35                 :         60 :     void * handle = NULL;
      36                 :            : 
      37                 :            :     // See if a handle is already open to the basename
      38                 :         60 :     const char *basename = rel_path + strlen(rel_path);
      39         [ +  + ]:       1290 :     while (basename-- > rel_path)
      40   [ +  -  +  - ]:       1230 :         if (*basename == PATHSEPSTRING[0] || *basename == '/')
      41                 :            :             break;
      42                 :         60 :     basename++;
      43                 :            : #if defined(_OS_WINDOWS_)
      44                 :            :     if ((handle = GetModuleHandleA(basename)))
      45                 :            :         return handle;
      46                 :            : #else
      47                 :            :     // if err == 0 the library is optional, so don't allow global lookups to see it
      48   [ +  +  -  + ]:         60 :     if ((handle = dlopen(basename, RTLD_NOLOAD | RTLD_NOW | (err ? RTLD_GLOBAL : RTLD_LOCAL))))
      49                 :          0 :         return handle;
      50                 :            : #endif
      51                 :            : 
      52                 :         60 :     char path[2*JL_PATH_MAX + 1] = {0};
      53                 :         60 :     strncat(path, src_dir, sizeof(path) - 1);
      54                 :         60 :     strncat(path, PATHSEPSTRING, sizeof(path) - 1);
      55                 :         60 :     strncat(path, rel_path, sizeof(path) - 1);
      56                 :            : 
      57                 :            : #if defined(_OS_WINDOWS_)
      58                 :            :     wchar_t wpath[2*JL_PATH_MAX + 1] = {0};
      59                 :            :     if (!utf8_to_wchar(path, wpath, 2*JL_PATH_MAX)) {
      60                 :            :         jl_loader_print_stderr3("ERROR: Unable to convert path ", path, " to wide string!\n");
      61                 :            :         exit(1);
      62                 :            :     }
      63                 :            :     handle = (void *)LoadLibraryExW(wpath, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
      64                 :            : #else
      65         [ +  + ]:         60 :     handle = dlopen(path, RTLD_NOW | (err ? RTLD_GLOBAL : RTLD_LOCAL));
      66                 :            : #endif
      67                 :            : 
      68         [ -  + ]:         60 :     if (handle == NULL) {
      69         [ #  # ]:          0 :         if (!err)
      70                 :          0 :             return NULL;
      71                 :          0 :         jl_loader_print_stderr3("ERROR: Unable to load dependent library ", path, "\n");
      72                 :            : #if defined(_OS_WINDOWS_)
      73                 :            :         LPWSTR wmsg = TEXT("");
      74                 :            :         FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
      75                 :            :                        FORMAT_MESSAGE_FROM_SYSTEM |
      76                 :            :                        FORMAT_MESSAGE_IGNORE_INSERTS |
      77                 :            :                        FORMAT_MESSAGE_MAX_WIDTH_MASK,
      78                 :            :                        NULL, GetLastError(),
      79                 :            :                        MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
      80                 :            :                        (LPWSTR)&wmsg, 0, NULL);
      81                 :            :         char err[256] = {0};
      82                 :            :         wchar_to_utf8(wmsg, err, 255);
      83                 :            :         jl_loader_print_stderr3("Message:", err, "\n");
      84                 :            : #else
      85                 :          0 :         char *dlerr = dlerror();
      86         [ #  # ]:          0 :         if (dlerr != NULL) {
      87                 :          0 :             jl_loader_print_stderr3("Message:", dlerr, "\n");
      88                 :            :         }
      89                 :            : #endif
      90                 :          0 :         exit(1);
      91                 :            :     }
      92                 :         60 :     return handle;
      93                 :            : }
      94                 :            : 
      95                 :       8475 : static void * lookup_symbol(const void * lib_handle, const char * symbol_name) {
      96                 :            : #ifdef _OS_WINDOWS_
      97                 :            :     return GetProcAddress((HMODULE) lib_handle, symbol_name);
      98                 :            : #else
      99                 :       8475 :     return dlsym((void *)lib_handle, symbol_name);
     100                 :            : #endif
     101                 :            : }
     102                 :            : 
     103                 :            : // Find the location of libjulia.
     104                 :            : char lib_dir[JL_PATH_MAX];
     105                 :         15 : JL_DLLEXPORT const char * jl_get_libdir()
     106                 :            : {
     107                 :            :     // Reuse the path if this is not the first call.
     108         [ -  + ]:         15 :     if (lib_dir[0] != 0) {
     109                 :          0 :         return lib_dir;
     110                 :            :     }
     111                 :            : #if defined(_OS_WINDOWS_)
     112                 :            :     // On Windows, we use GetModuleFileNameW
     113                 :            :     wchar_t libjulia_path[JL_PATH_MAX];
     114                 :            :     HMODULE libjulia = NULL;
     115                 :            : 
     116                 :            :     // Get a handle to libjulia.
     117                 :            :     if (!utf8_to_wchar(LIBJULIA_NAME, libjulia_path, JL_PATH_MAX)) {
     118                 :            :         jl_loader_print_stderr3("ERROR: Unable to convert path ", LIBJULIA_NAME, " to wide string!\n");
     119                 :            :         exit(1);
     120                 :            :     }
     121                 :            :     libjulia = LoadLibraryW(libjulia_path);
     122                 :            :     if (libjulia == NULL) {
     123                 :            :         jl_loader_print_stderr3("ERROR: Unable to load ", LIBJULIA_NAME, "!\n");
     124                 :            :         exit(1);
     125                 :            :     }
     126                 :            :     if (!GetModuleFileNameW(libjulia, libjulia_path, JL_PATH_MAX)) {
     127                 :            :         jl_loader_print_stderr("ERROR: GetModuleFileName() failed\n");
     128                 :            :         exit(1);
     129                 :            :     }
     130                 :            :     if (!wchar_to_utf8(libjulia_path, lib_dir, JL_PATH_MAX)) {
     131                 :            :         jl_loader_print_stderr("ERROR: Unable to convert julia path to UTF-8\n");
     132                 :            :         exit(1);
     133                 :            :     }
     134                 :            : #else
     135                 :            :     // On all other platforms, use dladdr()
     136                 :            :     Dl_info info;
     137         [ -  + ]:         15 :     if (!dladdr(&jl_get_libdir, &info)) {
     138                 :          0 :         jl_loader_print_stderr("ERROR: Unable to dladdr(&jl_get_libdir)!\n");
     139                 :          0 :         char *dlerr = dlerror();
     140         [ #  # ]:          0 :         if (dlerr != NULL) {
     141                 :          0 :             jl_loader_print_stderr3("Message:", dlerr, "\n");
     142                 :            :         }
     143                 :          0 :         exit(1);
     144                 :            :     }
     145                 :         15 :     strcpy(lib_dir, info.dli_fname);
     146                 :            : #endif
     147                 :            :     // Finally, convert to dirname
     148                 :         15 :     const char * new_dir = dirname(lib_dir);
     149         [ -  + ]:         15 :     if (new_dir != lib_dir) {
     150                 :            :         // On some platforms, dirname() mutates.  On others, it does not.
     151                 :          0 :         memcpy(lib_dir, new_dir, strlen(new_dir)+1);
     152                 :            :     }
     153                 :         15 :     return lib_dir;
     154                 :            : }
     155                 :            : 
     156                 :            : void * libjulia_internal = NULL;
     157                 :         15 : __attribute__((constructor)) void jl_load_libjulia_internal(void) {
     158                 :            :     // Only initialize this once
     159         [ -  + ]:         15 :     if (libjulia_internal != NULL) {
     160                 :          0 :         return;
     161                 :            :     }
     162                 :            : 
     163                 :            :     // Introspect to find our own path
     164                 :         15 :     const char * lib_dir = jl_get_libdir();
     165                 :            : 
     166                 :            :     // Pre-load libraries that libjulia-internal needs.
     167                 :         15 :     int deps_len = strlen(dep_libs);
     168                 :         15 :     char * curr_dep = &dep_libs[0];
     169                 :            : 
     170                 :            :     // We keep track of "special" libraries names (ones whose name is prefixed with `@`)
     171                 :            :     // which are libraries that we want to load in some special, custom way, such as
     172                 :            :     // `libjulia-internal` or `libjulia-codegen`.
     173                 :         15 :     int special_idx = 0;
     174                 :         15 :     char * special_library_names[2] = {NULL};
     175                 :         60 :     while (1) {
     176                 :            :         // try to find next colon character; if we can't, break out
     177                 :         75 :         char * colon = strchr(curr_dep, ':');
     178         [ +  + ]:         75 :         if (colon == NULL)
     179                 :         15 :             break;
     180                 :            : 
     181                 :            :         // Chop the string at the colon so it's a valid-ending-string
     182                 :         60 :         *colon = '\0';
     183                 :            : 
     184                 :            :         // If this library name starts with `@`, don't open it here (but mark it as special)
     185         [ +  + ]:         60 :         if (curr_dep[0] == '@') {
     186         [ -  + ]:         30 :             if (special_idx > sizeof(special_library_names)/sizeof(char *)) {
     187                 :          0 :                 jl_loader_print_stderr("ERROR: Too many special library names specified, check LOADER_BUILD_DEP_LIBS and friends!\n");
     188                 :          0 :                 exit(1);
     189                 :            :             }
     190                 :         30 :             special_library_names[special_idx] = curr_dep + 1;
     191                 :         30 :             special_idx += 1;
     192                 :            :         } else {
     193                 :         30 :             load_library(curr_dep, lib_dir, 1);
     194                 :            :         }
     195                 :            : 
     196                 :            :         // Skip ahead to next dependency
     197                 :         60 :         curr_dep = colon + 1;
     198                 :            :     }
     199                 :            : 
     200         [ -  + ]:         15 :     if (special_idx != sizeof(special_library_names)/sizeof(char *)) {
     201                 :          0 :         jl_loader_print_stderr("ERROR: Too few special library names specified, check LOADER_BUILD_DEP_LIBS and friends!\n");
     202                 :          0 :         exit(1);
     203                 :            :     }
     204                 :            : 
     205                 :            :     // Unpack our special library names.  This is why ordering of library names matters.
     206                 :         15 :     libjulia_internal = load_library(special_library_names[0], lib_dir, 1);
     207                 :         15 :     void *libjulia_codegen = load_library(special_library_names[1], lib_dir, 0);
     208                 :            :     const char * const * codegen_func_names;
     209                 :            :     const char *codegen_liberr;
     210         [ -  + ]:         15 :     if (libjulia_codegen == NULL) {
     211                 :            :         // if codegen is not available, use fallback implementation in libjulia-internal
     212                 :          0 :         libjulia_codegen = libjulia_internal;
     213                 :          0 :         codegen_func_names = jl_codegen_fallback_func_names;
     214                 :          0 :         codegen_liberr = " from libjulia-internal\n";
     215                 :            :     }
     216                 :            :     else {
     217                 :         15 :         codegen_func_names = jl_codegen_exported_func_names;
     218                 :         15 :         codegen_liberr = " from libjulia-codegen\n";
     219                 :            :     }
     220                 :            : 
     221                 :            :     // Once we have libjulia-internal loaded, re-export its symbols:
     222         [ +  + ]:       7755 :     for (unsigned int symbol_idx=0; jl_runtime_exported_func_names[symbol_idx] != NULL; ++symbol_idx) {
     223                 :       7740 :         void *addr = lookup_symbol(libjulia_internal, jl_runtime_exported_func_names[symbol_idx]);
     224         [ -  + ]:       7740 :         if (addr == NULL) {
     225                 :          0 :             jl_loader_print_stderr3("ERROR: Unable to load ", jl_runtime_exported_func_names[symbol_idx], " from libjulia-internal\n");
     226                 :          0 :             exit(1);
     227                 :            :         }
     228                 :       7740 :         (*jl_runtime_exported_func_addrs[symbol_idx]) = addr;
     229                 :            :     }
     230                 :            :     // jl_options must be initialized very early, in case an embedder sets some
     231                 :            :     // values there before calling jl_init
     232                 :         15 :     ((void (*)(void))jl_init_options_addr)();
     233                 :            : 
     234         [ +  + ]:        690 :     for (unsigned int symbol_idx=0; codegen_func_names[symbol_idx] != NULL; ++symbol_idx) {
     235                 :        675 :         void *addr = lookup_symbol(libjulia_codegen, codegen_func_names[symbol_idx]);
     236         [ -  + ]:        675 :         if (addr == NULL) {
     237                 :          0 :             jl_loader_print_stderr3("ERROR: Unable to load ", codegen_func_names[symbol_idx], codegen_liberr);
     238                 :          0 :             exit(1);
     239                 :            :         }
     240                 :        675 :         (*jl_codegen_exported_func_addrs[symbol_idx]) = addr;
     241                 :            :     }
     242                 :            :     // Next, if we're on Linux/FreeBSD, set up fast TLS.
     243                 :            : #if !defined(_OS_WINDOWS_) && !defined(_OS_DARWIN_)
     244                 :         15 :     void (*jl_pgcstack_setkey)(void*, void*(*)(void)) = lookup_symbol(libjulia_internal, "jl_pgcstack_setkey");
     245         [ -  + ]:         15 :     if (jl_pgcstack_setkey == NULL) {
     246                 :          0 :         jl_loader_print_stderr("ERROR: Cannot find jl_pgcstack_setkey() function within libjulia-internal!\n");
     247                 :          0 :         exit(1);
     248                 :            :     }
     249                 :         15 :     void *fptr = lookup_symbol(RTLD_DEFAULT, "jl_get_pgcstack_static");
     250                 :         15 :     void *(*key)(void) = lookup_symbol(RTLD_DEFAULT, "jl_pgcstack_addr_static");
     251   [ +  -  +  - ]:         15 :     if (fptr != NULL && key != NULL)
     252                 :         15 :         jl_pgcstack_setkey(fptr, key);
     253                 :            : #endif
     254                 :            : 
     255                 :            :     // jl_options must be initialized very early, in case an embedder sets some
     256                 :            :     // values there before calling jl_init
     257                 :         15 :     ((void (*)(void))jl_init_options_addr)();
     258                 :            : }
     259                 :            : 
     260                 :            : // Load libjulia and run the REPL with the given arguments (in UTF-8 format)
     261                 :         15 : JL_DLLEXPORT int jl_load_repl(int argc, char * argv[]) {
     262                 :            :     // Some compilers/platforms are known to have `__attribute__((constructor))` issues,
     263                 :            :     // so we have a fallback call of `jl_load_libjulia_internal()` here.
     264         [ -  + ]:         15 :     if (libjulia_internal == NULL) {
     265                 :          0 :         jl_load_libjulia_internal();
     266         [ #  # ]:          0 :         if (libjulia_internal == NULL) {
     267                 :          0 :             jl_loader_print_stderr("ERROR: libjulia-internal could not be loaded!\n");
     268                 :          0 :             exit(1);
     269                 :            :         }
     270                 :            :     }
     271                 :            :     // Load the repl entrypoint symbol and jump into it!
     272                 :         15 :     int (*entrypoint)(int, char **) = (int (*)(int, char **))lookup_symbol(libjulia_internal, "jl_repl_entrypoint");
     273         [ -  + ]:         15 :     if (entrypoint == NULL) {
     274                 :          0 :         jl_loader_print_stderr("ERROR: Unable to find `jl_repl_entrypoint()` within libjulia-internal!\n");
     275                 :          0 :         exit(1);
     276                 :            :     }
     277                 :         15 :     return entrypoint(argc, (char **)argv);
     278                 :            : }
     279                 :            : 
     280                 :            : #ifdef _OS_WINDOWS_
     281                 :            : int __stdcall DllMainCRTStartup(void* instance, unsigned reason, void* reserved) {
     282                 :            :     setup_stdio();
     283                 :            : 
     284                 :            :     // Because we override DllMainCRTStartup, we have to manually call our constructor methods
     285                 :            :     jl_load_libjulia_internal();
     286                 :            :     return 1;
     287                 :            : }
     288                 :            : #endif
     289                 :            : 
     290                 :            : #ifdef __cplusplus
     291                 :            : } // extern "C"
     292                 :            : #endif

Generated by: LCOV version 1.14