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
|