From 8a2ad6b7a8437ca9a7df41ff337c06317f468277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mos=C3=A8=20Giordano?= Date: Tue, 27 Sep 2022 01:46:39 +0100 Subject: [PATCH 01/24] Probe if system libstdc++ is newer than ours If the system libstdc++ is detected to be newer, load it. Otherwise, load the one that we ship. This improves compatibility with external shared libraries that the user might have on their system. Fixes #34276 Co-authored-by: Jameson Nash Co-authored-by: Elliot Saba --- Make.inc | 2 + cli/Makefile | 6 +- cli/loader_lib.c | 220 +++++++++++++++++++++++++++++++++++++++++++++-- deps/csl.mk | 1 - 4 files changed, 221 insertions(+), 8 deletions(-) diff --git a/Make.inc b/Make.inc index dfc2594c406eb..d011290d2af9d 100644 --- a/Make.inc +++ b/Make.inc @@ -1149,6 +1149,8 @@ BB_TRIPLET := $(subst $(SPACE),-,$(filter-out cxx%,$(filter-out libgfortran%,$(s LIBGFORTRAN_VERSION := $(subst libgfortran,,$(filter libgfortran%,$(subst -,$(SPACE),$(BB_TRIPLET_LIBGFORTRAN)))) +CSL_NEXT_GLIBCXX_VERSION=GLIBCXX_3\.4\.30|GLIBCXX_3\.5\.|GLIBCXX_4\. + # This is the set of projects that BinaryBuilder dependencies are hooked up for. # Note: we explicitly _do not_ define `CSL` here, since it requires some more # advanced techniques to decide whether it should be installed from a BB source diff --git a/cli/Makefile b/cli/Makefile index 274877ecaf12a..58c1f82f48662 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -12,6 +12,8 @@ LOADER_LDFLAGS = $(JLDFLAGS) -ffreestanding -L$(build_shlibdir) -L$(build_libdir ifeq ($(OS),WINNT) LOADER_CFLAGS += -municode -mconsole -nostdlib -fno-stack-check -fno-stack-protector -mno-stack-arg-probe +else ifeq ($(OS),Linux) +LOADER_CFLAGS += -DGLIBCXX_LEAST_VERSION_SYMBOL=\"$(shell echo "$(CSL_NEXT_GLIBCXX_VERSION)" | cut -d'|' -f1 | sed 's/\\//g')\" endif ifeq ($(OS),WINNT) @@ -110,7 +112,7 @@ endif $(build_shlibdir)/libjulia.$(JL_MAJOR_MINOR_SHLIB_EXT): $(LIB_OBJS) $(SRCDIR)/list_strip_symbols.h | $(build_shlibdir) $(build_libdir) @$(call PRINT_LINK, $(CC) $(call IMPLIB_FLAGS,$@.tmp) $(LOADER_CFLAGS) -DLIBRARY_EXPORTS -shared $(SHIPFLAGS) $(LIB_OBJS) -o $@ \ - $(JLIBLDFLAGS) $(LOADER_LDFLAGS) $(RPATH_LIB) $(call SONAME_FLAGS,libjulia.$(JL_MAJOR_SHLIB_EXT))) + $(JLIBLDFLAGS) $(LOADER_LDFLAGS) $(call SONAME_FLAGS,libjulia.$(JL_MAJOR_SHLIB_EXT))) @$(INSTALL_NAME_CMD)libjulia.$(SHLIB_EXT) $@ ifeq ($(OS), WINNT) @# Note that if the objcopy command starts getting too long, we can use `@file` to read @@ -120,7 +122,7 @@ endif $(build_shlibdir)/libjulia-debug.$(JL_MAJOR_MINOR_SHLIB_EXT): $(LIB_DOBJS) $(SRCDIR)/list_strip_symbols.h | $(build_shlibdir) $(build_libdir) @$(call PRINT_LINK, $(CC) $(call IMPLIB_FLAGS,$@.tmp) $(LOADER_CFLAGS) -DLIBRARY_EXPORTS -shared $(DEBUGFLAGS) $(LIB_DOBJS) -o $@ \ - $(JLIBLDFLAGS) $(LOADER_LDFLAGS) $(RPATH_LIB) $(call SONAME_FLAGS,libjulia-debug.$(JL_MAJOR_SHLIB_EXT))) + $(JLIBLDFLAGS) $(LOADER_LDFLAGS) $(call SONAME_FLAGS,libjulia-debug.$(JL_MAJOR_SHLIB_EXT))) @$(INSTALL_NAME_CMD)libjulia-debug.$(SHLIB_EXT) $@ ifeq ($(OS), WINNT) @$(call PRINT_ANALYZE, $(OBJCOPY) $(build_libdir)/$(notdir $@).tmp.a $(STRIP_EXPORTED_FUNCS) $(build_libdir)/$(notdir $@).a && rm $(build_libdir)/$(notdir $@).tmp.a) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 74241510ffd25..c13f80796f864 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -33,7 +33,6 @@ void jl_loader_print_stderr3(const char * msg1, const char * msg2, const char * /* Wrapper around dlopen(), with extra relative pathing thrown in*/ static void * load_library(const char * rel_path, const char * src_dir, int err) { void * handle = NULL; - // See if a handle is already open to the basename const char *basename = rel_path + strlen(rel_path); while (basename-- > rel_path) @@ -153,6 +152,181 @@ JL_DLLEXPORT const char * jl_get_libdir() return lib_dir; } +#ifdef _OS_LINUX_ +#ifndef GLIBCXX_LEAST_VERSION_SYMBOL /* Should always be defined in the makefile. This appeases the linter. */ +#define GLIBCXX_LEAST_VERSION_SYMBOL "GLIBCXX_a.b.c" +#endif + +#include +#include + +static void write_wrapper(int fd, char *str, size_t len) +{ + size_t written_sofar = 0; + while (len) { + ssize_t bytes_written = write(fd, str + written_sofar, len); + if (bytes_written == -1 && errno == EINTR) continue; + if (bytes_written == -1 && errno != EINTR) { + perror("Error in write wrapper:\nwrite"); + _exit(1); + } + len -= bytes_written; + written_sofar += bytes_written; + } +} + +// Where the buf passed in was malloced +static void read_wrapper(int fd, char **ret, size_t *ret_len) +{ + // Allocate an initial buffer + size_t len = JL_PATH_MAX; + char *buf = (char *)malloc(len + 1); + if (!buf) { + char err_str[] = "Error in read wrapper:\nmalloc() returned NULL.\n"; + size_t err_strlen = strlen(err_str); + write_wrapper(STDERR_FILENO, err_str, err_strlen); + exit(1); + } + + // Read into it, reallocating as necessary + size_t have_read = 0; + while (1) { + ssize_t n = read(fd, buf + have_read, len - have_read); + have_read += n; + if (n == 0) break; + if (n == -1 && errno != EINTR) { + perror("Error in read wrapper:\nread"); + exit(1); + } + if (n == -1 && errno == EINTR) continue; + if (have_read == len) { + buf = (char *)realloc(buf, 1 + (len *= 2)); + if (!buf) { + char err_str[] = "Error in read wrapper:\nrealloc() returned NULL.\n"; + size_t err_strlen = strlen(err_str); + write_wrapper(STDERR_FILENO, err_str, err_strlen); + exit(1); + } + } + } + + *ret = buf; + *ret_len = have_read; +} + +// On Linux, it can happen that the system has a newer libstdc++ than the one we ship, +// which can break loading of some system libraries: . + +// Return the path to the libstdcxx to load. +// If the path is found, return it. +// Otherwise, print the error and exit. +// The path returned must be freed. +static char *libstdcxxprobe(void) +{ + // Create the pipe and child process. + int fork_pipe[2]; + int ret = pipe(fork_pipe); + if (ret == -1) { + perror("Error during libstdcxxprobe:\npipe"); + exit(1); + } + pid_t pid = fork(); + if (pid == -1) { + perror("Error during libstdcxxprobe:\nfork"); + exit(1); + } + if (pid == (pid_t) 0) { // Child process. + ret = close(fork_pipe[0]); + if (ret == -1) { + perror("Error during libstdcxxprobe in child process:\nclose"); + _exit(1); + } + + // Open the first available libstdc++.so. + // If it can't be found, report so by exiting zero. + void *handle = dlopen("libstdc++.so.6", RTLD_LAZY); + if (!handle) { + _exit(0); + } + + // See if the version is compatible + char *dlerr = dlerror(); // clear out dlerror + void *sym = dlsym(handle, GLIBCXX_LEAST_VERSION_SYMBOL); + dlerr = dlerror(); + if (dlerr) { + // We can't use the library that was found, so don't write anything. + // The main process will see that nothing was written, + // then exit the function and return null. + _exit(0); + } + + // No error means the symbol was found, we can use this library. + // Get the path to it, and write it to the parent process. + struct link_map *lm; + ret = dlinfo(handle, RTLD_DI_LINKMAP, &lm); + if (ret == -1) { + char errbuf[2048]; + int err_len = snprintf(errbuf, 2048, "Error during libstdcxxprobe in child process:\ndlinfo() - %s\n", dlerror()); + write_wrapper(STDOUT_FILENO, errbuf, (size_t)err_len); + _exit(1); + } + char *libpath = lm->l_name; + write_wrapper(fork_pipe[1], libpath, strlen(libpath)); + _exit(0); + } + else { // Parent process. + ret = close(fork_pipe[1]); + if (ret == -1) { + perror("Error during libstdcxxprobe in parent process:\nclose"); + _exit(1); + } + + // Wait for the child to complete. + while (1) { + int wstatus; + pid_t npid = waitpid(pid, &wstatus, 0); + if (npid == -1) { + if (errno == EINTR) continue; + if (errno != EINTR) { + perror("Error during libstdcxxprobe in parent process:\nwaitpid"); + exit(1); + } + } + else if (!WIFEXITED(wstatus)) { + char err_str[] = "Error during libstdcxxprobe in parent process:\n" + "The child process did not exit normally.\n"; + size_t err_strlen = strlen(err_str); + write_wrapper(STDERR_FILENO, err_str, err_strlen); + exit(1); + } + else if (WEXITSTATUS(wstatus)) { + // The child has printed an error and exited, so the parent should exit too. + exit(1); + } + break; + } + + // Read the absolute path to the lib from the child process. + char *path; + size_t pathlen; + read_wrapper(fork_pipe[0], &path, &pathlen); + + // Close the read end of the pipe + ret = close(fork_pipe[0]); + if (ret == -1) { + perror("Error during libstdcxxprobe in parent process:\nclose"); + _exit(1); + } + + if (!pathlen) { + free(path); + return NULL; + } + return path; + } +} +#endif + void * libjulia_internal = NULL; __attribute__((constructor)) void jl_load_libjulia_internal(void) { // Only initialize this once @@ -161,11 +335,46 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { } // Introspect to find our own path - const char * lib_dir = jl_get_libdir(); + const char *lib_dir = jl_get_libdir(); // Pre-load libraries that libjulia-internal needs. int deps_len = strlen(dep_libs); - char * curr_dep = &dep_libs[0]; + char *curr_dep = &dep_libs[0]; + + void *cxx_handle; + int done_probe = 0; +#if defined(_OS_LINUX_) + int do_probe = 1; + char *probevar = getenv("JULIA_PROBE_LIBSTDCXX"); + if (probevar) { + if (strcmp(probevar, "1") == 0 || strcmp(probevar, "yes")) + do_probe = 1; + else if (strcmp(probevar, "0") == 0 || strcmp(probevar, "no")) + do_probe = 0; + } + if (do_probe) { + char *cxxpath = libstdcxxprobe(); + if (cxxpath) { + cxx_handle = dlopen(cxxpath, RTLD_LAZY); + char *dlr = dlerror(); + if (dlr) { + size_t buflen = strlen(cxxpath) + 2048; + char *errbuf = malloc(buflen); + int err_len = snprintf(errbuf, buflen, "Error, cannot load \"%s\"\n" + "dlinfo() - %s\n", cxxpath, dlr); + write_wrapper(STDOUT_FILENO, errbuf, (size_t)err_len); + free(errbuf); + exit(1); + } + free(cxxpath); + done_probe = 1; + } + } +#endif + // If not on linux, or the probe does not finish successfully, load the bundled version. + if (!done_probe) { + load_library("libstdc++.so", jl_get_libdir(), 1); + } // We keep track of "special" libraries names (ones whose name is prefixed with `@`) // which are libraries that we want to load in some special, custom way, such as @@ -189,7 +398,8 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { } special_library_names[special_idx] = curr_dep + 1; special_idx += 1; - } else { + } + else { load_library(curr_dep, lib_dir, 1); } @@ -278,7 +488,7 @@ JL_DLLEXPORT int jl_load_repl(int argc, char * argv[]) { } #ifdef _OS_WINDOWS_ -int __stdcall DllMainCRTStartup(void* instance, unsigned reason, void* reserved) { +int __stdcall DllMainCRTStartup(void *instance, unsigned reason, void *reserved) { setup_stdio(); // Because we override DllMainCRTStartup, we have to manually call our constructor methods diff --git a/deps/csl.mk b/deps/csl.mk index e3f84aa98974d..ff500620d18fd 100644 --- a/deps/csl.mk +++ b/deps/csl.mk @@ -22,7 +22,6 @@ endef # would get from CSL (by searching for a `GLIBCXX_3.4.X` symbol that does not exist # in our CSL, but would in a newer one), and default to `USE_BINARYBUILDER_CSL=0` in # this case. -CSL_NEXT_GLIBCXX_VERSION=GLIBCXX_3\.4\.30|GLIBCXX_3\.5\.|GLIBCXX_4\. # First, check to see if BB is disabled on a global setting ifeq ($(USE_BINARYBUILDER),0) From 314df3e1337a280b2e17951cced9f35b267156f2 Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Fri, 14 Oct 2022 13:43:06 -0500 Subject: [PATCH 02/24] Addressed review comments. --- cli/loader_lib.c | 25 ++++++++----------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index c13f80796f864..016301417ba9c 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -152,9 +152,15 @@ JL_DLLEXPORT const char * jl_get_libdir() return lib_dir; } +// On Linux, it can happen that the system has a newer libstdc++ than the one we ship, +// which can break loading of some system libraries: . +// As a fix, on linux we probe the system libstdc++ to see if it is newer, and then load it if it is. +// Otherwise, we load the packaged one. This improves compatibility with third party dynamic libs that +// may depend on symbols exported by the system libstdxc++. #ifdef _OS_LINUX_ -#ifndef GLIBCXX_LEAST_VERSION_SYMBOL /* Should always be defined in the makefile. This appeases the linter. */ -#define GLIBCXX_LEAST_VERSION_SYMBOL "GLIBCXX_a.b.c" +#ifndef GLIBCXX_LEAST_VERSION_SYMBOL +#warning GLIBCXX_LEAST_VERSION_SYMBOL should always be defined in the makefile. +#define GLIBCXX_LEAST_VERSION_SYMBOL "GLIBCXX_a.b.c" // Appease the linter #endif #include @@ -214,9 +220,6 @@ static void read_wrapper(int fd, char **ret, size_t *ret_len) *ret_len = have_read; } -// On Linux, it can happen that the system has a newer libstdc++ than the one we ship, -// which can break loading of some system libraries: . - // Return the path to the libstdcxx to load. // If the path is found, return it. // Otherwise, print the error and exit. @@ -237,10 +240,6 @@ static char *libstdcxxprobe(void) } if (pid == (pid_t) 0) { // Child process. ret = close(fork_pipe[0]); - if (ret == -1) { - perror("Error during libstdcxxprobe in child process:\nclose"); - _exit(1); - } // Open the first available libstdc++.so. // If it can't be found, report so by exiting zero. @@ -276,10 +275,6 @@ static char *libstdcxxprobe(void) } else { // Parent process. ret = close(fork_pipe[1]); - if (ret == -1) { - perror("Error during libstdcxxprobe in parent process:\nclose"); - _exit(1); - } // Wait for the child to complete. while (1) { @@ -313,10 +308,6 @@ static char *libstdcxxprobe(void) // Close the read end of the pipe ret = close(fork_pipe[0]); - if (ret == -1) { - perror("Error during libstdcxxprobe in parent process:\nclose"); - _exit(1); - } if (!pathlen) { free(path); From b395b0b481ca5f712ff65569eef1e83ebe0cafd8 Mon Sep 17 00:00:00 2001 From: apaz Date: Fri, 14 Oct 2022 13:51:18 -0500 Subject: [PATCH 03/24] Change error handling in wrapper functions Co-authored-by: Jameson Nash --- cli/loader_lib.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 016301417ba9c..9ed88cee3110d 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -173,7 +173,7 @@ static void write_wrapper(int fd, char *str, size_t len) ssize_t bytes_written = write(fd, str + written_sofar, len); if (bytes_written == -1 && errno == EINTR) continue; if (bytes_written == -1 && errno != EINTR) { - perror("Error in write wrapper:\nwrite"); + perror("(julia) child libstdcxxprobe write"); _exit(1); } len -= bytes_written; @@ -188,9 +188,7 @@ static void read_wrapper(int fd, char **ret, size_t *ret_len) size_t len = JL_PATH_MAX; char *buf = (char *)malloc(len + 1); if (!buf) { - char err_str[] = "Error in read wrapper:\nmalloc() returned NULL.\n"; - size_t err_strlen = strlen(err_str); - write_wrapper(STDERR_FILENO, err_str, err_strlen); + perror("(julia) malloc"); exit(1); } @@ -201,16 +199,14 @@ static void read_wrapper(int fd, char **ret, size_t *ret_len) have_read += n; if (n == 0) break; if (n == -1 && errno != EINTR) { - perror("Error in read wrapper:\nread"); + perror("(julia) libstdcxxprobe read"); exit(1); } if (n == -1 && errno == EINTR) continue; if (have_read == len) { buf = (char *)realloc(buf, 1 + (len *= 2)); if (!buf) { - char err_str[] = "Error in read wrapper:\nrealloc() returned NULL.\n"; - size_t err_strlen = strlen(err_str); - write_wrapper(STDERR_FILENO, err_str, err_strlen); + perror("(julia) realloc"); exit(1); } } @@ -230,7 +226,7 @@ static char *libstdcxxprobe(void) int fork_pipe[2]; int ret = pipe(fork_pipe); if (ret == -1) { - perror("Error during libstdcxxprobe:\npipe"); + perror("(julia) Error during libstdcxxprobe: pipe"); exit(1); } pid_t pid = fork(); From 128e8be61ab0c969116084e9e243f8a792e8dc19 Mon Sep 17 00:00:00 2001 From: apaz Date: Fri, 14 Oct 2022 13:53:22 -0500 Subject: [PATCH 04/24] Call write_wrapper three times instead of snprintf Co-authored-by: Jameson Nash --- cli/loader_lib.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 9ed88cee3110d..9f7ec5ebc631d 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -260,9 +260,11 @@ static char *libstdcxxprobe(void) struct link_map *lm; ret = dlinfo(handle, RTLD_DI_LINKMAP, &lm); if (ret == -1) { - char errbuf[2048]; - int err_len = snprintf(errbuf, 2048, "Error during libstdcxxprobe in child process:\ndlinfo() - %s\n", dlerror()); - write_wrapper(STDOUT_FILENO, errbuf, (size_t)err_len); + char *errbuf = dlerror(); + const char *errdesc = "Error during libstdcxxprobe in child process:\ndlinfo: "; + write_wrapper(STDERR_FILENO, errdesc, strlen(errdesc)); + write_wrapper(STDERR_FILENO, errbuf, strlen(errbuf)); + write_wrapper(STDERR_FILENO, "\n", 1); _exit(1); } char *libpath = lm->l_name; From b4f876f9ff138b0d6b4b4badc917d5d503171628 Mon Sep 17 00:00:00 2001 From: apaz Date: Fri, 14 Oct 2022 14:12:09 -0500 Subject: [PATCH 05/24] Apply suggestions from code review Co-authored-by: Jameson Nash --- cli/loader_lib.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 9f7ec5ebc631d..575ba71b49d19 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -235,7 +235,7 @@ static char *libstdcxxprobe(void) exit(1); } if (pid == (pid_t) 0) { // Child process. - ret = close(fork_pipe[0]); + close(fork_pipe[0]); // Open the first available libstdc++.so. // If it can't be found, report so by exiting zero. @@ -272,7 +272,7 @@ static char *libstdcxxprobe(void) _exit(0); } else { // Parent process. - ret = close(fork_pipe[1]); + close(fork_pipe[1]); // Wait for the child to complete. while (1) { @@ -305,7 +305,7 @@ static char *libstdcxxprobe(void) read_wrapper(fork_pipe[0], &path, &pathlen); // Close the read end of the pipe - ret = close(fork_pipe[0]); + close(fork_pipe[0]); if (!pathlen) { free(path); @@ -347,12 +347,8 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { cxx_handle = dlopen(cxxpath, RTLD_LAZY); char *dlr = dlerror(); if (dlr) { - size_t buflen = strlen(cxxpath) + 2048; - char *errbuf = malloc(buflen); - int err_len = snprintf(errbuf, buflen, "Error, cannot load \"%s\"\n" - "dlinfo() - %s\n", cxxpath, dlr); - write_wrapper(STDOUT_FILENO, errbuf, (size_t)err_len); - free(errbuf); + jl_loader_print_stderr("ERROR: Unable to dlopen(cxxpath) in parent!\n"); + jl_loader_print_stderr3("Message: ", dlerr, "\n"); exit(1); } free(cxxpath); From f423adbaab1d7dcd59748698cd226834b7fd75ba Mon Sep 17 00:00:00 2001 From: apaz Date: Fri, 14 Oct 2022 14:14:30 -0500 Subject: [PATCH 06/24] Update cli/loader_lib.c Co-authored-by: Jameson Nash --- cli/loader_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 575ba71b49d19..44667bcd3f80b 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -358,7 +358,7 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { #endif // If not on linux, or the probe does not finish successfully, load the bundled version. if (!done_probe) { - load_library("libstdc++.so", jl_get_libdir(), 1); + load_library("libstdc++.so.6", lib_dir, 1); } // We keep track of "special" libraries names (ones whose name is prefixed with `@`) From 6d843257fadb17e1305345f1794bc27c8f79adab Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Fri, 14 Oct 2022 14:22:42 -0500 Subject: [PATCH 07/24] Reordered reading and waiting to avoid a deadlock. --- cli/loader_lib.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 44667bcd3f80b..1f463a20033cd 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -274,6 +274,14 @@ static char *libstdcxxprobe(void) else { // Parent process. close(fork_pipe[1]); + // Read the absolute path to the lib from the child process. + char *path; + size_t pathlen; + read_wrapper(fork_pipe[0], &path, &pathlen); + + // Close the read end of the pipe + close(fork_pipe[0]); + // Wait for the child to complete. while (1) { int wstatus; @@ -299,14 +307,6 @@ static char *libstdcxxprobe(void) break; } - // Read the absolute path to the lib from the child process. - char *path; - size_t pathlen; - read_wrapper(fork_pipe[0], &path, &pathlen); - - // Close the read end of the pipe - close(fork_pipe[0]); - if (!pathlen) { free(path); return NULL; From 2961b0f58352df2bf7c63c8f3d1fa8169e7d2d2b Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Mon, 17 Oct 2022 10:08:18 -0500 Subject: [PATCH 08/24] Fixed obvious issues. --- cli/loader_lib.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 1f463a20033cd..d46b5815527bf 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -261,7 +261,7 @@ static char *libstdcxxprobe(void) ret = dlinfo(handle, RTLD_DI_LINKMAP, &lm); if (ret == -1) { char *errbuf = dlerror(); - const char *errdesc = "Error during libstdcxxprobe in child process:\ndlinfo: "; + char *errdesc = (char*)"Error during libstdcxxprobe in child process:\ndlinfo: "; write_wrapper(STDERR_FILENO, errdesc, strlen(errdesc)); write_wrapper(STDERR_FILENO, errbuf, strlen(errbuf)); write_wrapper(STDERR_FILENO, "\n", 1); @@ -331,9 +331,10 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { char *curr_dep = &dep_libs[0]; void *cxx_handle; - int done_probe = 0; + #if defined(_OS_LINUX_) int do_probe = 1; + int done_probe = 0; char *probevar = getenv("JULIA_PROBE_LIBSTDCXX"); if (probevar) { if (strcmp(probevar, "1") == 0 || strcmp(probevar, "yes")) @@ -348,13 +349,14 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { char *dlr = dlerror(); if (dlr) { jl_loader_print_stderr("ERROR: Unable to dlopen(cxxpath) in parent!\n"); - jl_loader_print_stderr3("Message: ", dlerr, "\n"); + jl_loader_print_stderr3("Message: ", dlr, "\n"); exit(1); } free(cxxpath); done_probe = 1; } } + #endif // If not on linux, or the probe does not finish successfully, load the bundled version. if (!done_probe) { From 6ff2bf5dd82766c7fdc9038906460cb133c66b04 Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Mon, 17 Oct 2022 15:53:44 -0500 Subject: [PATCH 09/24] Only load libstdc++ preemptively on linux. --- cli/loader_lib.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index d46b5815527bf..018f791231906 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -160,7 +160,7 @@ JL_DLLEXPORT const char * jl_get_libdir() #ifdef _OS_LINUX_ #ifndef GLIBCXX_LEAST_VERSION_SYMBOL #warning GLIBCXX_LEAST_VERSION_SYMBOL should always be defined in the makefile. -#define GLIBCXX_LEAST_VERSION_SYMBOL "GLIBCXX_a.b.c" // Appease the linter +#define GLIBCXX_LEAST_VERSION_SYMBOL "GLIBCXX_a.b.c" /* Appease the linter */ #endif #include @@ -356,12 +356,10 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { done_probe = 1; } } - -#endif - // If not on linux, or the probe does not finish successfully, load the bundled version. if (!done_probe) { load_library("libstdc++.so.6", lib_dir, 1); } +#endif // We keep track of "special" libraries names (ones whose name is prefixed with `@`) // which are libraries that we want to load in some special, custom way, such as From 799c64209d1cb4820aaa6ed6a3fe8f00d42d7aa9 Mon Sep 17 00:00:00 2001 From: apaz Date: Tue, 18 Oct 2022 09:10:24 -0500 Subject: [PATCH 10/24] Update cli/loader_lib.c Co-authored-by: Jameson Nash --- cli/loader_lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 018f791231906..2cc8b655116e5 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -294,8 +294,8 @@ static char *libstdcxxprobe(void) } } else if (!WIFEXITED(wstatus)) { - char err_str[] = "Error during libstdcxxprobe in parent process:\n" - "The child process did not exit normally.\n"; + const char err_str* = "Error during libstdcxxprobe in parent process:\n" + "The child process did not exit normally.\n"; size_t err_strlen = strlen(err_str); write_wrapper(STDERR_FILENO, err_str, err_strlen); exit(1); From c6520406f3b3348c11b7d8b44ed20e40c82417e6 Mon Sep 17 00:00:00 2001 From: apaz Date: Tue, 18 Oct 2022 09:11:55 -0500 Subject: [PATCH 11/24] Update cli/loader_lib.c Co-authored-by: Jameson Nash --- cli/loader_lib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 2cc8b655116e5..beb8c5989258e 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -166,7 +166,7 @@ JL_DLLEXPORT const char * jl_get_libdir() #include #include -static void write_wrapper(int fd, char *str, size_t len) +static void write_wrapper(int fd, const char *str, size_t len) { size_t written_sofar = 0; while (len) { From 1574d22cd1f573f17db38265c78be5066afb2b7d Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Wed, 19 Oct 2022 13:42:02 -0500 Subject: [PATCH 12/24] Specified path to bundled libstdc++ on the command line. --- cli/Makefile | 2 +- cli/loader_lib.c | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/cli/Makefile b/cli/Makefile index 58c1f82f48662..b1b02b19a5dae 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -13,7 +13,7 @@ LOADER_LDFLAGS = $(JLDFLAGS) -ffreestanding -L$(build_shlibdir) -L$(build_libdir ifeq ($(OS),WINNT) LOADER_CFLAGS += -municode -mconsole -nostdlib -fno-stack-check -fno-stack-protector -mno-stack-arg-probe else ifeq ($(OS),Linux) -LOADER_CFLAGS += -DGLIBCXX_LEAST_VERSION_SYMBOL=\"$(shell echo "$(CSL_NEXT_GLIBCXX_VERSION)" | cut -d'|' -f1 | sed 's/\\//g')\" +LOADER_CFLAGS += -DGLIBCXX_LEAST_VERSION_SYMBOL=\"$(shell echo "$(CSL_NEXT_GLIBCXX_VERSION)" | cut -d'|' -f1 | sed 's/\\//g')\" -DGLIBCXX_PATH=\"$($(private_libdir)/libstdc++.so.6)\" endif ifeq ($(OS),WINNT) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index beb8c5989258e..c5e7055c7c399 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -294,7 +294,7 @@ static char *libstdcxxprobe(void) } } else if (!WIFEXITED(wstatus)) { - const char err_str* = "Error during libstdcxxprobe in parent process:\n" + const char *err_str = "Error during libstdcxxprobe in parent process:\n" "The child process did not exit normally.\n"; size_t err_strlen = strlen(err_str); write_wrapper(STDERR_FILENO, err_str, err_strlen); @@ -357,7 +357,11 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { } } if (!done_probe) { - load_library("libstdc++.so.6", lib_dir, 1); +#ifndef GLIBCXX_PATH +#define GLIBCXX_PATH "$(private_libdir)/usr/lib/julia/libstdc++.so.6" +#warning GLIBCXX_PATH is meant to be specified on the command line +#endif + load_library(GLIBCXX_PATH, lib_dir, 1); } #endif From 213fdce01e34c34452def599f28d4d17a67fa5b2 Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Wed, 19 Oct 2022 15:54:52 -0500 Subject: [PATCH 13/24] Removed whitespace. --- cli/loader_lib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index c5e7055c7c399..c6c521f35a931 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -281,7 +281,7 @@ static char *libstdcxxprobe(void) // Close the read end of the pipe close(fork_pipe[0]); - + // Wait for the child to complete. while (1) { int wstatus; @@ -331,7 +331,7 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { char *curr_dep = &dep_libs[0]; void *cxx_handle; - + #if defined(_OS_LINUX_) int do_probe = 1; int done_probe = 0; @@ -363,6 +363,7 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { #endif load_library(GLIBCXX_PATH, lib_dir, 1); } + puts(GLIBCXX_PATH); #endif // We keep track of "special" libraries names (ones whose name is prefixed with `@`) From ea991073243258bed37328114b5dafa27fc88219 Mon Sep 17 00:00:00 2001 From: apaz Date: Wed, 19 Oct 2022 16:40:15 -0500 Subject: [PATCH 14/24] Update cli/Makefile Co-authored-by: Jameson Nash --- cli/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/Makefile b/cli/Makefile index b1b02b19a5dae..e78f6f74791a9 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -13,7 +13,7 @@ LOADER_LDFLAGS = $(JLDFLAGS) -ffreestanding -L$(build_shlibdir) -L$(build_libdir ifeq ($(OS),WINNT) LOADER_CFLAGS += -municode -mconsole -nostdlib -fno-stack-check -fno-stack-protector -mno-stack-arg-probe else ifeq ($(OS),Linux) -LOADER_CFLAGS += -DGLIBCXX_LEAST_VERSION_SYMBOL=\"$(shell echo "$(CSL_NEXT_GLIBCXX_VERSION)" | cut -d'|' -f1 | sed 's/\\//g')\" -DGLIBCXX_PATH=\"$($(private_libdir)/libstdc++.so.6)\" +LOADER_CFLAGS += -DGLIBCXX_LEAST_VERSION_SYMBOL=\"$(shell echo "$(CSL_NEXT_GLIBCXX_VERSION)" | cut -d'|' -f1 | sed 's/\\//g')\" -DGLIBCXX_PATH=\"$($(call relpath,$(build_libdir),$(build_libdir))/libstdc++.so.6)\" endif ifeq ($(OS),WINNT) From ece7a5a375edd07f7dcaed91abb3654f6d0983bc Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Thu, 20 Oct 2022 13:52:00 -0500 Subject: [PATCH 15/24] Handled make install stringreplace. --- Makefile | 7 ++++++- cli/Makefile | 2 +- cli/loader_lib.c | 23 +++++++++++------------ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/Makefile b/Makefile index e9941bcb33dc2..220599d2cdebf 100644 --- a/Makefile +++ b/Makefile @@ -232,7 +232,7 @@ endif # Note that we disable MSYS2's path munging here, as otherwise # it replaces our `:`-separated list as a `;`-separated one. define stringreplace - MSYS2_ARG_CONV_EXCL='*' $(build_depsbindir)/stringreplace $$(strings -t x - $1 | grep $2 | awk '{print $$1;}') $3 255 "$(call cygpath_w,$1)" + MSYS2_ARG_CONV_EXCL='*' $(build_depsbindir)/stringreplace $$(strings -t x - '$1' | grep '$2' | awk '{print $$1;}') '$3' 255 '$(call cygpath_w,$1)' endef @@ -382,6 +382,11 @@ else ifeq ($(JULIA_BUILD_MODE),debug) endif endif + # Replace libstdc++ path, which is also moving from `lib` to `../lib/julia`. +ifeq ($(OS),Linux) + $(call stringreplace,$(DESTDIR)$(shlibdir)/libjulia.$(JL_MAJOR_MINOR_SHLIB_EXT),\*libstdc\+\+\.so\.6$$,*$(call dep_lib_path,$(shlibdir),$(private_shlibdir)/libstdc++.so.6)) +endif + ifneq ($(LOADER_BUILD_DEP_LIBS),$(LOADER_INSTALL_DEP_LIBS)) # Next, overwrite relative path to libjulia-internal in our loader if $$(LOADER_BUILD_DEP_LIBS) != $$(LOADER_INSTALL_DEP_LIBS) diff --git a/cli/Makefile b/cli/Makefile index e78f6f74791a9..58c1f82f48662 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -13,7 +13,7 @@ LOADER_LDFLAGS = $(JLDFLAGS) -ffreestanding -L$(build_shlibdir) -L$(build_libdir ifeq ($(OS),WINNT) LOADER_CFLAGS += -municode -mconsole -nostdlib -fno-stack-check -fno-stack-protector -mno-stack-arg-probe else ifeq ($(OS),Linux) -LOADER_CFLAGS += -DGLIBCXX_LEAST_VERSION_SYMBOL=\"$(shell echo "$(CSL_NEXT_GLIBCXX_VERSION)" | cut -d'|' -f1 | sed 's/\\//g')\" -DGLIBCXX_PATH=\"$($(call relpath,$(build_libdir),$(build_libdir))/libstdc++.so.6)\" +LOADER_CFLAGS += -DGLIBCXX_LEAST_VERSION_SYMBOL=\"$(shell echo "$(CSL_NEXT_GLIBCXX_VERSION)" | cut -d'|' -f1 | sed 's/\\//g')\" endif ifeq ($(OS),WINNT) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index c6c521f35a931..b82f2c6df63fb 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -16,7 +16,7 @@ extern "C" { #endif // Save DEP_LIBS to a variable that is explicitly sized for expansion -static char dep_libs[1024] = DEP_LIBS; +static char dep_libs[1024] = "\0" DEP_LIBS; JL_DLLEXPORT void jl_loader_print_stderr(const char * msg) { @@ -239,7 +239,10 @@ static char *libstdcxxprobe(void) // Open the first available libstdc++.so. // If it can't be found, report so by exiting zero. - void *handle = dlopen("libstdc++.so.6", RTLD_LAZY); + // The star is there to prevent the compiler from merging constants + // with "\0*libstdc++.so.6", which we string replace inside the .so during + // make install. + void *handle = dlopen("libstdc++.so.6\0*", RTLD_LAZY); if (!handle) { _exit(0); } @@ -327,8 +330,8 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { const char *lib_dir = jl_get_libdir(); // Pre-load libraries that libjulia-internal needs. - int deps_len = strlen(dep_libs); - char *curr_dep = &dep_libs[0]; + int deps_len = strlen(&dep_libs[1]); + char *curr_dep = &dep_libs[1]; void *cxx_handle; @@ -337,9 +340,9 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { int done_probe = 0; char *probevar = getenv("JULIA_PROBE_LIBSTDCXX"); if (probevar) { - if (strcmp(probevar, "1") == 0 || strcmp(probevar, "yes")) + if (strcmp(probevar, "1") == 0 || strcmp(probevar, "yes") == 0) do_probe = 1; - else if (strcmp(probevar, "0") == 0 || strcmp(probevar, "no")) + else if (strcmp(probevar, "0") == 0 || strcmp(probevar, "no") == 0) do_probe = 0; } if (do_probe) { @@ -357,13 +360,9 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { } } if (!done_probe) { -#ifndef GLIBCXX_PATH -#define GLIBCXX_PATH "$(private_libdir)/usr/lib/julia/libstdc++.so.6" -#warning GLIBCXX_PATH is meant to be specified on the command line -#endif - load_library(GLIBCXX_PATH, lib_dir, 1); + const static char bundled_path[256] = "\0*libstdc++.so.6"; + load_library(&bundled_path[2], lib_dir, 1); } - puts(GLIBCXX_PATH); #endif // We keep track of "special" libraries names (ones whose name is prefixed with `@`) From 2788bc04c05e8c6a40d9bb9369ab27c7291c6edf Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Thu, 20 Oct 2022 14:09:09 -0500 Subject: [PATCH 16/24] Correctly quoted stringreplace. --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 220599d2cdebf..0c0cb1e467ca4 100644 --- a/Makefile +++ b/Makefile @@ -232,7 +232,7 @@ endif # Note that we disable MSYS2's path munging here, as otherwise # it replaces our `:`-separated list as a `;`-separated one. define stringreplace - MSYS2_ARG_CONV_EXCL='*' $(build_depsbindir)/stringreplace $$(strings -t x - '$1' | grep '$2' | awk '{print $$1;}') '$3' 255 '$(call cygpath_w,$1)' + MSYS2_ARG_CONV_EXCL='*' $(build_depsbindir)/stringreplace $$(strings -t x - '$1' | grep "$2" | awk '{print $$1;}') "$3" 255 "$(call cygpath_w,$1)" endef @@ -384,7 +384,7 @@ endif # Replace libstdc++ path, which is also moving from `lib` to `../lib/julia`. ifeq ($(OS),Linux) - $(call stringreplace,$(DESTDIR)$(shlibdir)/libjulia.$(JL_MAJOR_MINOR_SHLIB_EXT),\*libstdc\+\+\.so\.6$$,*$(call dep_lib_path,$(shlibdir),$(private_shlibdir)/libstdc++.so.6)) + $(call stringreplace,$(DESTDIR)$(shlibdir)/libjulia.$(JL_MAJOR_MINOR_SHLIB_EXT),\*libstdc++\.so\.6$$,*$(call dep_lib_path,$(shlibdir),$(private_shlibdir)/libstdc++.so.6)) endif From c4a95e7e41fb5da37fb6266c235f51e0478a96ec Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Mon, 24 Oct 2022 13:17:47 -0500 Subject: [PATCH 17/24] Added -Wl,--enable-new-dtags to prevent DT_RPATH for transitive dependencies --- Make.inc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/Make.inc b/Make.inc index d011290d2af9d..4d8e7aa32473d 100644 --- a/Make.inc +++ b/Make.inc @@ -1207,18 +1207,16 @@ ifneq (,$(filter $(OS),WINNT emscripten)) RPATH := RPATH_ORIGIN := RPATH_ESCAPED_ORIGIN := - RPATH_LIB := else ifeq ($(OS), Darwin) RPATH := -Wl,-rpath,'@executable_path/$(build_libdir_rel)' RPATH_ORIGIN := -Wl,-rpath,'@loader_path/' RPATH_ESCAPED_ORIGIN := $(RPATH_ORIGIN) - RPATH_LIB := -Wl,-rpath,'@loader_path/' else - RPATH := -Wl,-rpath,'$$ORIGIN/$(build_libdir_rel)' -Wl,-rpath,'$$ORIGIN/$(build_private_libdir_rel)' -Wl,-rpath-link,$(build_shlibdir) -Wl,-z,origin - RPATH_ORIGIN := -Wl,-rpath,'$$ORIGIN' -Wl,-z,origin - RPATH_ESCAPED_ORIGIN := -Wl,-rpath,'\$$\$$ORIGIN' -Wl,-z,origin -Wl,-rpath-link,$(build_shlibdir) - RPATH_LIB := -Wl,-rpath,'$$ORIGIN/' -Wl,-z,origin + RPATH := -Wl,-rpath,'$$ORIGIN/$(build_libdir_rel)' -Wl,-rpath,'$$ORIGIN/$(build_private_libdir_rel)' -Wl,-rpath-link,$(build_shlibdir) -Wl,-z,origin -Wl,--enable-new-dtags + RPATH_ORIGIN := -Wl,-rpath,'$$ORIGIN' -Wl,-z,origin -Wl,--enable-new-dtags + RPATH_ESCAPED_ORIGIN := -Wl,-rpath,'\$$\$$ORIGIN' -Wl,-z,origin -Wl,-rpath-link,$(build_shlibdir) -Wl,--enable-new-dtags endif +RPATH_LIB := $(RPATH_ORIGIN) # --whole-archive ifeq ($(OS), Darwin) From ca967aad24b030bdd8a7275e1bf13449e0aad9ee Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Mon, 24 Oct 2022 13:25:41 -0500 Subject: [PATCH 18/24] Updated news entry. --- NEWS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index db30021099233..c18bf2439c939 100644 --- a/NEWS.md +++ b/NEWS.md @@ -183,7 +183,8 @@ Deprecated or removed External dependencies --------------------- - +* On linux, now autodetects the system libstdc++ version, and automatically loads the system library if it is newer. +* Removed `RPATH` from the julia binary. On Linux this may break libraries that have failed to set `RUNPATH`. Tooling Improvements --------------------- From 4bf5699bf0c7abc9af858991a3b4020daf1afe86 Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Mon, 24 Oct 2022 13:44:35 -0500 Subject: [PATCH 19/24] Added comment about environment variable. --- NEWS.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS.md b/NEWS.md index c18bf2439c939..4567cfaf3d398 100644 --- a/NEWS.md +++ b/NEWS.md @@ -183,7 +183,7 @@ Deprecated or removed External dependencies --------------------- -* On linux, now autodetects the system libstdc++ version, and automatically loads the system library if it is newer. +* On linux, now autodetects the system libstdc++ version, and automatically loads the system library if it is newer. The old behavior of loading the bundled libstdc++ regardless of the system version obtained by setting the environment variable `JULIA_PROBE_LIBSTDCXX=0`. * Removed `RPATH` from the julia binary. On Linux this may break libraries that have failed to set `RUNPATH`. Tooling Improvements From 3314c163b15f8d3e8800fadb9d49d60493a1fa0f Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Wed, 26 Oct 2022 15:12:31 -0500 Subject: [PATCH 20/24] patched rpath for libgfortran and libLLVM. --- Makefile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Makefile b/Makefile index 0c0cb1e467ca4..4fb36895d03e8 100644 --- a/Makefile +++ b/Makefile @@ -382,6 +382,12 @@ else ifeq ($(JULIA_BUILD_MODE),debug) endif endif + # Fix rpaths for dependencies. This should be fixed in BinaryBuilder later. +ifeq ($(OS), Linux) + -$(PATCHELF) --set-rpath '$$ORIGIN' $(DESTDIR)$(private_shlibdir)/libgfortran.$(SHLIB_EXT) + -$(PATCHELF) --set-rpath '$$ORIGIN' $(DESTDIR)$(private_shlibdir)/libLLVM.$(SHLIB_EXT) +endif + # Replace libstdc++ path, which is also moving from `lib` to `../lib/julia`. ifeq ($(OS),Linux) $(call stringreplace,$(DESTDIR)$(shlibdir)/libjulia.$(JL_MAJOR_MINOR_SHLIB_EXT),\*libstdc++\.so\.6$$,*$(call dep_lib_path,$(shlibdir),$(private_shlibdir)/libstdc++.so.6)) From 794e7a7821a6ad99c8cd02e177721013e012ac15 Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Fri, 28 Oct 2022 17:41:42 -0500 Subject: [PATCH 21/24] Added explaination to Make.inc --- Make.inc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Make.inc b/Make.inc index 4d8e7aa32473d..a4c2b3ea113e5 100644 --- a/Make.inc +++ b/Make.inc @@ -1149,6 +1149,26 @@ BB_TRIPLET := $(subst $(SPACE),-,$(filter-out cxx%,$(filter-out libgfortran%,$(s LIBGFORTRAN_VERSION := $(subst libgfortran,,$(filter libgfortran%,$(subst -,$(SPACE),$(BB_TRIPLET_LIBGFORTRAN)))) +# CSL_NEXT_GLIBCXX_VERSION is a triple of the symbols representing support for whatever +# the next libstdc++ version would be. This is used for two things. +# 1. Whether the system libraries are new enough, if we need to use the libs bundled with CSL +# 2. To know which libstdc++ to load at runtime +# We want whichever libstdc++ library is newer, because if we don't it can cause problems. +# While what CSL bundles is quite bleeding-edge compared to what most distros shop, if someone +# tries to build an older branch of Julia, the version of CSL that ships with it may become +# relatively old. This is not a problem for code that is built in BB, but when we build Julia +# with the system compiler, that compiler uses the version of `libstdc++` that it is bundled +# with, and we can get linker errors when trying to run that `julia` executable with the +# `libstdc++` that comes from the (now old) BB-built CSL. +# To fix this, we take note when the system `libstdc++.so` is newer than whatever we +# would get from CSL (by searching for a `GLIBCXX_X.Y.Z` symbol that does not exist +# in our CSL, but would in a newer one), and default to `USE_BINARYBUILDER_CSL=0` in +# this case. This ensures that we link against a version with the symbols required. +# We also check the system libstdc++ at runtime in the cli loader library, and +# load it if it contains the version symbol that indicates than it is newer than the one +# shipped with CSL. Although we do not depend on any of the symbols, it is entirely +# possible that a user might choose to install a library which depends on symbols provided +# by a newer libstdc++. Without runtime detection, those libraries would break. CSL_NEXT_GLIBCXX_VERSION=GLIBCXX_3\.4\.30|GLIBCXX_3\.5\.|GLIBCXX_4\. # This is the set of projects that BinaryBuilder dependencies are hooked up for. From 483a081b25f9752c4b90c3760136e04e6ff692a7 Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Fri, 28 Oct 2022 19:31:17 -0500 Subject: [PATCH 22/24] Removed trailing space --- Make.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Make.inc b/Make.inc index a4c2b3ea113e5..1441e849235c7 100644 --- a/Make.inc +++ b/Make.inc @@ -1166,7 +1166,7 @@ LIBGFORTRAN_VERSION := $(subst libgfortran,,$(filter libgfortran%,$(subst -,$(SP # this case. This ensures that we link against a version with the symbols required. # We also check the system libstdc++ at runtime in the cli loader library, and # load it if it contains the version symbol that indicates than it is newer than the one -# shipped with CSL. Although we do not depend on any of the symbols, it is entirely +# shipped with CSL. Although we do not depend on any of the symbols, it is entirely # possible that a user might choose to install a library which depends on symbols provided # by a newer libstdc++. Without runtime detection, those libraries would break. CSL_NEXT_GLIBCXX_VERSION=GLIBCXX_3\.4\.30|GLIBCXX_3\.5\.|GLIBCXX_4\. From 2c9a17ec6705ac67cf909d25dc2e0ddc18d42e65 Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Mon, 31 Oct 2022 16:16:22 -0500 Subject: [PATCH 23/24] Removed patchelf for libgfortran, now that BB has been fixed. --- Makefile | 1 - 1 file changed, 1 deletion(-) diff --git a/Makefile b/Makefile index 4fb36895d03e8..15a8cd1c855f9 100644 --- a/Makefile +++ b/Makefile @@ -384,7 +384,6 @@ endif # Fix rpaths for dependencies. This should be fixed in BinaryBuilder later. ifeq ($(OS), Linux) - -$(PATCHELF) --set-rpath '$$ORIGIN' $(DESTDIR)$(private_shlibdir)/libgfortran.$(SHLIB_EXT) -$(PATCHELF) --set-rpath '$$ORIGIN' $(DESTDIR)$(private_shlibdir)/libLLVM.$(SHLIB_EXT) endif From f675f1720cbf8546d0ca4975ce6597a0c7b3eee2 Mon Sep 17 00:00:00 2001 From: apaz-cli Date: Wed, 2 Nov 2022 11:10:02 -0500 Subject: [PATCH 24/24] Fixed typos and comments Co-authored-by: Max Horn --- Make.inc | 6 +++--- NEWS.md | 2 +- cli/loader_lib.c | 5 +++-- deps/csl.mk | 6 +++--- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Make.inc b/Make.inc index 1441e849235c7..a4e97bcec0b48 100644 --- a/Make.inc +++ b/Make.inc @@ -1154,8 +1154,8 @@ LIBGFORTRAN_VERSION := $(subst libgfortran,,$(filter libgfortran%,$(subst -,$(SP # 1. Whether the system libraries are new enough, if we need to use the libs bundled with CSL # 2. To know which libstdc++ to load at runtime # We want whichever libstdc++ library is newer, because if we don't it can cause problems. -# While what CSL bundles is quite bleeding-edge compared to what most distros shop, if someone -# tries to build an older branch of Julia, the version of CSL that ships with it may become +# While what CSL bundles is quite bleeding-edge compared to what most distros ship, if someone +# tries to build an older branch of Julia, the version of CSL that ships with it may be # relatively old. This is not a problem for code that is built in BB, but when we build Julia # with the system compiler, that compiler uses the version of `libstdc++` that it is bundled # with, and we can get linker errors when trying to run that `julia` executable with the @@ -1165,7 +1165,7 @@ LIBGFORTRAN_VERSION := $(subst libgfortran,,$(filter libgfortran%,$(subst -,$(SP # in our CSL, but would in a newer one), and default to `USE_BINARYBUILDER_CSL=0` in # this case. This ensures that we link against a version with the symbols required. # We also check the system libstdc++ at runtime in the cli loader library, and -# load it if it contains the version symbol that indicates than it is newer than the one +# load it if it contains the version symbol that indicates that it is newer than the one # shipped with CSL. Although we do not depend on any of the symbols, it is entirely # possible that a user might choose to install a library which depends on symbols provided # by a newer libstdc++. Without runtime detection, those libraries would break. diff --git a/NEWS.md b/NEWS.md index 4567cfaf3d398..737511c8931c2 100644 --- a/NEWS.md +++ b/NEWS.md @@ -183,7 +183,7 @@ Deprecated or removed External dependencies --------------------- -* On linux, now autodetects the system libstdc++ version, and automatically loads the system library if it is newer. The old behavior of loading the bundled libstdc++ regardless of the system version obtained by setting the environment variable `JULIA_PROBE_LIBSTDCXX=0`. +* On Linux, now autodetects the system libstdc++ version, and automatically loads the system library if it is newer. The old behavior of loading the bundled libstdc++ regardless of the system version obtained by setting the environment variable `JULIA_PROBE_LIBSTDCXX=0`. * Removed `RPATH` from the julia binary. On Linux this may break libraries that have failed to set `RUNPATH`. Tooling Improvements diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 0619b9d646c45..fc057b52508f2 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -169,7 +169,7 @@ JL_DLLEXPORT const char * jl_get_libdir() // On Linux, it can happen that the system has a newer libstdc++ than the one we ship, // which can break loading of some system libraries: . // As a fix, on linux we probe the system libstdc++ to see if it is newer, and then load it if it is. -// Otherwise, we load the packaged one. This improves compatibility with third party dynamic libs that +// Otherwise, we load the bundled one. This improves compatibility with third party dynamic libs that // may depend on symbols exported by the system libstdxc++. #ifdef _OS_LINUX_ #ifndef GLIBCXX_LEAST_VERSION_SYMBOL @@ -180,6 +180,7 @@ JL_DLLEXPORT const char * jl_get_libdir() #include #include +// write(), but handle errors and avoid EINTR static void write_wrapper(int fd, const char *str, size_t len) { size_t written_sofar = 0; @@ -195,7 +196,7 @@ static void write_wrapper(int fd, const char *str, size_t len) } } -// Where the buf passed in was malloced +// read(), but handle errors and avoid EINTR static void read_wrapper(int fd, char **ret, size_t *ret_len) { // Allocate an initial buffer diff --git a/deps/csl.mk b/deps/csl.mk index ff500620d18fd..dd782880d21fd 100644 --- a/deps/csl.mk +++ b/deps/csl.mk @@ -12,10 +12,10 @@ endef # CSL bundles lots of system compiler libraries, and while it is quite bleeding-edge # as compared to what most distros ship, if someone tries to build an older branch, -# the version of CSL that ships with that branch may become relatively old. This is -# not a problem for code that is built in BB, but when we build Julia with the system +# the version of CSL that ships with that branch may be relatively old. This is not +# a problem for code that is built in BB, but when we build Julia with the system # compiler, that compiler uses the version of `libstdc++` that it is bundled with, -# and we can get linker errors when trying to run that `julia` executable with the +# and we can get linker errors when trying to run that `julia` executable with the # `libstdc++` that comes from the (now old) BB-built CSL. # # To fix this, we take note when the system `libstdc++.so` is newer than whatever we