Revision 6e23543bc477eb46e5fc8d5cab119190b990ed7c authored by Keno Fischer on 24 November 2023, 15:26:51 UTC, committed by GitHub on 24 November 2023, 15:26:51 UTC
This is cherry-picked from #52245. This is an independent bugfix, and looks like #52245 might need another round of discussion. There were two separate off-by-1's in the codegen code that is trying to detect assignments to slots inside try/catch regions. First, it was asking to include the value of the catch label, which is actually the first statement *not* in the try region. Second, there was a confusion of 0 and 1 based indexing in the iteration bounds. The end result of this was that the code was also looking at the first two statements of the catch region. This wasn't a problem before #52245 (other than a potentially over-conservative marking of some slots as volatile), because our catch blocks always had at least two statements (a :leave and a terminator), but with the `:leave` change, it is possible to have catch blocks with only one statement. If these happened to be at the end of the function, things would blow up. As a side node, this code isn't particularly sound, because it assumes that try/catch regions are lexical, which they are not. The assumption happens to work out ok for the code we generate in the frontend and optimized IR doesn't have slots, so we don't use this code, but it is not in general sound.
1 parent a386cd1
init.c
// This file is a part of Julia. License is MIT: https://julialang.org/license
/*
init.c
system initialization and global state
*/
#include "platform.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <libgen.h> // defines dirname
#if !defined(_OS_WINDOWS_) || defined(_COMPILER_GCC_)
#include <getopt.h>
#endif
#if defined(_OS_FREEBSD_)
#include <pthread_np.h>
#endif
#include "julia.h"
#include "julia_internal.h"
#define DEFINE_BUILTIN_GLOBALS
#include "builtin_proto.h"
#undef DEFINE_BUILTIN_GLOBALS
#include "threading.h"
#include "julia_assert.h"
#include "processor.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _OS_WINDOWS_
extern int needsSymRefreshModuleList;
extern BOOL (WINAPI *hSymRefreshModuleList)(HANDLE);
#else
#include <sys/resource.h>
#include <unistd.h>
#endif
// list of modules being deserialized with __init__ methods
jl_array_t *jl_module_init_order;
JL_DLLEXPORT size_t jl_page_size;
void jl_init_stack_limits(int ismaster, void **stack_lo, void **stack_hi)
{
#ifdef _OS_WINDOWS_
(void)ismaster;
// https://en.wikipedia.org/wiki/Win32_Thread_Information_Block
# ifdef _P64
*stack_hi = (void**)__readgsqword(0x08); // Stack Base / Bottom of stack (high address)
*stack_lo = (void**)__readgsqword(0x10); // Stack Limit / Ceiling of stack (low address)
# else // !_P64
*stack_hi = (void**)__readfsdword(0x04); // Stack Base / Bottom of stack (high address)
*stack_lo = (void**)__readfsdword(0x08); // Stack Limit / Ceiling of stack (low address)
# endif // _P64
#else // !_OS_WINDOWS_
// Only use pthread_*_np functions to get stack address for non-master
// threads since it seems to return bogus values for master thread on Linux
// and possibly OSX.
if (!ismaster) {
# if defined(_OS_LINUX_)
pthread_attr_t attr;
pthread_getattr_np(pthread_self(), &attr);
void *stackaddr;
size_t stacksize;
pthread_attr_getstack(&attr, &stackaddr, &stacksize);
pthread_attr_destroy(&attr);
*stack_lo = (void*)stackaddr;
#pragma GCC diagnostic push
#if defined(_COMPILER_GCC_) && __GNUC__ >= 12
#pragma GCC diagnostic ignored "-Wdangling-pointer"
#endif
*stack_hi = (void*)__builtin_frame_address(0);
#pragma GCC diagnostic pop
return;
# elif defined(_OS_DARWIN_)
extern void *pthread_get_stackaddr_np(pthread_t thread);
extern size_t pthread_get_stacksize_np(pthread_t thread);
pthread_t thread = pthread_self();
void *stackaddr = pthread_get_stackaddr_np(thread);
*stack_lo = (void*)stackaddr;
*stack_hi = (void*)__builtin_frame_address(0);
return;
# elif defined(_OS_FREEBSD_)
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_get_np(pthread_self(), &attr);
void *stackaddr;
size_t stacksize;
pthread_attr_getstack(&attr, &stackaddr, &stacksize);
pthread_attr_destroy(&attr);
*stack_lo = (void*)stackaddr;
*stack_hi = (void*)__builtin_frame_address(0);
return;
# else
# warning "Getting precise stack size for thread is not supported."
# endif
}
struct rlimit rl;
getrlimit(RLIMIT_STACK, &rl);
size_t stacksize = rl.rlim_cur;
*stack_hi = __builtin_frame_address(0);
*stack_lo = (void*)((char*)*stack_hi - stacksize);
#endif
}
static void jl_prep_sanitizers(void)
{
#if !defined(_OS_WINDOWS_)
#if defined(_COMPILER_ASAN_ENABLED_) || defined(_COMPILER_MSAN_ENABLED_)
struct rlimit rl;
// When using the sanitizers, increase stack size because they bloat
// stack usage
const rlim_t kStackSize = 64 * 1024 * 1024; // 64MiB stack
int result;
result = getrlimit(RLIMIT_STACK, &rl);
if (result == 0) {
if (rl.rlim_cur < kStackSize) {
rl.rlim_cur = kStackSize;
result = setrlimit(RLIMIT_STACK, &rl);
if (result != 0) {
fprintf(stderr, "setrlimit returned result = %d\n", result);
}
}
}
#endif
#endif
}
struct uv_shutdown_queue_item { uv_handle_t *h; struct uv_shutdown_queue_item *next; };
struct uv_shutdown_queue { struct uv_shutdown_queue_item *first; struct uv_shutdown_queue_item *last; };
static void jl_uv_exitcleanup_add(uv_handle_t *handle, struct uv_shutdown_queue *queue)
{
struct uv_shutdown_queue_item *item = (struct uv_shutdown_queue_item*)malloc_s(sizeof(struct uv_shutdown_queue_item));
item->h = handle;
item->next = NULL;
if (queue->last)
queue->last->next = item;
if (!queue->first)
queue->first = item;
queue->last = item;
}
static void jl_uv_exitcleanup_walk(uv_handle_t *handle, void *arg)
{
jl_uv_exitcleanup_add(handle, (struct uv_shutdown_queue*)arg);
}
static struct uv_shutdown_queue_item *next_shutdown_queue_item(struct uv_shutdown_queue_item *item)
{
struct uv_shutdown_queue_item *rv = item->next;
free(item);
return rv;
}
static void jl_close_item_atexit(uv_handle_t *handle)
{
if (handle->type != UV_FILE && uv_is_closing(handle))
return;
switch(handle->type) {
case UV_PROCESS:
// cause Julia to forget about the Process object
handle->data = NULL;
// and make libuv think it is already dead
((uv_process_t*)handle)->pid = 0;
// fall-through
case UV_TTY:
case UV_UDP:
case UV_TCP:
case UV_NAMED_PIPE:
case UV_POLL:
case UV_TIMER:
case UV_ASYNC:
case UV_FS_EVENT:
case UV_FS_POLL:
case UV_IDLE:
case UV_PREPARE:
case UV_CHECK:
case UV_SIGNAL:
case UV_FILE:
// These will be shutdown as appropriate by jl_close_uv
jl_close_uv(handle);
break;
case UV_HANDLE:
case UV_STREAM:
default:
assert(0 && "not a valid libuv handle");
}
}
// This prevents `ct` from returning via error handlers or other unintentional
// means by destroying some old state before we start destroying that state in atexit hooks.
void jl_task_frame_noreturn(jl_task_t *ct) JL_NOTSAFEPOINT;
// cause this process to exit with WEXITSTATUS(signo), after waiting to finish all julia, C, and C++ cleanup
JL_DLLEXPORT void jl_exit(int exitcode)
{
jl_atexit_hook(exitcode);
exit(exitcode);
}
// cause this process to exit with WTERMSIG(signo),
// fairly aggressively (flushing stderr a bit, and doing a little bit of other
// external cleanup, but no internal cleanup)
JL_DLLEXPORT void jl_raise(int signo)
{
uv_tty_reset_mode();
fflush(NULL);
#ifdef _OS_WINDOWS_
if (signo == SIGABRT) {
signal(signo, SIG_DFL);
abort();
}
// the exit status could also potentially be set to an NTSTATUS value
// corresponding to a signal number, but this seems somewhat is uncommon on Windows
TerminateProcess(GetCurrentProcess(), 3); // aka _exit
abort(); // prior call does not return, because we passed GetCurrentProcess()
#else
signal(signo, SIG_DFL);
sigset_t sset;
sigemptyset(&sset);
sigaddset(&sset, signo);
pthread_sigmask(SIG_UNBLOCK, &sset, NULL);
raise(signo); // aka pthread_kill(pthread_self(), signo);
if (signo == SIGABRT)
abort();
_exit(128 + signo);
#endif
}
JL_DLLEXPORT void jl_atexit_hook(int exitcode) JL_NOTSAFEPOINT_ENTER
{
uv_tty_reset_mode();
if (jl_atomic_load_relaxed(&jl_all_tls_states) == NULL)
return;
jl_task_t *ct = jl_get_current_task();
if (ct == NULL && jl_base_module) {
ct = container_of(jl_adopt_thread(), jl_task_t, gcstack);
}
else if (ct != NULL) {
// we are about to start tearing everything down, so lets try not to get
// upset by the local mess of things when we run the user's _atexit hooks
// this also forces us into a GC-unsafe region without a safepoint
jl_task_frame_noreturn(ct);
jl_gc_safepoint_(ct->ptls);
}
if (jl_base_module) {
jl_value_t *f = jl_get_global(jl_base_module, jl_symbol("_atexit"));
if (f != NULL) {
jl_value_t **fargs;
JL_GC_PUSHARGS(fargs, 2);
fargs[0] = f;
fargs[1] = jl_box_int32(exitcode);
JL_TRY {
assert(ct);
size_t last_age = ct->world_age;
ct->world_age = jl_get_world_counter();
jl_apply(fargs, 2);
ct->world_age = last_age;
}
JL_CATCH {
jl_printf((JL_STREAM*)STDERR_FILENO, "\natexit hook threw an error: ");
jl_static_show((JL_STREAM*)STDERR_FILENO, jl_current_exception());
jl_printf((JL_STREAM*)STDERR_FILENO, "\n");
jlbacktrace(); // written to STDERR_FILENO
}
JL_GC_POP();
}
}
if (ct && exitcode == 0)
jl_write_compiler_output();
jl_print_gc_stats(JL_STDERR);
if (jl_options.code_coverage)
jl_write_coverage_data(jl_options.output_code_coverage);
if (jl_options.malloc_log)
jl_write_malloc_log();
// replace standard output streams with something that we can still print to
// after the finalizers from base/stream.jl close the TTY
JL_STDOUT = (uv_stream_t*) STDOUT_FILENO;
JL_STDERR = (uv_stream_t*) STDERR_FILENO;
if (ct)
jl_gc_run_all_finalizers(ct);
uv_loop_t *loop = jl_global_event_loop();
if (loop != NULL) {
struct uv_shutdown_queue queue = {NULL, NULL};
JL_UV_LOCK();
uv_walk(loop, jl_uv_exitcleanup_walk, &queue);
struct uv_shutdown_queue_item *item = queue.first;
if (ct) {
while (item) {
JL_TRY {
while (item) {
jl_close_item_atexit(item->h);
item = next_shutdown_queue_item(item);
}
}
JL_CATCH {
//error handling -- continue cleanup, as much as possible
assert(item);
uv_unref(item->h);
jl_printf((JL_STREAM*)STDERR_FILENO, "error during exit cleanup: close: ");
jl_static_show((JL_STREAM*)STDERR_FILENO, jl_current_exception());
jl_printf((JL_STREAM*)STDERR_FILENO, "\n");
jlbacktrace(); // written to STDERR_FILENO
item = next_shutdown_queue_item(item);
}
}
}
else {
while (item) {
jl_close_item_atexit(item->h);
item = next_shutdown_queue_item(item);
}
}
// force libuv to spin until everything has finished closing
loop->stop_flag = 0;
while (uv_run(loop, UV_RUN_DEFAULT)) { }
jl_wake_libuv(); // set the async pending flag, so that future calls are immediate no-ops on other threads
// we would like to guarantee this, but cannot currently, so there is still a small race window
// that needs to be fixed in libuv
}
if (ct)
(void)jl_gc_safe_enter(ct->ptls); // park in gc-safe
if (loop != NULL) {
// TODO: consider uv_loop_close(loop) here, before shutdown?
uv_library_shutdown();
// no JL_UV_UNLOCK(), since it is now torn down
}
// TODO: Destroy threads?
jl_destroy_timing(); // cleans up the current timing_stack for noreturn
#ifdef USE_TIMING_COUNTS
jl_print_timings();
#endif
jl_teardown_codegen(); // prints stats
}
JL_DLLEXPORT void jl_postoutput_hook(void)
{
if (jl_atomic_load_relaxed(&jl_all_tls_states) == NULL)
return;
if (jl_base_module) {
jl_task_t *ct = jl_get_current_task();
jl_value_t *f = jl_get_global(jl_base_module, jl_symbol("_postoutput"));
if (f != NULL) {
JL_TRY {
size_t last_age = ct->world_age;
ct->world_age = jl_get_world_counter();
jl_apply(&f, 1);
ct->world_age = last_age;
}
JL_CATCH {
jl_printf((JL_STREAM*)STDERR_FILENO, "\npostoutput hook threw an error: ");
jl_static_show((JL_STREAM*)STDERR_FILENO, jl_current_exception());
jl_printf((JL_STREAM*)STDERR_FILENO, "\n");
jlbacktrace(); // written to STDERR_FILENO
}
}
}
return;
}
void post_boot_hooks(void);
void post_image_load_hooks(void);
JL_DLLEXPORT void *jl_libjulia_internal_handle;
JL_DLLEXPORT void *jl_libjulia_handle;
JL_DLLEXPORT void *jl_RTLD_DEFAULT_handle;
JL_DLLEXPORT void *jl_exe_handle;
#ifdef _OS_WINDOWS_
void *jl_ntdll_handle;
void *jl_kernel32_handle;
void *jl_crtdll_handle;
void *jl_winsock_handle;
extern const char *jl_crtdll_name;
#endif
uv_loop_t *jl_io_loop;
#ifdef _OS_WINDOWS_
static int uv_dup(uv_os_fd_t fd, uv_os_fd_t* dupfd) {
HANDLE current_process;
if (fd == UV_STDIN_FD || fd == UV_STDOUT_FD || fd == UV_STDERR_FD)
fd = GetStdHandle((DWORD)(uintptr_t) fd);
/* _get_osfhandle will sometimes return -2 in case of an error. This seems */
/* to happen when fd <= 2 and the process' corresponding stdio handle is */
/* set to NULL. Unfortunately DuplicateHandle will happily duplicate */
/* (HANDLE) -2, so this situation goes unnoticed until someone tries to */
/* use the duplicate. Therefore we filter out known-invalid handles here. */
if (fd == INVALID_HANDLE_VALUE ||
fd == NULL ||
fd == (HANDLE) -2) {
*dupfd = INVALID_HANDLE_VALUE;
return 0; // allow the execution to continue even if stdio is not available as in batchmode or without a console
}
current_process = GetCurrentProcess();
if (!DuplicateHandle(current_process,
fd,
current_process,
dupfd,
0,
TRUE,
DUPLICATE_SAME_ACCESS)) {
*dupfd = INVALID_HANDLE_VALUE;
return GetLastError();
}
return 0;
}
#else
static int uv_dup(uv_os_fd_t fd, uv_os_fd_t* dupfd) {
if ((*dupfd = fcntl(fd, F_DUPFD_CLOEXEC, 3)) == -1)
return -errno;
return 0;
}
#endif
static void *init_stdio_handle(const char *stdio, uv_os_fd_t fd, int readable)
{
void *handle;
int err;
// Duplicate the file descriptor so we can later dup it over if we want to redirect
// STDIO without having to worry about closing the associated libuv object.
// This also helps limit the impact other libraries can cause on our file handle.
if ((err = uv_dup(fd, &fd)))
jl_errorf("error initializing %s in uv_dup: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
switch(uv_guess_handle(fd)) {
case UV_TTY:
handle = malloc_s(sizeof(uv_tty_t));
if ((err = uv_tty_init(jl_io_loop, (uv_tty_t*)handle, fd, 0))) {
jl_errorf("error initializing %s in uv_tty_init: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
}
((uv_tty_t*)handle)->data = NULL;
uv_tty_set_mode((uv_tty_t*)handle, UV_TTY_MODE_NORMAL); // initialized cooked stdio
break;
default:
assert(0 && "missing case for uv_guess_handle return handling");
JL_FALLTHROUGH;
case UV_UDP:
JL_FALLTHROUGH;
case UV_UNKNOWN_HANDLE:
// dup the descriptor with a new one pointing at the bit bucket ...
#if defined(_OS_WINDOWS_)
CloseHandle(fd);
fd = CreateFile("NUL", readable ? FILE_GENERIC_READ : FILE_GENERIC_WRITE | FILE_READ_ATTRIBUTES,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
#else
{
int nullfd;
nullfd = open("/dev/null", O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH /* 0666 */);
assert(nullfd != -1);
dup2(nullfd, fd);
close(nullfd);
}
#endif
// ...and continue on as in the UV_FILE case
JL_FALLTHROUGH;
case UV_FILE:
handle = malloc_s(sizeof(jl_uv_file_t));
{
jl_uv_file_t *file = (jl_uv_file_t*)handle;
file->loop = jl_io_loop;
file->type = UV_FILE;
file->file = fd;
file->data = NULL;
}
break;
case UV_NAMED_PIPE:
handle = malloc_s(sizeof(uv_pipe_t));
if ((err = uv_pipe_init(jl_io_loop, (uv_pipe_t*)handle, 0))) {
jl_errorf("error initializing %s in uv_pipe_init: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
}
if ((err = uv_pipe_open((uv_pipe_t*)handle, fd))) {
jl_errorf("error initializing %s in uv_pipe_open: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
}
((uv_pipe_t*)handle)->data = NULL;
break;
case UV_TCP:
handle = malloc_s(sizeof(uv_tcp_t));
if ((err = uv_tcp_init(jl_io_loop, (uv_tcp_t*)handle))) {
jl_errorf("error initializing %s in uv_tcp_init: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
}
if ((err = uv_tcp_open((uv_tcp_t*)handle, (uv_os_sock_t)fd))) {
jl_errorf("error initializing %s in uv_tcp_open: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err);
}
((uv_tcp_t*)handle)->data = NULL;
break;
}
return handle;
}
static void init_stdio(void)
{
JL_STDIN = (uv_stream_t*)init_stdio_handle("stdin", UV_STDIN_FD, 1);
JL_STDOUT = (uv_stream_t*)init_stdio_handle("stdout", UV_STDOUT_FD, 0);
JL_STDERR = (uv_stream_t*)init_stdio_handle("stderr", UV_STDERR_FD, 0);
jl_flush_cstdio();
}
int jl_isabspath(const char *in) JL_NOTSAFEPOINT
{
#ifdef _OS_WINDOWS_
char c0 = in[0];
if (c0 == '/' || c0 == '\\') {
return 1; // absolute path relative to %CD% (current drive), or UNC
}
else if (c0 && in[1] == ':') {
char c2 = in[2];
return c2 == '/' || c2 == '\\'; // absolute path with drive name
}
#else
if (in[0] == '/') return 1; // absolute path
#endif
return 0; // relative path
}
static char *abspath(const char *in, int nprefix)
{ // compute an absolute realpath location, so that chdir doesn't change the file reference
// ignores (copies directly over) nprefix characters at the start of abspath
#ifndef _OS_WINDOWS_
char *out = realpath(in + nprefix, NULL);
if (out) {
if (nprefix > 0) {
size_t sz = strlen(out) + 1;
char *cpy = (char*)malloc_s(sz + nprefix);
memcpy(cpy, in, nprefix);
memcpy(cpy + nprefix, out, sz);
free(out);
out = cpy;
}
}
else {
size_t sz = strlen(in + nprefix) + 1;
if (in[nprefix] == PATHSEPSTRING[0]) {
out = (char*)malloc_s(sz + nprefix);
memcpy(out, in, sz + nprefix);
}
else {
size_t path_size = JL_PATH_MAX;
char *path = (char*)malloc_s(JL_PATH_MAX);
if (uv_cwd(path, &path_size)) {
jl_error("fatal error: unexpected error while retrieving current working directory");
}
out = (char*)malloc_s(path_size + 1 + sz + nprefix);
memcpy(out, in, nprefix);
memcpy(out + nprefix, path, path_size);
out[nprefix + path_size] = PATHSEPSTRING[0];
memcpy(out + nprefix + path_size + 1, in + nprefix, sz);
free(path);
}
}
#else
// GetFullPathName intentionally errors if given an empty string so manually insert `.` to invoke cwd
char *in2 = (char*)malloc_s(JL_PATH_MAX);
if (strlen(in) - nprefix == 0) {
memcpy(in2, in, nprefix);
in2[nprefix] = '.';
in2[nprefix+1] = '\0';
in = in2;
}
DWORD n = GetFullPathName(in + nprefix, 0, NULL, NULL);
if (n <= 0) {
jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed");
}
char *out = (char*)malloc_s(n + nprefix);
DWORD m = GetFullPathName(in + nprefix, n, out + nprefix, NULL);
if (n != m + 1) {
jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed");
}
memcpy(out, in, nprefix);
free(in2);
#endif
return out;
}
// create an absolute-path copy of the input path format string
// formed as `joinpath(replace(pwd(), "%" => "%%"), in)`
// unless `in` starts with `%`
static const char *absformat(const char *in)
{
if (in[0] == '%' || jl_isabspath(in))
return in;
// get an escaped copy of cwd
size_t path_size = JL_PATH_MAX;
char path[JL_PATH_MAX];
if (uv_cwd(path, &path_size)) {
jl_error("fatal error: unexpected error while retrieving current working directory");
}
size_t sz = strlen(in) + 1;
size_t i, fmt_size = 0;
for (i = 0; i < path_size; i++)
fmt_size += (path[i] == '%' ? 2 : 1);
char *out = (char*)malloc_s(fmt_size + 1 + sz);
fmt_size = 0;
for (i = 0; i < path_size; i++) { // copy-replace pwd portion
char c = path[i];
out[fmt_size++] = c;
if (c == '%')
out[fmt_size++] = '%';
}
out[fmt_size++] = PATHSEPSTRING[0]; // path sep
memcpy(out + fmt_size, in, sz); // copy over format, including nul
return out;
}
static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel)
{
// this function resolves the paths in jl_options to absolute file locations as needed
// and it replaces the pointers to `julia_bindir`, `julia_bin`, `image_file`, and output file paths
// it may fail, print an error, and exit(1) if any of these paths are longer than JL_PATH_MAX
//
// note: if you care about lost memory, you should call the appropriate `free()` function
// on the original pointer for each `char*` you've inserted into `jl_options`, after
// calling `julia_init()`
char *free_path = (char*)malloc_s(JL_PATH_MAX);
size_t path_size = JL_PATH_MAX;
if (uv_exepath(free_path, &path_size)) {
jl_error("fatal error: unexpected error while retrieving exepath");
}
if (path_size >= JL_PATH_MAX) {
jl_error("fatal error: jl_options.julia_bin path too long");
}
jl_options.julia_bin = (char*)malloc_s(path_size + 1);
memcpy((char*)jl_options.julia_bin, free_path, path_size);
((char*)jl_options.julia_bin)[path_size] = '\0';
if (!jl_options.julia_bindir) {
jl_options.julia_bindir = getenv("JULIA_BINDIR");
if (!jl_options.julia_bindir) {
jl_options.julia_bindir = dirname(free_path);
}
}
if (jl_options.julia_bindir)
jl_options.julia_bindir = abspath(jl_options.julia_bindir, 0);
free(free_path);
free_path = NULL;
if (jl_options.image_file) {
if (rel == JL_IMAGE_JULIA_HOME && !jl_isabspath(jl_options.image_file)) {
// build time path, relative to JULIA_BINDIR
free_path = (char*)malloc_s(JL_PATH_MAX);
int n = snprintf(free_path, JL_PATH_MAX, "%s" PATHSEPSTRING "%s",
jl_options.julia_bindir, jl_options.image_file);
if (n >= JL_PATH_MAX || n < 0) {
jl_error("fatal error: jl_options.image_file path too long");
}
jl_options.image_file = free_path;
}
if (jl_options.image_file)
jl_options.image_file = abspath(jl_options.image_file, 0);
if (free_path) {
free(free_path);
free_path = NULL;
}
}
if (jl_options.outputo)
jl_options.outputo = abspath(jl_options.outputo, 0);
if (jl_options.outputji)
jl_options.outputji = abspath(jl_options.outputji, 0);
if (jl_options.outputbc)
jl_options.outputbc = abspath(jl_options.outputbc, 0);
if (jl_options.outputasm)
jl_options.outputasm = abspath(jl_options.outputasm, 0);
if (jl_options.machine_file)
jl_options.machine_file = abspath(jl_options.machine_file, 0);
if (jl_options.output_code_coverage)
jl_options.output_code_coverage = absformat(jl_options.output_code_coverage);
if (jl_options.tracked_path)
jl_options.tracked_path = abspath(jl_options.tracked_path, 0);
const char **cmdp = jl_options.cmds;
if (cmdp) {
for (; *cmdp; cmdp++) {
const char *cmd = *cmdp;
if (cmd[0] == 'L') {
*cmdp = abspath(cmd, 1);
}
}
}
}
JL_DLLEXPORT int jl_is_file_tracked(jl_sym_t *path)
{
const char* path_ = jl_symbol_name(path);
int tpath_len = strlen(jl_options.tracked_path);
return (strlen(path_) >= tpath_len) && (strncmp(path_, jl_options.tracked_path, tpath_len) == 0);
}
static void jl_set_io_wait(int v)
{
jl_task_t *ct = jl_current_task;
ct->ptls->io_wait = v;
}
extern jl_mutex_t jl_modules_mutex;
extern jl_mutex_t precomp_statement_out_lock;
extern jl_mutex_t newly_inferred_mutex;
extern jl_mutex_t global_roots_lock;
extern jl_mutex_t profile_show_peek_cond_lock;
static void restore_fp_env(void)
{
if (jl_set_zero_subnormals(0) || jl_set_default_nans(0)) {
jl_error("Failed to configure floating point environment");
}
}
static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct);
JL_DLLEXPORT int jl_default_debug_info_kind;
static void init_global_mutexes(void) {
JL_MUTEX_INIT(&jl_modules_mutex, "jl_modules_mutex");
JL_MUTEX_INIT(&precomp_statement_out_lock, "precomp_statement_out_lock");
JL_MUTEX_INIT(&newly_inferred_mutex, "newly_inferred_mutex");
JL_MUTEX_INIT(&global_roots_lock, "global_roots_lock");
JL_MUTEX_INIT(&jl_codegen_lock, "jl_codegen_lock");
JL_MUTEX_INIT(&typecache_lock, "typecache_lock");
JL_MUTEX_INIT(&profile_show_peek_cond_lock, "profile_show_peek_cond_lock");
}
JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel)
{
// initialize many things, in no particular order
// but generally running from simple platform things to optional
// configuration features
jl_init_timing();
// Make sure we finalize the tls callback before starting any threads.
(void)jl_get_pgcstack();
// initialize backtraces
jl_init_profile_lock();
#ifdef _OS_WINDOWS_
uv_mutex_init(&jl_in_stackwalk);
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_IGNORE_CVREC);
if (!SymInitialize(GetCurrentProcess(), "", 1)) {
jl_safe_printf("WARNING: failed to initialize stack walk info\n");
}
needsSymRefreshModuleList = 0;
#else
// nongnu libunwind initialization is only threadsafe on architecture where the
// author could access TSAN, per https://github.com/libunwind/libunwind/pull/109
// so we need to do this once early (before threads)
rec_backtrace(NULL, 0, 0);
#endif
libsupport_init();
jl_safepoint_init();
jl_page_size = jl_getpagesize();
htable_new(&jl_current_modules, 0);
init_global_mutexes();
jl_precompile_toplevel_module = NULL;
ios_set_io_wait_func = jl_set_io_wait;
jl_io_loop = uv_default_loop(); // this loop will internal events (spawning process etc.),
// best to call this first, since it also initializes libuv
jl_init_uv();
init_stdio();
restore_fp_env();
if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
restore_signals();
jl_init_intrinsic_properties();
// Important offset for external codegen.
jl_task_gcstack_offset = offsetof(jl_task_t, gcstack);
jl_task_ptls_offset = offsetof(jl_task_t, ptls);
jl_prep_sanitizers();
void *stack_lo, *stack_hi;
jl_init_stack_limits(1, &stack_lo, &stack_hi);
jl_libjulia_internal_handle = jl_find_dynamic_library_by_addr(&jl_load_dynamic_library);
jl_libjulia_handle = jl_find_dynamic_library_by_addr(&jl_any_type);
#ifdef _OS_WINDOWS_
jl_exe_handle = GetModuleHandleA(NULL);
jl_RTLD_DEFAULT_handle = jl_libjulia_internal_handle;
jl_ntdll_handle = jl_dlopen("ntdll.dll", JL_RTLD_NOLOAD); // bypass julia's pathchecking for system dlls
jl_kernel32_handle = jl_dlopen("kernel32.dll", JL_RTLD_NOLOAD);
jl_crtdll_handle = jl_dlopen(jl_crtdll_name, JL_RTLD_NOLOAD);
jl_winsock_handle = jl_dlopen("ws2_32.dll", JL_RTLD_NOLOAD);
HMODULE jl_dbghelp = (HMODULE) jl_dlopen("dbghelp.dll", JL_RTLD_NOLOAD);
needsSymRefreshModuleList = 0;
if (jl_dbghelp)
jl_dlsym(jl_dbghelp, "SymRefreshModuleList", (void **)&hSymRefreshModuleList, 1);
#else
jl_exe_handle = jl_dlopen(NULL, JL_RTLD_NOW);
#ifdef RTLD_DEFAULT
jl_RTLD_DEFAULT_handle = RTLD_DEFAULT;
#else
jl_RTLD_DEFAULT_handle = jl_exe_handle;
#endif
#endif
jl_init_rand();
jl_init_runtime_ccall();
jl_init_tasks();
jl_init_threading();
jl_init_threadinginfra();
if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
jl_install_default_signal_handlers();
jl_gc_init();
arraylist_new(&jl_linkage_blobs, 0);
arraylist_new(&jl_image_relocs, 0);
arraylist_new(&eytzinger_image_tree, 0);
arraylist_new(&eytzinger_idxs, 0);
arraylist_push(&eytzinger_idxs, (void*)0);
arraylist_push(&eytzinger_image_tree, (void*)1); // outside image
jl_ptls_t ptls = jl_init_threadtls(0);
#pragma GCC diagnostic push
#if defined(_COMPILER_GCC_) && __GNUC__ >= 12
#pragma GCC diagnostic ignored "-Wdangling-pointer"
#endif
// warning: this changes `jl_current_task`, so be careful not to call that from this function
jl_task_t *ct = jl_init_root_task(ptls, stack_lo, stack_hi);
#pragma GCC diagnostic pop
JL_GC_PROMISE_ROOTED(ct);
_finish_julia_init(rel, ptls, ct);
}
static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_task_t *ct)
{
JL_TIMING(JULIA_INIT, JULIA_INIT);
jl_resolve_sysimg_location(rel);
// loads sysimg if available, and conditionally sets jl_options.cpu_target
if (rel == JL_IMAGE_IN_MEMORY)
jl_set_sysimg_so(jl_exe_handle);
else if (jl_options.image_file)
jl_preload_sysimg_so(jl_options.image_file);
if (jl_options.cpu_target == NULL)
jl_options.cpu_target = "native";
jl_init_codegen();
jl_init_common_symbols();
if (jl_options.image_file) {
jl_restore_system_image(jl_options.image_file);
} else {
jl_init_types();
jl_global_roots_table = jl_alloc_memory_any(0);
}
jl_init_flisp();
jl_init_serializer();
if (!jl_options.image_file) {
jl_top_module = jl_core_module;
jl_init_intrinsic_functions();
jl_init_primitives();
jl_init_main_module();
jl_load(jl_core_module, "boot.jl");
post_boot_hooks();
}
if (jl_base_module == NULL) {
// nthreads > 1 requires code in Base
jl_atomic_store_relaxed(&jl_n_threads, 1);
jl_n_markthreads = 0;
jl_n_sweepthreads = 0;
jl_n_gcthreads = 0;
jl_n_threads_per_pool[0] = 1;
jl_n_threads_per_pool[1] = 0;
} else {
post_image_load_hooks();
}
jl_start_threads();
jl_gc_enable(1);
if (jl_options.image_file && (!jl_generating_output() || jl_options.incremental) && jl_module_init_order) {
jl_array_t *init_order = jl_module_init_order;
JL_GC_PUSH1(&init_order);
jl_module_init_order = NULL;
int i, l = jl_array_nrows(init_order);
for (i = 0; i < l; i++) {
jl_value_t *mod = jl_array_ptr_ref(init_order, i);
jl_module_run_initializer((jl_module_t*)mod);
}
JL_GC_POP();
}
if (jl_options.handle_signals == JL_OPTIONS_HANDLE_SIGNALS_ON)
jl_install_sigint_handler();
}
#ifdef __cplusplus
}
#endif
![swh spinner](/static/img/swh-spinner.gif)
Computing file changes ...