Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move libstdc++ path into LOADER_*_DEP_LIBS #48342

Merged
merged 2 commits into from
Jan 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 55 additions & 6 deletions Make.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1465,7 +1465,7 @@ JULIA_SYSIMG_release := $(build_private_libdir)/sys.$(SHLIB_EXT)
JULIA_SYSIMG := $(JULIA_SYSIMG_$(JULIA_BUILD_MODE))

define dep_lib_path
$$($(PYTHON) $(call python_cygpath,$(JULIAHOME)/contrib/relative_path.py) $(1) $(2))
$(shell $(PYTHON) $(call python_cygpath,$(JULIAHOME)/contrib/relative_path.py) $(1) $(2))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This slows things down a bit (adds ~60ms to every make invocation on my linux desktop) but I think it's worth it for the extra clarity.

endef

LIBJULIAINTERNAL_BUILD_DEPLIB := $(call dep_lib_path,$(build_libdir),$(build_shlibdir)/libjulia-internal.$(JL_MAJOR_SHLIB_EXT))
Expand Down Expand Up @@ -1512,6 +1512,19 @@ LIBGCC_BUILD_DEPLIB := $(call dep_lib_path,$(build_libdir),$(build_shlibdir)/$(L
endif
LIBGCC_INSTALL_DEPLIB := $(call dep_lib_path,$(libdir),$(private_shlibdir)/$(LIBGCC_NAME))

# We only bother to define this on Linux, as that's the only platform that does libstdc++ probing
# On all other platforms, the LIBSTDCXX_*_DEPLIB variables will be empty.
ifeq ($(OS),Linux)
LIBSTDCXX_NAME := libstdc++.so.6
ifeq ($(USE_SYSTEM_CSL),1)
LIBSTDCXX_BUILD_DEPLIB := $(call dep_lib_path,$(build_libdir),$(build_private_shlibdir)/$(LIBSTDCXX_NAME))
else
LIBSTDCXX_BUILD_DEPLIB := $(call dep_lib_path,$(build_libdir),$(build_shlibdir)/$(LIBSTDCXX_NAME))
endif
LIBSTDCXX_INSTALL_DEPLIB := $(call dep_lib_path,$(libdir),$(private_shlibdir)/$(LIBSTDCXX_NAME))
endif


# USE_SYSTEM_LIBM and USE_SYSTEM_OPENLIBM causes it to get symlinked into build_private_shlibdir
ifeq ($(USE_SYSTEM_LIBM),1)
LIBM_BUILD_DEPLIB := $(call dep_lib_path,$(build_libdir),$(build_private_shlibdir)/$(LIBMNAME).$(SHLIB_EXT))
Expand All @@ -1525,6 +1538,8 @@ LIBM_INSTALL_DEPLIB := $(call dep_lib_path,$(libdir),$(private_shlibdir)/$(LIBMN
# We list:
# * libgcc_s, because FreeBSD needs to load ours, not the system one.
# * libopenlibm, because Windows has an untrustworthy libm, and we want to use ours more than theirs
# * libstdc++, because while performing `libstdc++` probing we need to
# know the path to the bundled `libstdc++` library.
# * libjulia-internal, which must always come second-to-last.
# * libjulia-codegen, which must always come last
#
Expand All @@ -1533,11 +1548,45 @@ LIBM_INSTALL_DEPLIB := $(call dep_lib_path,$(libdir),$(private_shlibdir)/$(LIBMN
# * install time relative paths are not equal to build time relative paths (../lib vs. ../lib/julia)
# That second point will no longer be true for most deps once they are placed within Artifacts directories.
# Note that we prefix `libjulia-codegen` and `libjulia-internal` with `@` to signify to the loader that it
# should not automatically dlopen() it in its loading loop.
LOADER_BUILD_DEP_LIBS = $(LIBGCC_BUILD_DEPLIB):$(LIBM_BUILD_DEPLIB):@$(LIBJULIAINTERNAL_BUILD_DEPLIB):@$(LIBJULIACODEGEN_BUILD_DEPLIB):
LOADER_DEBUG_BUILD_DEP_LIBS = $(LIBGCC_BUILD_DEPLIB):$(LIBM_BUILD_DEPLIB):@$(LIBJULIAINTERNAL_DEBUG_BUILD_DEPLIB):@$(LIBJULIACODEGEN_DEBUG_BUILD_DEPLIB):
LOADER_INSTALL_DEP_LIBS = $(LIBGCC_INSTALL_DEPLIB):$(LIBM_INSTALL_DEPLIB):@$(LIBJULIAINTERNAL_INSTALL_DEPLIB):@$(LIBJULIACODEGEN_INSTALL_DEPLIB):
LOADER_DEBUG_INSTALL_DEP_LIBS = $(LIBGCC_INSTALL_DEPLIB):$(LIBM_INSTALL_DEPLIB):@$(LIBJULIAINTERNAL_DEBUG_INSTALL_DEPLIB):@$(LIBJULIACODEGEN_DEBUG_INSTALL_DEPLIB):
# should not automatically dlopen() it in its loading loop, it is "special" and should happen later.
# We do the same for `libstdc++`, and explicitly place it _after_ `libgcc_s`, and `libm` since `libstdc++`
# may depend on those libraries (e.g. when USE_SYSTEM_LIBM=1)

# Helper function to join a list with colons, then place an extra at the end.
define build_deplibs
$(subst $(SPACE),:,$(strip $(1))):
endef

LOADER_BUILD_DEP_LIBS = $(call build_deplibs, \
$(LIBGCC_BUILD_DEPLIB) \
$(LIBM_BUILD_DEPLIB) \
@$(LIBSTDCXX_BUILD_DEPLIB) \
@$(LIBJULIAINTERNAL_BUILD_DEPLIB) \
@$(LIBJULIACODEGEN_BUILD_DEPLIB) \
)

LOADER_DEBUG_BUILD_DEP_LIBS = $(call build_deplibs, \
$(LIBGCC_BUILD_DEPLIB) \
$(LIBM_BUILD_DEPLIB) \
@$(LIBSTDCXX_BUILD_DEPLIB) \
@$(LIBJULIAINTERNAL_DEBUG_BUILD_DEPLIB) \
@$(LIBJULIACODEGEN_DEBUG_BUILD_DEPLIB) \
)

LOADER_INSTALL_DEP_LIBS = $(call build_deplibs, \
$(LIBGCC_INSTALL_DEPLIB) \
$(LIBM_INSTALL_DEPLIB) \
@$(LIBSTDCXX_INSTALL_DEPLIB) \
@$(LIBJULIAINTERNAL_INSTALL_DEPLIB) \
@$(LIBJULIACODEGEN_INSTALL_DEPLIB) \
)
LOADER_DEBUG_INSTALL_DEP_LIBS = $(call build_deplibs, \
$(LIBGCC_INSTALL_DEPLIB) \
$(LIBM_INSTALL_DEPLIB) \
@$(LIBSTDCXX_INSTALL_DEPLIB) \
@$(LIBJULIAINTERNAL_DEBUG_INSTALL_DEPLIB) \
@$(LIBJULIACODEGEN_DEBUG_INSTALL_DEPLIB) \
)

# Colors for make
ifndef VERBOSE
Expand Down
6 changes: 0 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -425,12 +425,6 @@ ifeq ($(OS), Linux)
-$(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))
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)
ifeq ($(JULIA_BUILD_MODE),release)
Expand Down
130 changes: 83 additions & 47 deletions cli/loader_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,8 @@ static char *libstdcxxprobe(void)
}
#endif

void * libjulia_internal = NULL;
void *libjulia_internal = NULL;
void *libjulia_codegen = NULL;
__attribute__((constructor)) void jl_load_libjulia_internal(void) {
// Only initialize this once
if (libjulia_internal != NULL) {
Expand All @@ -364,43 +365,46 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) {
int deps_len = strlen(&dep_libs[1]);
char *curr_dep = &dep_libs[1];

void *cxx_handle;
// 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.
// The current list is:
// libstdc++
// libjulia-internal
// libjulia-codegen
const int NUM_SPECIAL_LIBRARIES = 3;
int special_idx = 0;
while (1) {
// try to find next colon character; if we can't, break out
char * colon = strchr(curr_dep, ':');
if (colon == NULL)
break;

#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") == 0)
do_probe = 1;
else if (strcmp(probevar, "0") == 0 || strcmp(probevar, "no") == 0)
do_probe = 0;
}
if (do_probe) {
char *cxxpath = libstdcxxprobe();
if (cxxpath) {
cxx_handle = dlopen(cxxpath, RTLD_LAZY);
char *dlr = dlerror();
if (dlr) {
jl_loader_print_stderr("ERROR: Unable to dlopen(cxxpath) in parent!\n");
jl_loader_print_stderr3("Message: ", dlr, "\n");
// If this library name starts with `@`, don't open it here (but mark it as special)
if (curr_dep[0] == '@') {
special_idx += 1;
if (special_idx > NUM_SPECIAL_LIBRARIES) {
jl_loader_print_stderr("ERROR: Too many special library names specified, check LOADER_BUILD_DEP_LIBS and friends!\n");
exit(1);
}
free(cxxpath);
done_probe = 1;
}

// Skip to next dep
curr_dep = colon + 1;
}
if (!done_probe) {
const static char bundled_path[256] = "\0*libstdc++.so.6";
load_library(&bundled_path[2], lib_dir, 1);

// Assert that we have exactly the right number of special library names
if (special_idx != NUM_SPECIAL_LIBRARIES) {
jl_loader_print_stderr("ERROR: Too few special library names specified, check LOADER_BUILD_DEP_LIBS and friends!\n");
exit(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
// `libjulia-internal` or `libjulia-codegen`.
int special_idx = 0;
char * special_library_names[2] = {NULL};
// Now that we've asserted that we have the right number of special
// libraries, actually run a loop over the deps loading them in-order.
// If it's a special library, we do slightly different things, especially
// for libstdc++, where we actually probe for a system libstdc++ and
// load that if it's newer.
special_idx = 0;
curr_dep = &dep_libs[1];
while (1) {
// try to find next colon character; if we can't, break out
char * colon = strchr(curr_dep, ':');
Expand All @@ -410,31 +414,63 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) {
// Chop the string at the colon so it's a valid-ending-string
*colon = '\0';

// If this library name starts with `@`, don't open it here (but mark it as special)
// If this library name starts with `@`, it's a special library
// and requires special handling:
if (curr_dep[0] == '@') {
if (special_idx > sizeof(special_library_names)/sizeof(char *)) {
jl_loader_print_stderr("ERROR: Too many special library names specified, check LOADER_BUILD_DEP_LIBS and friends!\n");
exit(1);
// Skip the `@` for future function calls.
curr_dep += 1;

// First special library to be loaded is `libstdc++`; perform probing here.
if (special_idx == 0) {
#if defined(_OS_LINUX_)
int do_probe = 1;
int probe_successful = 0;

// Check to see if the user has disabled libstdc++ probing
char *probevar = getenv("JULIA_PROBE_LIBSTDCXX");
if (probevar) {
if (strcmp(probevar, "1") == 0 || strcmp(probevar, "yes") == 0)
do_probe = 1;
else if (strcmp(probevar, "0") == 0 || strcmp(probevar, "no") == 0)
do_probe = 0;
}
if (do_probe) {
char *cxxpath = libstdcxxprobe();
if (cxxpath) {
void *cxx_handle = dlopen(cxxpath, RTLD_LAZY);
const char *dlr = dlerror();
if (dlr) {
jl_loader_print_stderr("ERROR: Unable to dlopen(cxxpath) in parent!\n");
jl_loader_print_stderr3("Message: ", dlr, "\n");
exit(1);
}
free(cxxpath);
probe_successful = 1;
}
}
// If the probe rejected the system libstdc++ (or didn't find one!)
// just load our bundled libstdc++ as identified by curr_dep;
if (!probe_successful) {
load_library(curr_dep, lib_dir, 1);
}
#endif
} else if (special_idx == 1) {
// This special library is `libjulia-internal`
libjulia_internal = load_library(curr_dep, lib_dir, 1);
} else if (special_idx == 2) {
// This special library is `libjulia-codegen`
libjulia_codegen = load_library(curr_dep, lib_dir, 0);
}
special_library_names[special_idx] = curr_dep + 1;
special_idx += 1;
}
else {
special_idx++;
} else {
// Otherwise, just load it as "normal"
load_library(curr_dep, lib_dir, 1);
}

// Skip ahead to next dependency
curr_dep = colon + 1;
}

if (special_idx != sizeof(special_library_names)/sizeof(char *)) {
jl_loader_print_stderr("ERROR: Too few special library names specified, check LOADER_BUILD_DEP_LIBS and friends!\n");
exit(1);
}

// Unpack our special library names. This is why ordering of library names matters.
libjulia_internal = load_library(special_library_names[0], lib_dir, 1);
void *libjulia_codegen = load_library(special_library_names[1], lib_dir, 0);
const char * const * codegen_func_names;
const char *codegen_liberr;
if (libjulia_codegen == NULL) {
Expand Down