Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : /*
4 : : init.c
5 : : system initialization and global state
6 : : */
7 : : #include "platform.h"
8 : :
9 : : #include <stdlib.h>
10 : : #include <string.h>
11 : : #include <stdio.h>
12 : : #include <fcntl.h>
13 : : #include <errno.h>
14 : : #include <libgen.h> // defines dirname
15 : :
16 : : #if !defined(_OS_WINDOWS_) || defined(_COMPILER_GCC_)
17 : : #include <getopt.h>
18 : : #endif
19 : :
20 : : #if defined(_OS_FREEBSD_)
21 : : #include <pthread_np.h>
22 : : #endif
23 : :
24 : : #include "julia.h"
25 : : #include "julia_internal.h"
26 : : #define DEFINE_BUILTIN_GLOBALS
27 : : #include "builtin_proto.h"
28 : : #undef DEFINE_BUILTIN_GLOBALS
29 : : #include "threading.h"
30 : : #include "julia_assert.h"
31 : : #include "processor.h"
32 : :
33 : : #ifdef __cplusplus
34 : : extern "C" {
35 : : #endif
36 : :
37 : : #ifdef _OS_WINDOWS_
38 : : extern int needsSymRefreshModuleList;
39 : : extern BOOL (WINAPI *hSymRefreshModuleList)(HANDLE);
40 : : #else
41 : : #include <sys/resource.h>
42 : : #include <unistd.h>
43 : : #endif
44 : :
45 : : // list of modules being deserialized with __init__ methods
46 : : jl_array_t *jl_module_init_order;
47 : :
48 : : JL_DLLEXPORT size_t jl_page_size;
49 : :
50 : 15 : void jl_init_stack_limits(int ismaster, void **stack_lo, void **stack_hi)
51 : : {
52 : : #ifdef _OS_WINDOWS_
53 : : (void)ismaster;
54 : : // https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
55 : : # ifdef _P64
56 : : *stack_hi = (void**)__readgsqword(0x08); // Stack Base / Bottom of stack (high address)
57 : : *stack_lo = (void**)__readgsqword(0x10); // Stack Limit / Ceiling of stack (low address)
58 : : # else // !_P64
59 : : *stack_hi = (void**)__readfsdword(0x04); // Stack Base / Bottom of stack (high address)
60 : : *stack_lo = (void**)__readfsdword(0x08); // Stack Limit / Ceiling of stack (low address)
61 : : # endif // _P64
62 : : #else // !_OS_WINDOWS_
63 : : // Only use pthread_*_np functions to get stack address for non-master
64 : : // threads since it seems to return bogus values for master thread on Linux
65 : : // and possibly OSX.
66 [ - + ]: 15 : if (!ismaster) {
67 : : # if defined(_OS_LINUX_)
68 : : pthread_attr_t attr;
69 : 0 : pthread_getattr_np(pthread_self(), &attr);
70 : : void *stackaddr;
71 : : size_t stacksize;
72 : 0 : pthread_attr_getstack(&attr, &stackaddr, &stacksize);
73 : 0 : pthread_attr_destroy(&attr);
74 : 0 : *stack_lo = (void*)stackaddr;
75 : : #pragma GCC diagnostic push
76 : : #if defined(_COMPILER_GCC_) && __GNUC__ >= 12
77 : : #pragma GCC diagnostic ignored "-Wdangling-pointer"
78 : : #endif
79 : 0 : *stack_hi = (void*)&stacksize;
80 : : #pragma GCC diagnostic pop
81 : 0 : return;
82 : : # elif defined(_OS_DARWIN_)
83 : : extern void *pthread_get_stackaddr_np(pthread_t thread);
84 : : extern size_t pthread_get_stacksize_np(pthread_t thread);
85 : : pthread_t thread = pthread_self();
86 : : void *stackaddr = pthread_get_stackaddr_np(thread);
87 : : size_t stacksize = pthread_get_stacksize_np(thread);
88 : : *stack_lo = (void*)stackaddr;
89 : : *stack_hi = (void*)&stacksize;
90 : : return;
91 : : # elif defined(_OS_FREEBSD_)
92 : : pthread_attr_t attr;
93 : : pthread_attr_init(&attr);
94 : : pthread_attr_get_np(pthread_self(), &attr);
95 : : void *stackaddr;
96 : : size_t stacksize;
97 : : pthread_attr_getstack(&attr, &stackaddr, &stacksize);
98 : : pthread_attr_destroy(&attr);
99 : : *stack_lo = (void*)stackaddr;
100 : : *stack_hi = (void*)&stacksize;
101 : : return;
102 : : # else
103 : : # warning "Getting precise stack size for thread is not supported."
104 : : # endif
105 : : }
106 : : struct rlimit rl;
107 : 15 : getrlimit(RLIMIT_STACK, &rl);
108 : 15 : size_t stacksize = rl.rlim_cur;
109 : : // We intentionally leak a stack address here core.StackAddressEscape
110 : : # ifndef __clang_analyzer__
111 : 15 : *stack_hi = (void*)&stacksize;
112 : : #pragma GCC diagnostic push
113 : : #if defined(_COMPILER_GCC_) && __GNUC__ >= 12
114 : : #pragma GCC diagnostic ignored "-Wdangling-pointer"
115 : : #endif
116 : 15 : *stack_lo = (void*)((char*)*stack_hi - stacksize);
117 : : #pragma GCC diagnostic pop
118 : : # else
119 : : *stack_hi = 0;
120 : : *stack_lo = 0;
121 : : # endif
122 : : #endif
123 : : }
124 : :
125 : 15 : static void jl_prep_sanitizers(void)
126 : : {
127 : : #if !defined(_OS_WINDOWS_)
128 : : #if defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_MSAN_ENABLED_)
129 : : struct rlimit rl;
130 : :
131 : : // When using the sanitizers, increase stack size because they bloat
132 : : // stack usage
133 : : const rlim_t kStackSize = 64 * 1024 * 1024; // 64MiB stack
134 : : int result;
135 : :
136 : : result = getrlimit(RLIMIT_STACK, &rl);
137 : : if (result == 0) {
138 : : if (rl.rlim_cur < kStackSize) {
139 : : rl.rlim_cur = kStackSize;
140 : : result = setrlimit(RLIMIT_STACK, &rl);
141 : : if (result != 0) {
142 : : fprintf(stderr, "setrlimit returned result = %d\n", result);
143 : : }
144 : : }
145 : : }
146 : : #endif
147 : : #endif
148 : 15 : }
149 : :
150 : : struct uv_shutdown_queue_item { uv_handle_t *h; struct uv_shutdown_queue_item *next; };
151 : : struct uv_shutdown_queue { struct uv_shutdown_queue_item *first; struct uv_shutdown_queue_item *last; };
152 : :
153 : 64 : static void jl_uv_exitcleanup_add(uv_handle_t *handle, struct uv_shutdown_queue *queue)
154 : : {
155 : 64 : struct uv_shutdown_queue_item *item = (struct uv_shutdown_queue_item*)malloc_s(sizeof(struct uv_shutdown_queue_item));
156 : 64 : item->h = handle;
157 : 64 : item->next = NULL;
158 [ + + ]: 64 : if (queue->last)
159 : 49 : queue->last->next = item;
160 [ + + ]: 64 : if (!queue->first)
161 : 15 : queue->first = item;
162 : 64 : queue->last = item;
163 : 64 : }
164 : :
165 : 64 : static void jl_uv_exitcleanup_walk(uv_handle_t *handle, void *arg)
166 : : {
167 : 64 : jl_uv_exitcleanup_add(handle, (struct uv_shutdown_queue*)arg);
168 : 64 : }
169 : :
170 : 64 : static struct uv_shutdown_queue_item *next_shutdown_queue_item(struct uv_shutdown_queue_item *item)
171 : : {
172 : 64 : struct uv_shutdown_queue_item *rv = item->next;
173 : 64 : free(item);
174 : 64 : return rv;
175 : : }
176 : :
177 : 64 : static void jl_close_item_atexit(uv_handle_t *handle)
178 : : {
179 [ + - + + ]: 64 : if (handle->type != UV_FILE && uv_is_closing(handle))
180 : 40 : return;
181 [ - + - ]: 24 : switch(handle->type) {
182 : 0 : case UV_PROCESS:
183 : : // cause Julia to forget about the Process object
184 : 0 : handle->data = NULL;
185 : : // and make libuv think it is already dead
186 : 0 : ((uv_process_t*)handle)->pid = 0;
187 : : // fall-through
188 : 24 : case UV_TTY:
189 : : case UV_UDP:
190 : : case UV_TCP:
191 : : case UV_NAMED_PIPE:
192 : : case UV_POLL:
193 : : case UV_TIMER:
194 : : case UV_ASYNC:
195 : : case UV_FS_EVENT:
196 : : case UV_FS_POLL:
197 : : case UV_IDLE:
198 : : case UV_PREPARE:
199 : : case UV_CHECK:
200 : : case UV_SIGNAL:
201 : : case UV_FILE:
202 : : // These will be shutdown as appropriate by jl_close_uv
203 : 24 : jl_close_uv(handle);
204 : 24 : break;
205 : 0 : case UV_HANDLE:
206 : : case UV_STREAM:
207 : : default:
208 : 0 : assert(0 && "not a valid libuv handle");
209 : : }
210 : : }
211 : :
212 : 15 : JL_DLLEXPORT void jl_atexit_hook(int exitcode)
213 : : {
214 [ - + ]: 15 : if (jl_all_tls_states == NULL)
215 : 0 : return;
216 : :
217 : 15 : jl_task_t *ct = jl_current_task;
218 : :
219 [ + - ]: 15 : if (exitcode == 0)
220 : 15 : jl_write_compiler_output();
221 : 15 : jl_print_gc_stats(JL_STDERR);
222 [ - + ]: 15 : if (jl_options.code_coverage)
223 : 0 : jl_write_coverage_data(jl_options.output_code_coverage);
224 [ - + ]: 15 : if (jl_options.malloc_log)
225 : 0 : jl_write_malloc_log();
226 [ + + ]: 15 : if (jl_base_module) {
227 : 14 : jl_value_t *f = jl_get_global(jl_base_module, jl_symbol("_atexit"));
228 [ + - ]: 14 : if (f != NULL) {
229 [ + - + + ]: 28 : JL_TRY {
230 : 14 : size_t last_age = ct->world_age;
231 : 14 : ct->world_age = jl_get_world_counter();
232 : 14 : jl_apply(&f, 1);
233 : 14 : ct->world_age = last_age;
234 : : }
235 [ # # ]: 0 : JL_CATCH {
236 : 0 : jl_printf((JL_STREAM*)STDERR_FILENO, "\natexit hook threw an error: ");
237 : 0 : jl_static_show((JL_STREAM*)STDERR_FILENO, jl_current_exception());
238 : 0 : jl_printf((JL_STREAM*)STDERR_FILENO, "\n");
239 : 0 : jlbacktrace(); // written to STDERR_FILENO
240 : : }
241 : : }
242 : : }
243 : :
244 : : // replace standard output streams with something that we can still print to
245 : : // after the finalizers from base/stream.jl close the TTY
246 : 15 : JL_STDOUT = (uv_stream_t*) STDOUT_FILENO;
247 : 15 : JL_STDERR = (uv_stream_t*) STDERR_FILENO;
248 : :
249 : 15 : jl_gc_run_all_finalizers(ct);
250 : :
251 : 15 : uv_loop_t *loop = jl_global_event_loop();
252 [ + - ]: 15 : if (loop != NULL) {
253 : 15 : struct uv_shutdown_queue queue = {NULL, NULL};
254 : 15 : JL_UV_LOCK();
255 : 15 : uv_walk(loop, jl_uv_exitcleanup_walk, &queue);
256 : 15 : struct uv_shutdown_queue_item *item = queue.first;
257 [ + - ]: 15 : if (ct != NULL) {
258 [ + + ]: 30 : while (item) {
259 [ + - + + ]: 30 : JL_TRY {
260 [ + + ]: 79 : while (item) {
261 : 64 : jl_close_item_atexit(item->h);
262 : 64 : item = next_shutdown_queue_item(item);
263 : : }
264 : : }
265 [ # # ]: 0 : JL_CATCH {
266 : : //error handling -- continue cleanup, as much as possible
267 [ # # ]: 0 : assert(item);
268 : 0 : uv_unref(item->h);
269 : 0 : jl_printf((JL_STREAM*)STDERR_FILENO, "error during exit cleanup: close: ");
270 : 0 : jl_static_show((JL_STREAM*)STDERR_FILENO, jl_current_exception());
271 : 0 : jl_printf((JL_STREAM*)STDERR_FILENO, "\n");
272 : 0 : jlbacktrace(); // written to STDERR_FILENO
273 : 0 : item = next_shutdown_queue_item(item);
274 : : }
275 : : }
276 : : }
277 : : else {
278 [ # # ]: 0 : while (item) {
279 : 0 : jl_close_item_atexit(item->h);
280 : 0 : item = next_shutdown_queue_item(item);
281 : : }
282 : : }
283 : :
284 : : // force libuv to spin until everything has finished closing
285 : 15 : loop->stop_flag = 0;
286 [ - + ]: 15 : while (uv_run(loop, UV_RUN_DEFAULT)) { }
287 : 15 : JL_UV_UNLOCK();
288 : : }
289 : :
290 : : // TODO: Destroy threads
291 : :
292 : 15 : jl_destroy_timing();
293 : : #ifdef ENABLE_TIMINGS
294 : : jl_print_timings();
295 : : #endif
296 : :
297 : 15 : jl_teardown_codegen();
298 : : }
299 : :
300 : 2 : JL_DLLEXPORT void jl_postoutput_hook(void)
301 : : {
302 [ - + ]: 2 : if (jl_all_tls_states == NULL)
303 : 0 : return;
304 : :
305 [ + - ]: 2 : if (jl_base_module) {
306 : 2 : jl_task_t *ct = jl_get_current_task();
307 : 2 : jl_value_t *f = jl_get_global(jl_base_module, jl_symbol("_postoutput"));
308 [ + - ]: 2 : if (f != NULL) {
309 [ + - + + ]: 4 : JL_TRY {
310 : 2 : size_t last_age = ct->world_age;
311 : 2 : ct->world_age = jl_get_world_counter();
312 : 2 : jl_apply(&f, 1);
313 : 2 : ct->world_age = last_age;
314 : : }
315 [ # # ]: 0 : JL_CATCH {
316 : 0 : jl_printf((JL_STREAM*)STDERR_FILENO, "\npostoutput hook threw an error: ");
317 : 0 : jl_static_show((JL_STREAM*)STDERR_FILENO, jl_current_exception());
318 : 0 : jl_printf((JL_STREAM*)STDERR_FILENO, "\n");
319 : 0 : jlbacktrace(); // written to STDERR_FILENO
320 : : }
321 : : }
322 : : }
323 : 2 : return;
324 : : }
325 : :
326 : : static void post_boot_hooks(void);
327 : :
328 : : JL_DLLEXPORT void *jl_libjulia_internal_handle;
329 : : JL_DLLEXPORT void *jl_libjulia_handle;
330 : : JL_DLLEXPORT void *jl_RTLD_DEFAULT_handle;
331 : : JL_DLLEXPORT void *jl_exe_handle;
332 : : #ifdef _OS_WINDOWS_
333 : : void *jl_ntdll_handle;
334 : : void *jl_kernel32_handle;
335 : : void *jl_crtdll_handle;
336 : : void *jl_winsock_handle;
337 : : extern const char *jl_crtdll_name;
338 : : #endif
339 : :
340 : : uv_loop_t *jl_io_loop;
341 : :
342 : : #ifdef _OS_WINDOWS_
343 : : static int uv_dup(uv_os_fd_t fd, uv_os_fd_t* dupfd) {
344 : : HANDLE current_process;
345 : :
346 : : if (fd == UV_STDIN_FD || fd == UV_STDOUT_FD || fd == UV_STDERR_FD)
347 : : fd = GetStdHandle((DWORD)(uintptr_t) fd);
348 : :
349 : : /* _get_osfhandle will sometimes return -2 in case of an error. This seems */
350 : : /* to happen when fd <= 2 and the process' corresponding stdio handle is */
351 : : /* set to NULL. Unfortunately DuplicateHandle will happily duplicate */
352 : : /* (HANDLE) -2, so this situation goes unnoticed until someone tries to */
353 : : /* use the duplicate. Therefore we filter out known-invalid handles here. */
354 : : if (fd == INVALID_HANDLE_VALUE ||
355 : : fd == NULL ||
356 : : fd == (HANDLE) -2) {
357 : : *dupfd = INVALID_HANDLE_VALUE;
358 : : return 0; // allow the execution to continue even if stdio is not available as in batchmode or without a console
359 : : }
360 : :
361 : : current_process = GetCurrentProcess();
362 : :
363 : : if (!DuplicateHandle(current_process,
364 : : fd,
365 : : current_process,
366 : : dupfd,
367 : : 0,
368 : : TRUE,
369 : : DUPLICATE_SAME_ACCESS)) {
370 : : *dupfd = INVALID_HANDLE_VALUE;
371 : : return GetLastError();
372 : : }
373 : :
374 : : return 0;
375 : : }
376 : : #else
377 : 45 : static int uv_dup(uv_os_fd_t fd, uv_os_fd_t* dupfd) {
378 [ - + ]: 45 : if ((*dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 3)) == -1)
379 : 0 : return -errno;
380 : 45 : return 0;
381 : : }
382 : : #endif
383 : :
384 : 45 : static void *init_stdio_handle(const char *stdio, uv_os_fd_t fd, int readable)
385 : : {
386 : : void *handle;
387 : : int err;
388 : : // Duplicate the file descriptor so we can later dup it over if we want to redirect
389 : : // STDIO without having to worry about closing the associated libuv object.
390 : : // This also helps limit the impact other libraries can cause on our file handle.
391 [ - + ]: 45 : if ((err = uv_dup(fd, &fd)))
392 : 0 : jl_errorf("error initializing %s in uv_dup: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
393 [ + - - + : 45 : switch(uv_guess_handle(fd)) {
+ - ]
394 : 37 : case UV_TTY:
395 : 37 : handle = malloc_s(sizeof(uv_tty_t));
396 [ - + ]: 37 : if ((err = uv_tty_init(jl_io_loop, (uv_tty_t*)handle, fd, 0))) {
397 : 0 : jl_errorf("error initializing %s in uv_tty_init: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
398 : : }
399 : 37 : ((uv_tty_t*)handle)->data = NULL;
400 : 37 : uv_tty_set_mode((uv_tty_t*)handle, UV_TTY_MODE_NORMAL); // initialized cooked stdio
401 : 37 : break;
402 : 0 : default:
403 : 0 : assert(0 && "missing case for uv_guess_handle return handling");
404 : : JL_FALLTHROUGH;
405 : : case UV_UDP:
406 : : JL_FALLTHROUGH;
407 : : case UV_UNKNOWN_HANDLE:
408 : : // dup the descriptor with a new one pointing at the bit bucket ...
409 : : #if defined(_OS_WINDOWS_)
410 : : CloseHandle(fd);
411 : : fd = CreateFile("NUL", readable ? FILE_GENERIC_READ : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES,
412 : : FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
413 : : #else
414 : : {
415 : : int nullfd;
416 : 0 : nullfd = open("/dev/null", O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH /* 0666 */);
417 [ # # ]: 0 : assert(nullfd != -1);
418 : 0 : dup2(nullfd, fd);
419 : 0 : close(nullfd);
420 : : }
421 : : #endif
422 : : // ...and continue on as in the UV_FILE case
423 : : JL_FALLTHROUGH;
424 : 2 : case UV_FILE:
425 : 2 : handle = malloc_s(sizeof(jl_uv_file_t));
426 : : {
427 : 2 : jl_uv_file_t *file = (jl_uv_file_t*)handle;
428 : 2 : file->loop = jl_io_loop;
429 : 2 : file->type = UV_FILE;
430 : 2 : file->file = fd;
431 : 2 : file->data = NULL;
432 : : }
433 : 2 : break;
434 : 6 : case UV_NAMED_PIPE:
435 : 6 : handle = malloc_s(sizeof(uv_pipe_t));
436 [ - + ]: 6 : if ((err = uv_pipe_init(jl_io_loop, (uv_pipe_t*)handle, 0))) {
437 : 0 : jl_errorf("error initializing %s in uv_pipe_init: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
438 : : }
439 [ - + ]: 6 : if ((err = uv_pipe_open((uv_pipe_t*)handle, fd))) {
440 : 0 : jl_errorf("error initializing %s in uv_pipe_open: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
441 : : }
442 : 6 : ((uv_pipe_t*)handle)->data = NULL;
443 : 6 : break;
444 : 0 : case UV_TCP:
445 : 0 : handle = malloc_s(sizeof(uv_tcp_t));
446 [ # # ]: 0 : if ((err = uv_tcp_init(jl_io_loop, (uv_tcp_t*)handle))) {
447 : 0 : jl_errorf("error initializing %s in uv_tcp_init: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
448 : : }
449 [ # # ]: 0 : if ((err = uv_tcp_open((uv_tcp_t*)handle, (uv_os_sock_t)fd))) {
450 : 0 : jl_errorf("error initializing %s in uv_tcp_open: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
451 : : }
452 : 0 : ((uv_tcp_t*)handle)->data = NULL;
453 : 0 : break;
454 : : }
455 : 45 : return handle;
456 : : }
457 : :
458 : 15 : static void init_stdio(void)
459 : : {
460 : 15 : JL_STDIN = (uv_stream_t*)init_stdio_handle("stdin", UV_STDIN_FD, 1);
461 : 15 : JL_STDOUT = (uv_stream_t*)init_stdio_handle("stdout", UV_STDOUT_FD, 0);
462 : 15 : JL_STDERR = (uv_stream_t*)init_stdio_handle("stderr", UV_STDERR_FD, 0);
463 : 15 : jl_flush_cstdio();
464 : 15 : }
465 : :
466 : 221 : int jl_isabspath(const char *in) JL_NOTSAFEPOINT
467 : : {
468 : : #ifdef _OS_WINDOWS_
469 : : char c0 = in[0];
470 : : if (c0 == '/' || c0 == '\\') {
471 : : return 1; // absolute path relative to %CD% (current drive), or UNC
472 : : }
473 : : else if (c0 && in[1] == ':') {
474 : : char c2 = in[2];
475 : : return c2 == '/' || c2 == '\\'; // absolute path with drive name
476 : : }
477 : : #else
478 [ + + ]: 221 : if (in[0] == '/') return 1; // absolute path
479 : : #endif
480 : 167 : return 0; // relative path
481 : : }
482 : :
483 : 38 : static char *abspath(const char *in, int nprefix)
484 : : { // compute an absolute realpath location, so that chdir doesn't change the file reference
485 : : // ignores (copies directly over) nprefix characters at the start of abspath
486 : : #ifndef _OS_WINDOWS_
487 : 38 : char *out = realpath(in + nprefix, NULL);
488 [ + + ]: 38 : if (out) {
489 [ - + ]: 29 : if (nprefix > 0) {
490 : 0 : size_t sz = strlen(out) + 1;
491 : 0 : char *cpy = (char*)malloc_s(sz + nprefix);
492 : 0 : memcpy(cpy, in, nprefix);
493 : 0 : memcpy(cpy + nprefix, out, sz);
494 : 0 : free(out);
495 : 0 : out = cpy;
496 : : }
497 : : }
498 : : else {
499 : 9 : size_t sz = strlen(in + nprefix) + 1;
500 [ + - ]: 9 : if (in[nprefix] == PATHSEPSTRING[0]) {
501 : 9 : out = (char*)malloc_s(sz + nprefix);
502 : 9 : memcpy(out, in, sz + nprefix);
503 : : }
504 : : else {
505 : 0 : size_t path_size = JL_PATH_MAX;
506 : 0 : char *path = (char*)malloc_s(JL_PATH_MAX);
507 [ # # ]: 0 : if (uv_cwd(path, &path_size)) {
508 : 0 : jl_error("fatal error: unexpected error while retrieving current working directory");
509 : : }
510 : 0 : out = (char*)malloc_s(path_size + 1 + sz + nprefix);
511 : 0 : memcpy(out, in, nprefix);
512 : 0 : memcpy(out + nprefix, path, path_size);
513 : 0 : out[nprefix + path_size] = PATHSEPSTRING[0];
514 : 0 : memcpy(out + nprefix + path_size + 1, in + nprefix, sz);
515 : 0 : free(path);
516 : : }
517 : : }
518 : : #else
519 : : DWORD n = GetFullPathName(in + nprefix, 0, NULL, NULL);
520 : : if (n <= 0) {
521 : : jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed");
522 : : }
523 : : char *out = (char*)malloc_s(n + nprefix);
524 : : DWORD m = GetFullPathName(in + nprefix, n, out + nprefix, NULL);
525 : : if (n != m + 1) {
526 : : jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed");
527 : : }
528 : : memcpy(out, in, nprefix);
529 : : #endif
530 : 38 : return out;
531 : : }
532 : :
533 : : // create an absolute-path copy of the input path format string
534 : : // formed as `joinpath(replace(pwd(), "%" => "%%"), in)`
535 : : // unless `in` starts with `%`
536 : 0 : static const char *absformat(const char *in)
537 : : {
538 [ # # # # ]: 0 : if (in[0] == '%' || jl_isabspath(in))
539 : 0 : return in;
540 : : // get an escaped copy of cwd
541 : 0 : size_t path_size = JL_PATH_MAX;
542 : : char path[JL_PATH_MAX];
543 [ # # ]: 0 : if (uv_cwd(path, &path_size)) {
544 : 0 : jl_error("fatal error: unexpected error while retrieving current working directory");
545 : : }
546 : 0 : size_t sz = strlen(in) + 1;
547 : 0 : size_t i, fmt_size = 0;
548 [ # # ]: 0 : for (i = 0; i < path_size; i++)
549 [ # # ]: 0 : fmt_size += (path[i] == '%' ? 2 : 1);
550 : 0 : char *out = (char*)malloc_s(fmt_size + 1 + sz);
551 : 0 : fmt_size = 0;
552 [ # # ]: 0 : for (i = 0; i < path_size; i++) { // copy-replace pwd portion
553 : 0 : char c = path[i];
554 : 0 : out[fmt_size++] = c;
555 [ # # ]: 0 : if (c == '%')
556 : 0 : out[fmt_size++] = '%';
557 : : }
558 : 0 : out[fmt_size++] = PATHSEPSTRING[0]; // path sep
559 : 0 : memcpy(out + fmt_size, in, sz); // copy over format, including nul
560 : 0 : return out;
561 : : }
562 : :
563 : 15 : static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel)
564 : : { // this function resolves the paths in jl_options to absolute file locations as needed
565 : : // and it replaces the pointers to `julia_bindir`, `julia_bin`, `image_file`, and output file paths
566 : : // it may fail, print an error, and exit(1) if any of these paths are longer than JL_PATH_MAX
567 : : //
568 : : // note: if you care about lost memory, you should call the appropriate `free()` function
569 : : // on the original pointer for each `char*` you've inserted into `jl_options`, after
570 : : // calling `julia_init()`
571 : 15 : char *free_path = (char*)malloc_s(JL_PATH_MAX);
572 : 15 : size_t path_size = JL_PATH_MAX;
573 [ - + ]: 15 : if (uv_exepath(free_path, &path_size)) {
574 : 0 : jl_error("fatal error: unexpected error while retrieving exepath");
575 : : }
576 [ - + ]: 15 : if (path_size >= JL_PATH_MAX) {
577 : 0 : jl_error("fatal error: jl_options.julia_bin path too long");
578 : : }
579 : 15 : jl_options.julia_bin = (char*)malloc_s(path_size + 1);
580 : 15 : memcpy((char*)jl_options.julia_bin, free_path, path_size);
581 : 15 : ((char*)jl_options.julia_bin)[path_size] = '\0';
582 [ + - ]: 15 : if (!jl_options.julia_bindir) {
583 : 15 : jl_options.julia_bindir = getenv("JULIA_BINDIR");
584 [ + + ]: 15 : if (!jl_options.julia_bindir) {
585 : 1 : jl_options.julia_bindir = dirname(free_path);
586 : : }
587 : : }
588 [ + - ]: 15 : if (jl_options.julia_bindir)
589 : 15 : jl_options.julia_bindir = abspath(jl_options.julia_bindir, 0);
590 : 15 : free(free_path);
591 : 15 : free_path = NULL;
592 [ + + ]: 15 : if (jl_options.image_file) {
593 [ + + + - ]: 14 : if (rel == JL_IMAGE_JULIA_HOME && !jl_isabspath(jl_options.image_file)) {
594 : : // build time path, relative to JULIA_BINDIR
595 : 2 : free_path = (char*)malloc_s(JL_PATH_MAX);
596 : 2 : int n = snprintf(free_path, JL_PATH_MAX, "%s" PATHSEPSTRING "%s",
597 : : jl_options.julia_bindir, jl_options.image_file);
598 [ + - - + ]: 2 : if (n >= JL_PATH_MAX || n < 0) {
599 : 0 : jl_error("fatal error: jl_options.image_file path too long");
600 : : }
601 : 2 : jl_options.image_file = free_path;
602 : : }
603 [ + - ]: 14 : if (jl_options.image_file)
604 : 14 : jl_options.image_file = abspath(jl_options.image_file, 0);
605 [ + + ]: 14 : if (free_path) {
606 : 2 : free(free_path);
607 : 2 : free_path = NULL;
608 : : }
609 : : }
610 [ + + ]: 15 : if (jl_options.outputo)
611 : 2 : jl_options.outputo = abspath(jl_options.outputo, 0);
612 [ + + ]: 15 : if (jl_options.outputji)
613 : 7 : jl_options.outputji = abspath(jl_options.outputji, 0);
614 [ - + ]: 15 : if (jl_options.outputbc)
615 : 0 : jl_options.outputbc = abspath(jl_options.outputbc, 0);
616 [ - + ]: 15 : if (jl_options.outputasm)
617 : 0 : jl_options.outputasm = abspath(jl_options.outputasm, 0);
618 [ - + ]: 15 : if (jl_options.machine_file)
619 : 0 : jl_options.machine_file = abspath(jl_options.machine_file, 0);
620 [ - + ]: 15 : if (jl_options.output_code_coverage)
621 : 0 : jl_options.output_code_coverage = absformat(jl_options.output_code_coverage);
622 [ - + ]: 15 : if (jl_options.tracked_path)
623 : 0 : jl_options.tracked_path = absformat(jl_options.tracked_path);
624 : :
625 : 15 : const char **cmdp = jl_options.cmds;
626 [ + + ]: 15 : if (cmdp) {
627 [ + + ]: 8 : for (; *cmdp; cmdp++) {
628 : 4 : const char *cmd = *cmdp;
629 [ - + ]: 4 : if (cmd[0] == 'L') {
630 : 0 : *cmdp = abspath(cmd, 1);
631 : : }
632 : : }
633 : : }
634 : 15 : }
635 : :
636 : 0 : JL_DLLEXPORT int jl_is_file_tracked(jl_sym_t *path)
637 : : {
638 : 0 : const char* path_ = jl_symbol_name(path);
639 : 0 : int tpath_len = strlen(jl_options.tracked_path);
640 [ # # # # ]: 0 : return (strlen(path_) >= tpath_len) && (strncmp(path_, jl_options.tracked_path, tpath_len) == 0);
641 : : }
642 : :
643 : 9134 : static void jl_set_io_wait(int v)
644 : : {
645 : 9134 : jl_task_t *ct = jl_current_task;
646 : 9134 : ct->ptls->io_wait = v;
647 : 9134 : }
648 : :
649 : : extern jl_mutex_t jl_modules_mutex;
650 : :
651 : 15 : static void restore_fp_env(void)
652 : : {
653 [ + - - + ]: 15 : if (jl_set_zero_subnormals(0) || jl_set_default_nans(0)) {
654 : 0 : jl_error("Failed to configure floating point environment");
655 : : }
656 : 15 : }
657 : :
658 : : static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct);
659 : :
660 : : JL_DLLEXPORT int jl_default_debug_info_kind;
661 : :
662 : 15 : JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel)
663 : : {
664 : 15 : jl_default_debug_info_kind = 0;
665 : :
666 : 15 : jl_init_timing();
667 : : // Make sure we finalize the tls callback before starting any threads.
668 : 15 : (void)jl_get_pgcstack();
669 : 15 : jl_safepoint_init();
670 : 15 : libsupport_init();
671 : 15 : htable_new(&jl_current_modules, 0);
672 : 15 : JL_MUTEX_INIT(&jl_modules_mutex);
673 : 15 : jl_precompile_toplevel_module = NULL;
674 : 15 : ios_set_io_wait_func = jl_set_io_wait;
675 : 15 : jl_io_loop = uv_default_loop(); // this loop will internal events (spawning process etc.),
676 : : // best to call this first, since it also initializes libuv
677 : 15 : jl_init_uv();
678 : 15 : init_stdio();
679 : 15 : restore_fp_env();
680 : 15 : restore_signals();
681 : 15 : jl_init_intrinsic_properties();
682 : :
683 : 15 : jl_page_size = jl_getpagesize();
684 : 15 : jl_prep_sanitizers();
685 : : void *stack_lo, *stack_hi;
686 : 15 : jl_init_stack_limits(1, &stack_lo, &stack_hi);
687 : :
688 : 15 : jl_libjulia_internal_handle = jl_load_dynamic_library(NULL, JL_RTLD_DEFAULT, 1);
689 : : #ifdef _OS_WINDOWS_
690 : : jl_exe_handle = GetModuleHandleA(NULL);
691 : : jl_RTLD_DEFAULT_handle = jl_libjulia_internal_handle;
692 : : if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
693 : : (LPCWSTR)&jl_any_type,
694 : : (HMODULE*)&jl_libjulia_handle)) {
695 : : jl_error("could not load base module");
696 : : }
697 : : jl_ntdll_handle = jl_dlopen("ntdll.dll", 0); // bypass julia's pathchecking for system dlls
698 : : jl_kernel32_handle = jl_dlopen("kernel32.dll", 0);
699 : : jl_crtdll_handle = jl_dlopen(jl_crtdll_name, 0);
700 : : jl_winsock_handle = jl_dlopen("ws2_32.dll", 0);
701 : : uv_mutex_init(&jl_in_stackwalk);
702 : : SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_IGNORE_CVREC);
703 : : if (!SymInitialize(GetCurrentProcess(), "", 1)) {
704 : : jl_printf(JL_STDERR, "WARNING: failed to initialize stack walk info\n");
705 : : }
706 : : needsSymRefreshModuleList = 0;
707 : : HMODULE jl_dbghelp = (HMODULE) jl_dlopen("dbghelp.dll", 0);
708 : : if (jl_dbghelp)
709 : : jl_dlsym(jl_dbghelp, "SymRefreshModuleList", (void **)&hSymRefreshModuleList, 1);
710 : : #else
711 : 15 : jl_exe_handle = jl_dlopen(NULL, JL_RTLD_NOW);
712 : : #ifdef RTLD_DEFAULT
713 : : jl_RTLD_DEFAULT_handle = RTLD_DEFAULT;
714 : : #else
715 : 15 : jl_RTLD_DEFAULT_handle = jl_exe_handle;
716 : : #endif
717 : : #endif
718 : :
719 [ + + + - : 15 : if ((jl_options.outputo || jl_options.outputbc || jl_options.outputasm) &&
- + ]
720 [ + - - + ]: 2 : (jl_options.code_coverage || jl_options.malloc_log)) {
721 : 0 : jl_error("cannot generate code-coverage or track allocation information while generating a .o, .bc, or .s output file");
722 : : }
723 : :
724 : 15 : jl_init_rand();
725 : 15 : jl_init_profile_lock();
726 : 15 : jl_init_runtime_ccall();
727 : 15 : jl_init_tasks();
728 : 15 : jl_init_threading();
729 : 15 : jl_init_threadinginfra();
730 [ + - ]: 15 : if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
731 : 15 : jl_install_default_signal_handlers();
732 : :
733 : 15 : jl_gc_init();
734 : 15 : jl_ptls_t ptls = jl_init_threadtls(0);
735 : : #pragma GCC diagnostic push
736 : : #if defined(_COMPILER_GCC_) && __GNUC__ >= 12
737 : : #pragma GCC diagnostic ignored "-Wdangling-pointer"
738 : : #endif
739 : : // warning: this changes `jl_current_task`, so be careful not to call that from this function
740 : 15 : jl_task_t *ct = jl_init_root_task(ptls, stack_lo, stack_hi);
741 : : #pragma GCC diagnostic pop
742 : : JL_GC_PROMISE_ROOTED(ct);
743 : 15 : _finish_julia_init(rel, ptls, ct);
744 : 15 : }
745 : :
746 : 15 : static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct)
747 : : {
748 : 15 : jl_resolve_sysimg_location(rel);
749 : : // loads sysimg if available, and conditionally sets jl_options.cpu_target
750 [ + + ]: 15 : if (jl_options.image_file)
751 : 14 : jl_preload_sysimg_so(jl_options.image_file);
752 [ - + ]: 15 : if (jl_options.cpu_target == NULL)
753 : 0 : jl_options.cpu_target = "native";
754 : :
755 [ + + ]: 15 : if (jl_options.image_file) {
756 : 14 : jl_restore_system_image(jl_options.image_file);
757 : : } else {
758 : 1 : jl_init_types();
759 : 1 : jl_init_codegen();
760 : : }
761 : :
762 : 15 : jl_init_common_symbols();
763 : 15 : jl_init_flisp();
764 : 15 : jl_init_serializer();
765 : :
766 [ + + ]: 15 : if (!jl_options.image_file) {
767 : 1 : jl_core_module = jl_new_module(jl_symbol("Core"));
768 : 1 : jl_core_module->parent = jl_core_module;
769 : 1 : jl_type_typename->mt->module = jl_core_module;
770 : 1 : jl_top_module = jl_core_module;
771 : 1 : jl_init_intrinsic_functions();
772 : 1 : jl_init_primitives();
773 : 1 : jl_init_main_module();
774 : 1 : jl_load(jl_core_module, "boot.jl");
775 : 1 : post_boot_hooks();
776 : : }
777 : :
778 [ + + ]: 15 : if (jl_base_module == NULL) {
779 : : // nthreads > 1 requires code in Base
780 : 3 : jl_n_threads = 1;
781 : : }
782 : 15 : jl_start_threads();
783 : :
784 : 15 : jl_gc_enable(1);
785 : :
786 [ + + + + : 15 : if (jl_options.image_file && (!jl_generating_output() || jl_options.incremental) && jl_module_init_order) {
+ + + - ]
787 : 10 : jl_array_t *init_order = jl_module_init_order;
788 : 10 : JL_GC_PUSH1(&init_order);
789 : 10 : jl_module_init_order = NULL;
790 : 10 : int i, l = jl_array_len(init_order);
791 [ + + ]: 250 : for (i = 0; i < l; i++) {
792 : 240 : jl_value_t *mod = jl_array_ptr_ref(init_order, i);
793 : 240 : jl_module_run_initializer((jl_module_t*)mod);
794 : : }
795 : 10 : JL_GC_POP();
796 : : }
797 : :
798 [ + - ]: 15 : if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
799 : 15 : jl_install_sigint_handler();
800 : 15 : }
801 : :
802 : 29 : static jl_value_t *core(const char *name)
803 : : {
804 : 29 : return jl_get_global(jl_core_module, jl_symbol(name));
805 : : }
806 : :
807 : : // fetch references to things defined in boot.jl
808 : 1 : static void post_boot_hooks(void)
809 : : {
810 : 1 : jl_char_type = (jl_datatype_t*)core("Char");
811 : 1 : jl_int8_type = (jl_datatype_t*)core("Int8");
812 : 1 : jl_int16_type = (jl_datatype_t*)core("Int16");
813 : 1 : jl_float16_type = (jl_datatype_t*)core("Float16");
814 : 1 : jl_float32_type = (jl_datatype_t*)core("Float32");
815 : 1 : jl_float64_type = (jl_datatype_t*)core("Float64");
816 : 1 : jl_floatingpoint_type = (jl_datatype_t*)core("AbstractFloat");
817 : 1 : jl_number_type = (jl_datatype_t*)core("Number");
818 : 1 : jl_signed_type = (jl_datatype_t*)core("Signed");
819 : 1 : jl_datatype_t *jl_unsigned_type = (jl_datatype_t*)core("Unsigned");
820 : 1 : jl_datatype_t *jl_integer_type = (jl_datatype_t*)core("Integer");
821 : :
822 : 1 : jl_bool_type->super = jl_integer_type;
823 : 1 : jl_uint8_type->super = jl_unsigned_type;
824 : 1 : jl_uint16_type->super = jl_unsigned_type;
825 : 1 : jl_uint32_type->super = jl_unsigned_type;
826 : 1 : jl_uint64_type->super = jl_unsigned_type;
827 : 1 : jl_int32_type->super = jl_signed_type;
828 : 1 : jl_int64_type->super = jl_signed_type;
829 : :
830 : 1 : jl_errorexception_type = (jl_datatype_t*)core("ErrorException");
831 : 1 : jl_stackovf_exception = jl_new_struct_uninit((jl_datatype_t*)core("StackOverflowError"));
832 : 1 : jl_diverror_exception = jl_new_struct_uninit((jl_datatype_t*)core("DivideError"));
833 : 1 : jl_undefref_exception = jl_new_struct_uninit((jl_datatype_t*)core("UndefRefError"));
834 : 1 : jl_undefvarerror_type = (jl_datatype_t*)core("UndefVarError");
835 : 1 : jl_atomicerror_type = (jl_datatype_t*)core("ConcurrencyViolationError");
836 : 1 : jl_interrupt_exception = jl_new_struct_uninit((jl_datatype_t*)core("InterruptException"));
837 : 1 : jl_boundserror_type = (jl_datatype_t*)core("BoundsError");
838 : 1 : jl_memory_exception = jl_new_struct_uninit((jl_datatype_t*)core("OutOfMemoryError"));
839 : 1 : jl_readonlymemory_exception = jl_new_struct_uninit((jl_datatype_t*)core("ReadOnlyMemoryError"));
840 : 1 : jl_typeerror_type = (jl_datatype_t*)core("TypeError");
841 : : #ifdef SEGV_EXCEPTION
842 : : jl_segv_exception = jl_new_struct_uninit((jl_datatype_t*)core("SegmentationFault"));
843 : : #endif
844 : 1 : jl_argumenterror_type = (jl_datatype_t*)core("ArgumentError");
845 : 1 : jl_methoderror_type = (jl_datatype_t*)core("MethodError");
846 : 1 : jl_loaderror_type = (jl_datatype_t*)core("LoadError");
847 : 1 : jl_initerror_type = (jl_datatype_t*)core("InitError");
848 : 1 : jl_pair_type = core("Pair");
849 : :
850 : 1 : jl_weakref_type = (jl_datatype_t*)core("WeakRef");
851 : 1 : jl_vecelement_typename = ((jl_datatype_t*)jl_unwrap_unionall(core("VecElement")))->name;
852 : :
853 : 1 : jl_init_box_caches();
854 : :
855 : : // set module field of primitive types
856 : : int i;
857 : 1 : void **table = jl_core_module->bindings.table;
858 [ + + ]: 1025 : for (i = 1; i < jl_core_module->bindings.size; i += 2) {
859 [ + + ]: 1024 : if (table[i] != HT_NOTFOUND) {
860 : 325 : jl_binding_t *b = (jl_binding_t*)table[i];
861 : 325 : jl_value_t *v = jl_atomic_load_relaxed(&b->value);
862 [ + + ]: 325 : if (v) {
863 [ + + ]: 314 : if (jl_is_unionall(v))
864 : 12 : v = jl_unwrap_unionall(v);
865 [ + + ]: 314 : if (jl_is_datatype(v)) {
866 : 208 : jl_datatype_t *tt = (jl_datatype_t*)v;
867 : 208 : tt->name->module = jl_core_module;
868 [ + + ]: 208 : if (tt->name->mt)
869 : 205 : tt->name->mt->module = jl_core_module;
870 : : }
871 : : }
872 : : }
873 : : }
874 : 1 : }
875 : :
876 : : #ifdef __cplusplus
877 : : }
878 : : #endif
|