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 : 2396 : static void * load_library(const char * rel_path, const char * src_dir, int err) {
35 : 2396 : void * handle = NULL;
36 : :
37 : : // See if a handle is already open to the basename
38 : 2396 : const char *basename = rel_path + strlen(rel_path);
39 [ + + ]: 51514 : while (basename-- > rel_path)
40 [ + - + - ]: 49118 : if (*basename == PATHSEPSTRING[0] || *basename == '/')
41 : : break;
42 : 2396 : 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 [ + + - + ]: 2396 : if ((handle = dlopen(basename, RTLD_NOLOAD | RTLD_NOW | (err ? RTLD_GLOBAL : RTLD_LOCAL))))
49 : 0 : return handle;
50 : : #endif
51 : :
52 : 2396 : char path[2*JL_PATH_MAX + 1] = {0};
53 : 2396 : strncat(path, src_dir, sizeof(path) - 1);
54 : 2396 : strncat(path, PATHSEPSTRING, sizeof(path) - 1);
55 : 2396 : 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 [ + + ]: 2396 : handle = dlopen(path, RTLD_NOW | (err ? RTLD_GLOBAL : RTLD_LOCAL));
66 : : #endif
67 : :
68 [ - + ]: 2396 : 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 : 2396 : return handle;
93 : : }
94 : :
95 : 338435 : 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 : 338435 : 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 : 599 : JL_DLLEXPORT const char * jl_get_libdir()
106 : : {
107 : : // Reuse the path if this is not the first call.
108 [ - + ]: 599 : 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 [ - + ]: 599 : 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 : 599 : strcpy(lib_dir, info.dli_fname);
146 : : #endif
147 : : // Finally, convert to dirname
148 : 599 : const char * new_dir = dirname(lib_dir);
149 [ - + ]: 599 : 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 : 599 : return lib_dir;
154 : : }
155 : :
156 : : void * libjulia_internal = NULL;
157 : 599 : __attribute__((constructor)) void jl_load_libjulia_internal(void) {
158 : : // Only initialize this once
159 [ - + ]: 599 : if (libjulia_internal != NULL) {
160 : 0 : return;
161 : : }
162 : :
163 : : // Introspect to find our own path
164 : 599 : const char * lib_dir = jl_get_libdir();
165 : :
166 : : // Pre-load libraries that libjulia-internal needs.
167 : 599 : int deps_len = strlen(dep_libs);
168 : 599 : 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 : 599 : int special_idx = 0;
174 : 599 : char * special_library_names[2] = {NULL};
175 : 2396 : while (1) {
176 : : // try to find next colon character; if we can't, break out
177 : 2995 : char * colon = strchr(curr_dep, ':');
178 [ + + ]: 2995 : if (colon == NULL)
179 : 599 : break;
180 : :
181 : : // Chop the string at the colon so it's a valid-ending-string
182 : 2396 : *colon = '\0';
183 : :
184 : : // If this library name starts with `@`, don't open it here (but mark it as special)
185 [ + + ]: 2396 : if (curr_dep[0] == '@') {
186 [ - + ]: 1198 : 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 : 1198 : special_library_names[special_idx] = curr_dep + 1;
191 : 1198 : special_idx += 1;
192 : : } else {
193 : 1198 : load_library(curr_dep, lib_dir, 1);
194 : : }
195 : :
196 : : // Skip ahead to next dependency
197 : 2396 : curr_dep = colon + 1;
198 : : }
199 : :
200 [ - + ]: 599 : 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 : 599 : libjulia_internal = load_library(special_library_names[0], lib_dir, 1);
207 : 599 : 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 [ - + ]: 599 : 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 : 599 : codegen_func_names = jl_codegen_exported_func_names;
218 : 599 : codegen_liberr = " from libjulia-codegen\n";
219 : : }
220 : :
221 : : // Once we have libjulia-internal loaded, re-export its symbols:
222 [ + + ]: 309683 : for (unsigned int symbol_idx=0; jl_runtime_exported_func_names[symbol_idx] != NULL; ++symbol_idx) {
223 : 309084 : void *addr = lookup_symbol(libjulia_internal, jl_runtime_exported_func_names[symbol_idx]);
224 [ - + ]: 309084 : 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 : 309084 : (*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 : 599 : ((void (*)(void))jl_init_options_addr)();
233 : :
234 [ + + ]: 27554 : for (unsigned int symbol_idx=0; codegen_func_names[symbol_idx] != NULL; ++symbol_idx) {
235 : 26955 : void *addr = lookup_symbol(libjulia_codegen, codegen_func_names[symbol_idx]);
236 [ - + ]: 26955 : 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 : 26955 : (*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 : 599 : void (*jl_pgcstack_setkey)(void*, void*(*)(void)) = lookup_symbol(libjulia_internal, "jl_pgcstack_setkey");
245 [ - + ]: 599 : 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 : 599 : void *fptr = lookup_symbol(RTLD_DEFAULT, "jl_get_pgcstack_static");
250 : 599 : void *(*key)(void) = lookup_symbol(RTLD_DEFAULT, "jl_pgcstack_addr_static");
251 [ + - + - ]: 599 : if (fptr != NULL && key != NULL)
252 : 599 : 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 : 599 : ((void (*)(void))jl_init_options_addr)();
258 : : }
259 : :
260 : : // Load libjulia and run the REPL with the given arguments (in UTF-8 format)
261 : 599 : 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 [ - + ]: 599 : 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 : 599 : int (*entrypoint)(int, char **) = (int (*)(int, char **))lookup_symbol(libjulia_internal, "jl_repl_entrypoint");
273 [ - + ]: 599 : 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 : 599 : 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
|