diff --git a/Make.inc b/Make.inc index e0bc8ba224a6d..1685142e691a8 100644 --- a/Make.inc +++ b/Make.inc @@ -65,6 +65,7 @@ USE_SYSTEM_LIBGIT2:=0 USE_SYSTEM_PATCHELF:=0 USE_SYSTEM_LIBWHICH:=0 USE_SYSTEM_ZLIB:=0 +USE_SYSTEM_ZSTD:=0 USE_SYSTEM_P7ZIP:=0 USE_SYSTEM_LLD:=0 @@ -1201,6 +1202,14 @@ else UTF8PROC_INC := $(build_includedir) endif +ifeq ($(USE_SYSTEM_ZSTD), 1) + LIBZSTD := -lzstd + ZSTD_INC := $(LOCALBASE)/include +else + LIBZSTD := -L$(build_shlibdir) -lzstd + ZSTD_INC := $(build_includedir) +endif + # We need python for things like BB triplet recognition. We don't really care # about version, generally, so just find something that works: PYTHON := $(shell which python 2>/dev/null || which python3 2>/dev/null || which python2 2>/dev/null || echo not found) @@ -1268,7 +1277,7 @@ CSL_NEXT_GLIBCXX_VERSION=GLIBCXX_3\.4\.33|GLIBCXX_3\.5\.|GLIBCXX_4\. # 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 # or not. See `deps/csl.mk` for more detail. -BB_PROJECTS := BLASTRAMPOLINE OPENBLAS LLVM LIBSUITESPARSE OPENLIBM GMP MBEDTLS LIBSSH2 NGHTTP2 MPFR CURL LIBGIT2 PCRE LIBUV LIBUNWIND DSFMT OBJCONV ZLIB P7ZIP LLD LIBTRACYCLIENT +BB_PROJECTS := BLASTRAMPOLINE OPENBLAS LLVM LIBSUITESPARSE OPENLIBM GMP MBEDTLS LIBSSH2 NGHTTP2 MPFR CURL LIBGIT2 PCRE LIBUV LIBUNWIND DSFMT OBJCONV ZLIB ZSTD P7ZIP LLD LIBTRACYCLIENT define SET_BB_DEFAULT # First, check to see if BB is disabled on a global setting ifeq ($$(USE_BINARYBUILDER),0) @@ -1397,7 +1406,7 @@ JLDFLAGS += -Wl,--stack,8388608 --disable-auto-import --disable-runtime-pseudo-r ifeq ($(ARCH),i686) JLDFLAGS += -Wl,--large-address-aware endif -JCPPFLAGS += -D_WIN32_WINNT=0x0502 +JCPPFLAGS += -D_WIN32_WINNT=_WIN32_WINNT_WIN8 UNTRUSTED_SYSTEM_LIBM := 1 # Use hard links for files on windows, rather than soft links # https://stackoverflow.com/questions/3648819/how-to-make-a-symbolic-link-with-cygwin-in-windows-7 diff --git a/Makefile b/Makefile index 05683fda0a004..bdc05d75f9a2c 100644 --- a/Makefile +++ b/Makefile @@ -225,6 +225,7 @@ JL_PRIVATE_LIBS-$(USE_SYSTEM_ZLIB) += zlib else JL_PRIVATE_LIBS-$(USE_SYSTEM_ZLIB) += libz endif +JL_PRIVATE_LIBS-$(USE_SYSTEM_ZSTD) += libzstd ifeq ($(USE_LLVM_SHLIB),1) JL_PRIVATE_LIBS-$(USE_SYSTEM_LLVM) += libLLVM $(LLVM_SHARED_LIB_NAME) endif diff --git a/THIRDPARTY.md b/THIRDPARTY.md index 412b84b688758..0b2982043d431 100644 --- a/THIRDPARTY.md +++ b/THIRDPARTY.md @@ -55,6 +55,7 @@ Julia bundles the following external programs and libraries: - [7-Zip](https://www.7-zip.org/license.txt) - [ZLIB](https://zlib.net/zlib_license.html) +- [ZSTD](https://github.com/facebook/zstd/blob/v1.5.7/LICENSE) On some platforms, distributions of Julia contain SSL certificate authority certificates, released under the [Mozilla Public License](https://en.wikipedia.org/wiki/Mozilla_Public_License). diff --git a/base/options.jl b/base/options.jl index 18fa2ad92654d..0465947801223 100644 --- a/base/options.jl +++ b/base/options.jl @@ -58,6 +58,7 @@ struct JLOptions strip_ir::Int8 permalloc_pkgimg::Int8 heap_size_hint::UInt64 + compress_sysimage::Int8 end # This runs early in the sysimage != is not defined yet diff --git a/base/util.jl b/base/util.jl index 3a621211162ec..f233e6f1d234d 100644 --- a/base/util.jl +++ b/base/util.jl @@ -245,6 +245,9 @@ function julia_cmd(julia=joinpath(Sys.BINDIR, julia_exename()); cpu_target::Unio if opts.use_sysimage_native_code == 0 push!(addflags, "--sysimage-native-code=no") end + if opts.compress_sysimage == 1 + push!(addflags, "--compress-sysimage=yes") + end return `$julia -C $cpu_target -J$image_file $addflags` end diff --git a/contrib/refresh_checksums.mk b/contrib/refresh_checksums.mk index f67088141ccd4..506f524062538 100644 --- a/contrib/refresh_checksums.mk +++ b/contrib/refresh_checksums.mk @@ -24,7 +24,7 @@ CLANG_TRIPLETS=$(filter %-darwin %-freebsd,$(TRIPLETS)) NON_CLANG_TRIPLETS=$(filter-out %-darwin %-freebsd,$(TRIPLETS)) # These are the projects currently using BinaryBuilder; both GCC-expanded and non-GCC-expanded: -BB_PROJECTS=mbedtls libssh2 nghttp2 mpfr curl libgit2 pcre libuv unwind llvmunwind dsfmt objconv p7zip zlib libsuitesparse openlibm blastrampoline libtracyclient +BB_PROJECTS=mbedtls libssh2 nghttp2 mpfr curl libgit2 pcre libuv unwind llvmunwind dsfmt objconv p7zip zlib zstd libsuitesparse openlibm blastrampoline libtracyclient BB_GCC_EXPANDED_PROJECTS=openblas csl BB_CXX_EXPANDED_PROJECTS=gmp llvm clang llvm-tools lld # These are non-BB source-only deps diff --git a/deps/Makefile b/deps/Makefile index 6a1b03eec919e..e942262cf6f0e 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -70,10 +70,12 @@ endif endif endif +PATCHELF_MANIFEST := ifneq (,$(findstring $(OS),Linux FreeBSD)) ifeq ($(USE_SYSTEM_PATCHELF), 0) DEP_LIBS += patchelf PATCHELF:=$(build_depsbindir)/patchelf +PATCHELF_MANIFEST:=$(build_prefix)/manifest/patchelf else PATCHELF:=patchelf endif @@ -155,6 +157,10 @@ ifeq ($(USE_SYSTEM_ZLIB), 0) DEP_LIBS += zlib endif +ifeq ($(USE_SYSTEM_ZSTD), 0) +DEP_LIBS += zstd +endif + ifeq ($(USE_SYSTEM_P7ZIP), 0) DEP_LIBS += p7zip endif @@ -195,7 +201,7 @@ DEP_LIBS_STAGED := $(DEP_LIBS) # list all targets DEP_LIBS_STAGED_ALL := llvm llvm-tools clang llvmunwind unwind libuv pcre \ openlibm dsfmt blastrampoline openblas lapack gmp mpfr patchelf utf8proc \ - objconv mbedtls libssh2 nghttp2 curl libgit2 libwhich zlib p7zip csl \ + objconv mbedtls libssh2 nghttp2 curl libgit2 libwhich zlib zstd p7zip csl \ sanitizers libsuitesparse lld libtracyclient ittapi JuliaSyntax terminfo DEP_LIBS_ALL := $(DEP_LIBS_STAGED_ALL) @@ -244,6 +250,7 @@ include $(SRCDIR)/openblas.mk include $(SRCDIR)/utf8proc.mk include $(SRCDIR)/libsuitesparse.mk include $(SRCDIR)/zlib.mk +include $(SRCDIR)/zstd.mk include $(SRCDIR)/unwind.mk include $(SRCDIR)/gmp.mk include $(SRCDIR)/mpfr.mk diff --git a/deps/checksums/zstd b/deps/checksums/zstd new file mode 100644 index 0000000000000..aea151b266966 --- /dev/null +++ b/deps/checksums/zstd @@ -0,0 +1,38 @@ +Zstd.v1.5.7+1.aarch64-apple-darwin.tar.gz/md5/d6b2fb32d705078dbc369986ac8b056b +Zstd.v1.5.7+1.aarch64-apple-darwin.tar.gz/sha512/5dfcf36087ce8540b1f6a04181adee962e2164a763e758ac5cc256c332756774b381ca58e26641a15ce555d59641690a6da72a67bf935d8611734f2006bde504 +Zstd.v1.5.7+1.aarch64-linux-gnu.tar.gz/md5/0c627ec83e426383c25eb4bc297f3548 +Zstd.v1.5.7+1.aarch64-linux-gnu.tar.gz/sha512/1fdcf77e877f0676fc26a05e0cc20a1d6e1df731d81e0bba9a5657131116bbea75da4d38953969d8d07dce0bf2d7654075dbb285ebe5f4588c446e88774336c8 +Zstd.v1.5.7+1.aarch64-linux-musl.tar.gz/md5/cc9ada74a19db50d7dd6edd05866c902 +Zstd.v1.5.7+1.aarch64-linux-musl.tar.gz/sha512/0b33c0df144bb1e95290685f01695b26da834a70a365c0362314cb001ba611962a0876bc5baac31f19c80bcb110e030fb9840a56761b4d29a7893ca65f95b111 +Zstd.v1.5.7+1.aarch64-unknown-freebsd.tar.gz/md5/5daa5b2bf2b856c448feaa8329d0de1b +Zstd.v1.5.7+1.aarch64-unknown-freebsd.tar.gz/sha512/b39d025463b4bf21295fd5bbff91ba501506b3480363cdcfe6dd2f11d2e0afaf130f6c74d962e503fccb7a55bfcad0504ebb19f18b6b5c8b8103e7b9919df536 +Zstd.v1.5.7+1.armv6l-linux-gnueabihf.tar.gz/md5/f4218e8b4f8d415df49aeba9d43f0ba0 +Zstd.v1.5.7+1.armv6l-linux-gnueabihf.tar.gz/sha512/878d4f90160c6b0c341c61ecafbf5f5cb89c73db3175f272adc666bc25c88b127145d78946bc0fcb992489b54fbb48089bfcacf768397fc5d54d7cae4aeae9f9 +Zstd.v1.5.7+1.armv6l-linux-musleabihf.tar.gz/md5/3c2e132ca47e6d1d23c149fdde9d8bd5 +Zstd.v1.5.7+1.armv6l-linux-musleabihf.tar.gz/sha512/3745d99c9ca0ce9f98ff9393e405e8b382d05573a972067d57e800e282a9544fff7bc3d49b91eccc98d7736acdc3faa4c637911d79fab10f5a691d33ae775574 +Zstd.v1.5.7+1.armv7l-linux-gnueabihf.tar.gz/md5/926d765281bef388ecc25d04cbb66102 +Zstd.v1.5.7+1.armv7l-linux-gnueabihf.tar.gz/sha512/2d2c14587e2e7b2b147cb6423720cc30ed6aa57ed07372a1aa54e7f2e6badb5aa640b116e83371561d6f8f3a1b3f7fff7f6df137f8c7be788ee889bb30273eae +Zstd.v1.5.7+1.armv7l-linux-musleabihf.tar.gz/md5/c25420561ce254e57d74e30c88fc53dd +Zstd.v1.5.7+1.armv7l-linux-musleabihf.tar.gz/sha512/2f924e2089589057e8713d04db9a1cb2f2d571ad9e7eeda3b7f898c9a75f8fecf0647f2185d3c01fc3b399d3662ff3b1acb13429c8a953f0394a3ed9ca30b877 +Zstd.v1.5.7+1.i686-linux-gnu.tar.gz/md5/3314bf1b52f2295555fb4ae44b1d9331 +Zstd.v1.5.7+1.i686-linux-gnu.tar.gz/sha512/91502910a0c9b786d91499477fee2445b8f6de6bcb71af7d79c738ea2430c67cb1957866383ee3921ed1a23c53a80be19aea6abcf0e76056ffee69583728c3ed +Zstd.v1.5.7+1.i686-linux-musl.tar.gz/md5/845eddc06527a4c4b196666f7ac64ba3 +Zstd.v1.5.7+1.i686-linux-musl.tar.gz/sha512/bb15b4327cef32be38c2fd68afedb3245c7db881ad66d3ece2198ff3034be9c12efa3d62bcba2b8e6056e7d8cb5f1b3e33726f7d1e1bead235c38f8fa985b557 +Zstd.v1.5.7+1.i686-w64-mingw32.tar.gz/md5/9bc0b3c951f5e66393fd5433bf60a2c8 +Zstd.v1.5.7+1.i686-w64-mingw32.tar.gz/sha512/550b0189097e569f98404aa836b76a5cbdc36428292214c4af8916dea2713440cf3ba94125b3e5fa0c65b2bcb916733094fdef906ad19f923d90dabfc961c75a +Zstd.v1.5.7+1.powerpc64le-linux-gnu.tar.gz/md5/468d930de7a27af961996e7c6ed35298 +Zstd.v1.5.7+1.powerpc64le-linux-gnu.tar.gz/sha512/d680715b1ac9ff07d5662c499fbab67757509599335f861158b9dba32fe9b22da6e52d0db6b402dd4542799621ad3dccf254dfd9d3c8748bbd22f7446681539a +Zstd.v1.5.7+1.riscv64-linux-gnu.tar.gz/md5/b93fef8db2b0b4417f7836d73c5fbe86 +Zstd.v1.5.7+1.riscv64-linux-gnu.tar.gz/sha512/9f3ee42c7952aba2d2c26252f058bb7ab96828fafc978c9273b500ef15ccd271c51399d4b93eebd4c832b087ab5ed8a4847104ce9c83c9483aaa13c22df681bb +Zstd.v1.5.7+1.x86_64-apple-darwin.tar.gz/md5/29a260789fae6f6b6df0e5cebdafd615 +Zstd.v1.5.7+1.x86_64-apple-darwin.tar.gz/sha512/015045a1b7a477504057cb4c87428d42386218e48af38f83739dbe6b93961ca2c8dd4d794377a2d54b8cc284f5a467e3358d4f534cf8bcbcad886ef8cea038e9 +Zstd.v1.5.7+1.x86_64-linux-gnu.tar.gz/md5/06656befb6ef9a8cc7f56e7152c2acc5 +Zstd.v1.5.7+1.x86_64-linux-gnu.tar.gz/sha512/16aea0d95432a87d21d9a6f55d84e45df85caf1fda77c75b7e9a8bba519605168585f21a812773ddf1075d9bad68412e63b8cad1a143420e25ae4405bb41842e +Zstd.v1.5.7+1.x86_64-linux-musl.tar.gz/md5/da13dd1cc0d20ba9a06e9e79a588cda4 +Zstd.v1.5.7+1.x86_64-linux-musl.tar.gz/sha512/cd4218fa92dcf8772390788d5654ca12132af7829fb0ada016f3c663e2045e29e7d7587f2f5a4f057020cacca17c188c8537f284b1456100d57e84bb47c40e77 +Zstd.v1.5.7+1.x86_64-unknown-freebsd.tar.gz/md5/bce5f37e53e330bfe4df4a28cf5c223b +Zstd.v1.5.7+1.x86_64-unknown-freebsd.tar.gz/sha512/8f6bd7664efea537ac7815db0604ca1a07bcfb71b5152c22dc7f0a11b57643f059c341fa71d315407e2333e4c97e43e214471c73eed8b977680785302c7c2b3e +Zstd.v1.5.7+1.x86_64-w64-mingw32.tar.gz/md5/7cf3a740fa174004b94125e8754f4a19 +Zstd.v1.5.7+1.x86_64-w64-mingw32.tar.gz/sha512/faac37ad4dacb0f083364c593cd3bd1c0b592947341a631bd2fbc4081361d97ef89482f4459c46ad37ae030aa900c62305a8525e64a2ad8e91204d76dda89db1 +zstd-f8745da6ff1ad1e7bab384bd1f9d742439278e99.tar.gz/md5/a679d9aa86549b5851100ac5d4044c68 +zstd-f8745da6ff1ad1e7bab384bd1f9d742439278e99.tar.gz/sha512/27c6fff165abea694d91311a6657a939433ba1d707147ed9072b5e4ecce259b929970306788e0c3e95db38ce85e894e5025936b1faa81cf67741b8464e24fc4e diff --git a/deps/zstd.mk b/deps/zstd.mk new file mode 100644 index 0000000000000..5ead77641858a --- /dev/null +++ b/deps/zstd.mk @@ -0,0 +1,60 @@ +## Zstd ## +ifneq ($(USE_BINARYBUILDER_ZSTD), 1) +ZSTD_GIT_URL := https://github.com/facebook/zstd.git +ZSTD_TAR_URL = https://api.github.com/repos/facebook/zstd/tarball/$1 +$(eval $(call git-external,zstd,ZSTD,,,$(BUILDDIR))) + +ZSTD_BUILD_OPTS := MOREFLAGS="-DZSTD_MULTITHREAD $(fPIC)" bindir=$(build_private_libexecdir) + +$(BUILDDIR)/$(ZSTD_SRC_DIR)/build-configured: $(BUILDDIR)/$(ZSTD_SRC_DIR)/source-extracted + echo 1 > $@ + +$(BUILDDIR)/$(ZSTD_SRC_DIR)/build-compiled: $(BUILDDIR)/$(ZSTD_SRC_DIR)/build-configured + $(MAKE) -C $(dir $<) $(MAKE_COMMON) $(ZSTD_BUILD_OPTS) + echo 1 > $@ + +$(eval $(call staged-install, \ + zstd,$(ZSTD_SRC_DIR), \ + MAKE_INSTALL,$(ZSTD_BUILD_OPTS) MT=1,, \ + $(INSTALL_NAME_CMD)libzstd.$(SHLIB_EXT) $(build_private_libexecdir)/libzstd.$(SHLIB_EXT))) + +clean-zstd: + -rm -f $(BUILDDIR)/$(ZSTD_SRC_DIR)/build-configured $(BUILDDIR)/$(ZSTD_SRC_DIR)/build-compiled + -$(MAKE) -C $(BUILDDIR)/$(ZSTD_SRC_DIR) $(MAKE_COMMON) $(ZSTD_BUILD_OPTS) clean + +get-zstd: $(ZSTD_SRC_FILE) +extract-zstd: $(BUILDDIR)/$(ZSTD_SRC_DIR)/source-extracted +configure-zstd: $(BUILDDIR)/$(ZSTD_SRC_DIR)/build-configured +compile-zstd: $(BUILDDIR)/$(ZSTD_SRC_DIR)/build-compiled +fastcheck-zstd: check-zstd +check-zstd: compile-zstd + +else # USE_BINARYBUILDER_ZSTD + +$(eval $(call bb-install,zstd,ZSTD,false)) +# move from bindir to shlibdir, where we expect to install it +install-zstd: post-install-zstd +uninstall-zstd: pre-uninstall-zstd +post-install-zstd: $(build_prefix)/manifest/zstd $(PATCHELF_MANIFEST) + mkdir -p $(build_private_libexecdir)/ + [ ! -e $(build_bindir)/zstdmt$(EXE) ] || mv $(build_bindir)/zstdmt$(EXE) $(build_private_libexecdir)/zstdmt$(EXE) + [ ! -e $(build_bindir)/zstd$(EXE) ] || mv $(build_bindir)/zstd$(EXE) $(build_private_libexecdir)/zstd$(EXE) + [ -e $(build_private_libexecdir)/zstd$(EXE) ] + [ -e $(build_private_libexecdir)/zstdmt$(EXE) ] +ifeq ($(OS), Darwin) + for j in zstd zstdmt ; do \ + [ -L $(build_private_libexecdir)/$$j ] && continue; \ + install_name_tool -rpath @executable_path/$(reverse_build_private_libexecdir_rel) @loader_path/$(build_libdir_rel) $(build_private_libexecdir)/$$j 2>/dev/null || true; \ + install_name_tool -rpath @loader_path/$(build_libdir_rel) @executable_path/$(reverse_build_private_libexecdir_rel) $(build_private_libexecdir)/$$j || exit 1; \ + done +else ifneq (,$(findstring $(OS),Linux FreeBSD)) + for j in zstd zstdmt ; do \ + [ -L $(build_private_libexecdir)/$$j ] && continue; \ + $(PATCHELF) $(PATCHELF_SET_RPATH_ARG) '$$ORIGIN/$(reverse_build_private_libexecdir_rel)' $(build_private_libexecdir)/$$j || exit 1; \ + done +endif + +pre-uninstall-zstd: + -rm -f $(build_private_libexecdir)/zstd$(EXE) $(build_private_libexecdir)/zstdmt$(EXE) + +endif # USE_BINARYBUILDER_ZSTD diff --git a/deps/zstd.version b/deps/zstd.version new file mode 100644 index 0000000000000..d4d960aa6f04b --- /dev/null +++ b/deps/zstd.version @@ -0,0 +1,8 @@ +# -*- makefile -*- +## jll artifact +ZSTD_JLL_NAME := Zstd + +## source build +ZSTD_VER := 1.5.7 +ZSTD_BRANCH=v1.5.7 +ZSTD_SHA1=f8745da6ff1ad1e7bab384bd1f9d742439278e99 diff --git a/julia.spdx.json b/julia.spdx.json index 63683dd302a39..21d07c68560a0 100644 --- a/julia.spdx.json +++ b/julia.spdx.json @@ -420,6 +420,18 @@ "copyrightText": "Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler", "summary": "A massively spiffy yet delicately unobtrusive compression library." }, + { + "name": "zstd", + "SPDXID": "SPDXRef-zstd", + "downloadLocation": "git+https://github.com/facebook/zstd.git", + "filesAnalyzed": false, + "homepage": "https://www.zstd.net", + "sourceInfo": "The git hash of the version in use can be found in the file deps/zstd.version", + "licenseConcluded": "BSD-3-Clause", + "licenseDeclared": "GPL-2.0+ OR BSD-3-Clause", + "copyrightText": "Copyright (c) Meta Platforms, Inc. and affiliates. All rights reserved.", + "summary": "Zstandard, or zstd as short version, is a fast lossless compression algorithm." + }, { "name": "patchelf", "SPDXID": "SPDXRef-patchelf", @@ -627,6 +639,11 @@ "relationshipType": "BUILD_DEPENDENCY_OF", "relatedSpdxElement": "SPDXRef-JuliaMain" }, + { + "spdxElementId": "SPDXRef-zstd", + "relationshipType": "BUILD_DEPENDENCY_OF", + "relatedSpdxElement": "SPDXRef-JuliaMain" + }, { "spdxElementId": "SPDXRef-patchelf", "relationshipType": "BUILD_TOOL_OF", diff --git a/src/Makefile b/src/Makefile index bf9001e5fba93..d1cfe867431fc 100644 --- a/src/Makefile +++ b/src/Makefile @@ -17,7 +17,8 @@ FLAGS := \ -D_GNU_SOURCE -I$(BUILDDIR) -I$(SRCDIR) \ -I$(SRCDIR)/flisp -I$(SRCDIR)/support \ -I$(LIBUV_INC) -I$(build_includedir) \ - -I$(JULIAHOME)/deps/valgrind + -I$(JULIAHOME)/deps/valgrind \ + -I$(ZSTD_INC) FLAGS += -Wall -Wno-strict-aliasing -fno-omit-frame-pointer -fvisibility=hidden -fno-common \ -Wno-comment -Wpointer-arith -Wundef ifeq ($(USEGCC),1) # GCC bug #25509 (void)__attribute__((warn_unused_result)) @@ -46,7 +47,8 @@ SRCS := \ simplevector runtime_intrinsics precompile jloptions mtarraylist \ threading scheduler stackwalk gc gc-debug gc-pages gc-stacks gc-alloc-profiler gc-page-profiler method \ jlapi signal-handling safepoint timing subtype rtutils gc-heap-snapshot \ - crc32c APInt-C processor ircode opaque_closure codegen-stubs coverage runtime_ccall + crc32c APInt-C processor ircode opaque_closure codegen-stubs coverage runtime_ccall \ + null_sysimage RT_LLVMLINK := CG_LLVMLINK := @@ -56,7 +58,8 @@ CODEGEN_SRCS := codegen jitlayers aotcompile debuginfo disasm llvm-simdloop llvm llvm-final-gc-lowering llvm-pass-helpers llvm-late-gc-lowering llvm-ptls \ llvm-lower-handlers llvm-gc-invariant-verifier llvm-propagate-addrspaces \ llvm-multiversioning llvm-alloc-opt llvm-alloc-helpers cgmemmgr llvm-remove-addrspaces \ - llvm-remove-ni llvm-julia-licm llvm-demote-float16 llvm-cpufeatures pipeline llvm_api + llvm-remove-ni llvm-julia-licm llvm-demote-float16 llvm-cpufeatures pipeline llvm_api \ + null_sysimage FLAGS += -I$(shell $(LLVM_CONFIG_HOST) --includedir) CG_LLVM_LIBS := all ifeq ($(USE_POLLY),1) @@ -160,15 +163,15 @@ endif CLANG_LDFLAGS := $(LLVM_LDFLAGS) ifeq ($(OS), Darwin) CLANG_LDFLAGS += -Wl,-undefined,dynamic_lookup -OSLIBS += -Wl,-U,__dyld_atfork_parent -Wl,-U,__dyld_atfork_prepare -Wl,-U,__dyld_dlopen_atfork_parent -Wl,-U,__dyld_dlopen_atfork_prepare -Wl,-U,_jl_image_pointers -Wl,-U,_jl_system_image_data -Wl,-U,_jl_system_image_size +OSLIBS += -Wl,-U,__dyld_atfork_parent -Wl,-U,__dyld_atfork_prepare -Wl,-U,__dyld_dlopen_atfork_parent -Wl,-U,__dyld_dlopen_atfork_prepare LIBJULIA_PATH_REL := @rpath/libjulia else LIBJULIA_PATH_REL := libjulia endif COMMON_LIBPATHS := -L$(build_libdir) -L$(build_shlibdir) -RT_LIBS := $(WHOLE_ARCHIVE) $(LIBUV) $(WHOLE_ARCHIVE) $(LIBUTF8PROC) $(NO_WHOLE_ARCHIVE) $(LIBUNWIND) $(RT_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) $(LIBITTAPI) -CG_LIBS := $(LIBUNWIND) $(CG_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) $(LIBITTAPI) +RT_LIBS := $(WHOLE_ARCHIVE) $(LIBUV) $(WHOLE_ARCHIVE) $(LIBUTF8PROC) $(NO_WHOLE_ARCHIVE) $(LIBUNWIND) $(RT_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) $(LIBITTAPI) $(LIBZSTD) +CG_LIBS := $(LIBUNWIND) $(CG_LLVMLINK) $(OSLIBS) $(LIBTRACYCLIENT) $(LIBITTAPI) $(LIBZSTD) RT_DEBUG_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp-debug.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport-debug.a -ljulia-debug $(RT_LIBS) CG_DEBUG_LIBS := $(COMMON_LIBPATHS) $(CG_LIBS) -ljulia-debug -ljulia-internal-debug RT_RELEASE_LIBS := $(COMMON_LIBPATHS) $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport.a -ljulia $(RT_LIBS) @@ -471,7 +474,7 @@ $(build_shlibdir)/lib%Plugin.$(SHLIB_EXT): $(SRCDIR)/clangsa/%.cpp $(LLVM_CONFIG # before attempting this static analysis, so that all necessary headers # and dependencies are properly installed: # make -C src install-analysis-deps -ANALYSIS_DEPS := llvm clang llvm-tools libuv utf8proc +ANALYSIS_DEPS := llvm clang llvm-tools libuv utf8proc zstd ifeq ($(OS),Darwin) ANALYSIS_DEPS += llvmunwind else ifneq ($(OS),WINNT) diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 97734852f9479..8132628b5d370 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -57,9 +57,10 @@ #include #include - using namespace llvm; +#include + #include "jitlayers.h" #include "serialize.h" #include "julia_assert.h" @@ -1659,12 +1660,25 @@ void jl_dump_native_impl(void *native_code, sysimgM.setDataLayout(DL); sysimgM.setStackProtectorGuard(StackProtectorGuard); sysimgM.setOverrideStackAlignment(OverrideStackAlignment); - Constant *data = ConstantDataArray::get(Context, - ArrayRef((const unsigned char*)z->buf, z->size)); + + int compression = jl_options.compress_sysimage ? 15 : 0; + ArrayRef sysimg_data{z->buf, (size_t)z->size}; + SmallVector compressed_data; + if (compression) { + compressed_data.resize(ZSTD_compressBound(z->size)); + size_t comp_size = ZSTD_compress(compressed_data.data(), compressed_data.size(), + z->buf, z->size, compression); + compressed_data.resize(comp_size); + sysimg_data = compressed_data; + ios_close(z); + free(z); + } + + Constant *data = ConstantDataArray::get(Context, sysimg_data); auto sysdata = new GlobalVariable(sysimgM, data->getType(), false, GlobalVariable::ExternalLinkage, data, "jl_system_image_data"); - sysdata->setAlignment(Align(64)); + sysdata->setAlignment(Align(jl_page_size)); #if JL_LLVM_VERSION >= 180000 sysdata->setCodeModel(CodeModel::Large); #else @@ -1672,14 +1686,27 @@ void jl_dump_native_impl(void *native_code, sysdata->setSection(".ldata"); #endif addComdat(sysdata, TheTriple); - Constant *len = ConstantInt::get(sysimgM.getDataLayout().getIntPtrType(Context), z->size); + Constant *len = ConstantInt::get(sysimgM.getDataLayout().getIntPtrType(Context), sysimg_data.size()); addComdat(new GlobalVariable(sysimgM, len->getType(), true, GlobalVariable::ExternalLinkage, len, "jl_system_image_size"), TheTriple); - // Free z here, since we've copied out everything into data - // Results in serious memory savings - ios_close(z); - free(z); + + const char *unpack_func = compression ? "jl_image_unpack_zstd" : "jl_image_unpack_uncomp"; + auto unpack = new GlobalVariable(sysimgM, DL.getIntPtrType(Context), true, + GlobalVariable::ExternalLinkage, nullptr, + unpack_func); + addComdat(new GlobalVariable(sysimgM, PointerType::getUnqual(Context), true, + GlobalVariable::ExternalLinkage, unpack, + "jl_image_unpack"), + TheTriple); + + if (!compression) { + // Free z here, since we've copied out everything into data + // Results in serious memory savings + ios_close(z); + free(z); + } + compressed_data.clear(); // Note that we don't set z to null, this allows the check in WRITE_ARCHIVE // to function as expected // no need to free the module/context, destructor handles that @@ -1833,7 +1860,7 @@ void jl_dump_native_impl(void *native_code, builder.CreateRet(ConstantInt::get(T_int32, 1)); } if (imaging_mode) { - auto specs = jl_get_llvm_clone_targets(); + auto specs = jl_get_llvm_clone_targets(jl_options.cpu_target); const uint32_t base_flags = has_veccall ? JL_TARGET_VEC_CALL : 0; SmallVector data; auto push_i32 = [&] (uint32_t v) { diff --git a/src/init.c b/src/init.c index a5c9a6b19f94d..3517aa9d8fa5a 100644 --- a/src/init.c +++ b/src/init.c @@ -847,19 +847,30 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ { 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); + jl_image_buf_t sysimage = { JL_IMAGE_KIND_NONE }; + if (rel == JL_IMAGE_IN_MEMORY) { + sysimage = jl_set_sysimg_so(jl_exe_handle); + jl_options.image_file = jl_options.julia_bin; + } else if (jl_options.image_file) - jl_preload_sysimg_so(jl_options.image_file); + sysimage = jl_preload_sysimg(jl_options.image_file); + if (jl_options.cpu_target == NULL) jl_options.cpu_target = "native"; - jl_init_codegen(); + // Parse image, perform relocations, and init JIT targets, etc. + jl_image_t parsed_image = jl_init_processor_sysimg(sysimage, jl_options.cpu_target); + + jl_init_codegen(); jl_init_common_symbols(); - if (jl_options.image_file) { - jl_restore_system_image(jl_options.image_file); + + if (sysimage.kind != JL_IMAGE_KIND_NONE) { + // Load the .ji or .so sysimage + jl_restore_system_image(&parsed_image, sysimage); } else { + // No sysimage provided, init a minimal environment jl_init_types(); jl_global_roots_list = (jl_genericmemory_t*)jl_an_empty_memory_any; jl_global_roots_keyset = (jl_genericmemory_t*)jl_an_empty_memory_any; @@ -868,7 +879,7 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ jl_init_flisp(); jl_init_serializer(); - if (!jl_options.image_file) { + if (sysimage.kind == JL_IMAGE_KIND_NONE) { jl_top_module = jl_core_module; jl_init_intrinsic_functions(); jl_init_primitives(); @@ -892,7 +903,8 @@ static NOINLINE void _finish_julia_init(JL_IMAGE_SEARCH rel, jl_ptls_t ptls, jl_ jl_gc_enable(1); - if (jl_options.image_file && (!jl_generating_output() || jl_options.incremental) && jl_module_init_order) { + if ((sysimage.kind != JL_IMAGE_KIND_NONE) && + (!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; diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index b3ed1e069de16..e418ce06a74a7 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1139,7 +1139,7 @@ namespace { options.ExplicitEmulatedTLS = true; #endif uint32_t target_flags = 0; - auto target = jl_get_llvm_target(imaging_default(), target_flags); + auto target = jl_get_llvm_target(jl_options.cpu_target, jl_generating_output(), target_flags); auto &TheCPU = target.first; SmallVector targetFeatures(target.second.begin(), target.second.end()); std::string errorstr; diff --git a/src/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index 05057cfd80861..66db481187b47 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -370,7 +370,7 @@ XX(jl_pointerset) \ XX(jl_pop_handler) \ XX(jl_pop_handler_noexcept) \ - XX(jl_preload_sysimg_so) \ + XX(jl_preload_sysimg) \ XX(jl_prepend_cwd) \ XX(jl_printf) \ XX(jl_print_backtrace) \ @@ -400,7 +400,6 @@ XX(jl_restore_incremental) \ XX(jl_restore_package_image_from_file) \ XX(jl_restore_system_image) \ - XX(jl_restore_system_image_data) \ XX(jl_rethrow) \ XX(jl_rethrow_other) \ XX(jl_running_on_valgrind) \ diff --git a/src/jloptions.c b/src/jloptions.c index ad13151703531..2b1c0d40005e6 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -101,6 +101,7 @@ JL_DLLEXPORT void jl_init_options(void) 0, // strip-ir 0, // permalloc_pkgimg 0, // heap-size-hint + 0, // compress_sysimage }; jl_options_initialized = 1; } @@ -222,10 +223,13 @@ static const char opts_hidden[] = " Enable or disable JIT compiler, or request exhaustive or minimal compilation\n\n" // compiler output options - " --output-o Generate an object file (including system image data)\n" - " --output-ji Generate a system image data file (.ji)\n" - " --strip-metadata Remove docstrings and source location info from system image\n" - " --strip-ir Remove IR (intermediate representation) of compiled functions\n\n" + " --output-o Generate an object file (including system image data)\n" + " --output-ji Generate a system image data file (.ji)\n" + " --strip-metadata Remove docstrings and source location info from system image\n" + " --strip-ir Remove IR (intermediate representation) of compiled functions\n" + " --compress-sysimage={yes|no*} Compress the sys/pkgimage heap at the expense of\n" + " slightly increased load time.\n" + "\n" // compiler debugging (see the devdocs for tips on using these options) " --output-unopt-bc Generate unoptimized LLVM bitcode (.bc)\n" @@ -282,7 +286,8 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) opt_strip_ir, opt_heap_size_hint, opt_gc_threads, - opt_permalloc_pkgimg + opt_permalloc_pkgimg, + opt_compress_sysimage, }; static const char* const shortopts = "+vhqH:e:E:L:J:C:it:p:O:g:"; static const struct option longopts[] = { @@ -344,6 +349,7 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) { "strip-ir", no_argument, 0, opt_strip_ir }, { "permalloc-pkgimg",required_argument, 0, opt_permalloc_pkgimg }, { "heap-size-hint", required_argument, 0, opt_heap_size_hint }, + { "compress-sysimage", required_argument, 0, opt_compress_sysimage }, { 0, 0, 0, 0 } }; @@ -895,6 +901,12 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) else jl_errorf("julia: invalid argument to --permalloc-pkgimg={yes|no} (%s)", optarg); break; + case opt_compress_sysimage: + if (!strcmp(optarg,"yes")) + jl_options.compress_sysimage = 1; + else if (!strcmp(optarg,"no")) + jl_options.compress_sysimage = 0; + break; default: jl_errorf("julia: unhandled option -- %c\n" "This is a bug, please report it.", c); diff --git a/src/jloptions.h b/src/jloptions.h index f72e72b462066..ca938aa3fb503 100644 --- a/src/jloptions.h +++ b/src/jloptions.h @@ -62,6 +62,7 @@ typedef struct { int8_t strip_ir; int8_t permalloc_pkgimg; uint64_t heap_size_hint; + int8_t compress_sysimage; } jl_options_t; #endif diff --git a/src/julia.h b/src/julia.h index 2bb8bc5d0b59f..b8783ff821d00 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2030,6 +2030,25 @@ typedef enum { JL_IMAGE_IN_MEMORY = 2 } JL_IMAGE_SEARCH; +typedef enum { + JL_IMAGE_KIND_NONE = 0, + JL_IMAGE_KIND_JI, + JL_IMAGE_KIND_SO, +} jl_image_kind_t; + +// A loaded, but unparsed .ji or .so image file +typedef struct { + jl_image_kind_t kind; + void *handle; + const void *pointers; // jl_image_pointers_t * + const char *data; + size_t size; + uint64_t base; +} jl_image_buf_t; + +struct _jl_image_t; +typedef struct _jl_image_t jl_image_t; + JL_DLLIMPORT const char *jl_get_libdir(void); JL_DLLEXPORT void julia_init(JL_IMAGE_SEARCH rel); JL_DLLEXPORT void jl_init(void); @@ -2046,11 +2065,10 @@ JL_DLLEXPORT const char *jl_pathname_for_handle(void *handle); JL_DLLEXPORT jl_gcframe_t **jl_adopt_thread(void); JL_DLLEXPORT int jl_deserialize_verify_header(ios_t *s); -JL_DLLEXPORT void jl_preload_sysimg_so(const char *fname); -JL_DLLEXPORT void jl_set_sysimg_so(void *handle); +JL_DLLEXPORT jl_image_buf_t jl_preload_sysimg(const char *fname); +JL_DLLEXPORT jl_image_buf_t jl_set_sysimg_so(void *handle); JL_DLLEXPORT void jl_create_system_image(void **, jl_array_t *worklist, bool_t emit_split, ios_t **s, ios_t **z, jl_array_t **udeps, int64_t *srctextpos); -JL_DLLEXPORT void jl_restore_system_image(const char *fname); -JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len); +JL_DLLEXPORT void jl_restore_system_image(jl_image_t *image, jl_image_buf_t buf); JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *depmods, int complete, const char *pkgimage); JL_DLLEXPORT void jl_set_newly_inferred(jl_value_t *newly_inferred); diff --git a/src/julia_internal.h b/src/julia_internal.h index 72d9dfdad5c7e..dff769e7e0f66 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1710,24 +1710,6 @@ jl_sym_t *_jl_symbol(const char *str, size_t len) JL_NOTSAFEPOINT; #define JL_GC_ASSERT_LIVE(x) (void)(x) #endif -#ifdef _OS_WINDOWS_ -// On Windows, weak symbols do not default to 0 due to a GCC bug -// (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=90826), use symbol -// aliases with a known value instead. -#define JL_WEAK_SYMBOL_OR_ALIAS_DEFAULT(sym) __attribute__((weak,alias(#sym))) -#define JL_WEAK_SYMBOL_DEFAULT(sym) &sym -#else -#define JL_WEAK_SYMBOL_OR_ALIAS_DEFAULT(sym) __attribute__((weak)) -#define JL_WEAK_SYMBOL_DEFAULT(sym) NULL -#endif - -//JL_DLLEXPORT float julia__gnu_h2f_ieee(half param) JL_NOTSAFEPOINT; -//JL_DLLEXPORT half julia__gnu_f2h_ieee(float param) JL_NOTSAFEPOINT; -//JL_DLLEXPORT half julia__truncdfhf2(double param) JL_NOTSAFEPOINT; -//JL_DLLEXPORT float julia__truncsfbf2(float param) JL_NOTSAFEPOINT; -//JL_DLLEXPORT float julia__truncdfbf2(double param) JL_NOTSAFEPOINT; -//JL_DLLEXPORT double julia__extendhfdf2(half n) JL_NOTSAFEPOINT; - JL_DLLEXPORT uint32_t jl_crc32c(uint32_t crc, const char *buf, size_t len); // -- exports from codegen -- // diff --git a/src/llvm-multiversioning.cpp b/src/llvm-multiversioning.cpp index 1901a721e5e08..299213090f064 100644 --- a/src/llvm-multiversioning.cpp +++ b/src/llvm-multiversioning.cpp @@ -215,7 +215,7 @@ static void annotate_module_clones(Module &M) { if (auto maybe_specs = get_target_specs(M)) { specs = std::move(*maybe_specs); } else { - auto full_specs = jl_get_llvm_clone_targets(); + auto full_specs = jl_get_llvm_clone_targets(jl_options.cpu_target); specs.reserve(full_specs.size()); for (auto &spec: full_specs) { specs.push_back(TargetSpec::fromSpec(spec)); diff --git a/src/null_sysimage.c b/src/null_sysimage.c new file mode 100644 index 0000000000000..386842f0c4e77 --- /dev/null +++ b/src/null_sysimage.c @@ -0,0 +1,15 @@ +// This file is a part of Julia. License is MIT: https://julialang.org/license + +#include +#include "processor.h" + +/** + * These symbols support statically linking the sysimage with libjulia-internal. + * + * Here we provide dummy definitions that are used when these are not linked + * together (the default build configuration). The 0 value of jl_system_image_size + * is used as a sentinel to indicate that the sysimage should be loaded externally. + **/ +char jl_system_image_data = 0; +size_t jl_system_image_size = 0; +jl_image_pointers_t jl_image_pointers = { 0 }; diff --git a/src/processor.cpp b/src/processor.cpp index 730e470f4153d..28f5d80d77cd2 100644 --- a/src/processor.cpp +++ b/src/processor.cpp @@ -504,7 +504,8 @@ static inline llvm::SmallVector, 0> parse_cmdline(const char *option, F &&feature_cb) { if (!option) - option = "native"; + abort(); + llvm::SmallVector, 0> res; TargetData arg{}; auto reset_arg = [&] { @@ -612,36 +613,29 @@ parse_cmdline(const char *option, F &&feature_cb) // Cached version of command line parsing template -static inline llvm::SmallVector, 0> &get_cmdline_targets(F &&feature_cb) +static inline llvm::SmallVector, 0> &get_cmdline_targets(const char *cpu_target, F &&feature_cb) { static llvm::SmallVector, 0> targets = - parse_cmdline(jl_options.cpu_target, std::forward(feature_cb)); + parse_cmdline(cpu_target, std::forward(feature_cb)); return targets; } -extern "C" { -void *image_pointers_unavailable; -extern void * JL_WEAK_SYMBOL_OR_ALIAS_DEFAULT(image_pointers_unavailable) jl_image_pointers; -} - // Load sysimg, use the `callback` for dispatch and perform all relocations // for the selected target. template -static inline jl_image_t parse_sysimg(void *hdl, F &&callback) +static inline jl_image_t parse_sysimg(jl_image_buf_t image, F &&callback, void *ctx) { JL_TIMING(LOAD_IMAGE, LOAD_Processor); jl_image_t res{}; - const jl_image_pointers_t *pointers; - if (hdl == jl_exe_handle && &jl_image_pointers != JL_WEAK_SYMBOL_DEFAULT(image_pointers_unavailable)) - pointers = (const jl_image_pointers_t *)&jl_image_pointers; - else - jl_dlsym(hdl, "jl_image_pointers", (void**)&pointers, 1); + if (image.kind != JL_IMAGE_KIND_SO) + return res; + const jl_image_pointers_t *pointers = (const jl_image_pointers_t *)image.pointers; const void *ids = pointers->target_data; jl_value_t* rejection_reason = nullptr; JL_GC_PUSH1(&rejection_reason); - uint32_t target_idx = callback(ids, &rejection_reason); + uint32_t target_idx = callback(ctx, ids, &rejection_reason); if (target_idx == UINT32_MAX) { jl_error(jl_string_ptr(rejection_reason)); } @@ -799,17 +793,7 @@ static inline jl_image_t parse_sysimg(void *hdl, F &&callback) res.fptrs.nclones = clones.size(); } -#ifdef _OS_WINDOWS_ - res.base = (intptr_t)hdl; -#else - Dl_info dlinfo; - if (dladdr((void*)pointers, &dlinfo) != 0) { - res.base = (intptr_t)dlinfo.dli_fbase; - } - else { - res.base = 0; - } -#endif + res.base = image.base; { void *pgcstack_func_slot = pointers->ptls->pgcstack_func_slot; @@ -1025,7 +1009,7 @@ JL_DLLEXPORT jl_value_t *jl_get_cpu_features(void) } extern "C" JL_DLLEXPORT jl_value_t* jl_reflect_clone_targets() { - auto specs = jl_get_llvm_clone_targets(); + auto specs = jl_get_llvm_clone_targets(jl_options.cpu_target); const uint32_t base_flags = 0; llvm::SmallVector data; auto push_i32 = [&] (uint32_t v) { diff --git a/src/processor.h b/src/processor.h index 82a1121aaf7c4..65b634fd0ba26 100644 --- a/src/processor.h +++ b/src/processor.h @@ -64,6 +64,7 @@ JL_DLLEXPORT int jl_test_cpu_feature(jl_cpu_feature_t feature); static const uint32_t jl_sysimg_tag_mask = 0x80000000u; static const uint32_t jl_sysimg_val_mask = ~((uint32_t)0x80000000u); +// A parsed image file typedef struct _jl_image_fptrs_t { // number of functions uint32_t nptrs; @@ -82,14 +83,14 @@ typedef struct _jl_image_fptrs_t { const uint32_t *clone_idxs; } jl_image_fptrs_t; -typedef struct { +struct _jl_image_t { uint64_t base; const char *gvars_base; const int32_t *gvars_offsets; uint32_t ngvars; jl_image_fptrs_t fptrs; void **jl_small_typeof; -} jl_image_t; +}; // The header for each image // Details important counts about the image @@ -206,8 +207,8 @@ typedef struct { * * Return the data about the function pointers selected. */ -jl_image_t jl_init_processor_sysimg(void *hdl); -jl_image_t jl_init_processor_pkgimg(void *hdl); +jl_image_t jl_init_processor_sysimg(jl_image_buf_t image, const char *cpu_target); +jl_image_t jl_init_processor_pkgimg(jl_image_buf_t image); // Return the name of the host CPU as a julia string. JL_DLLEXPORT jl_value_t *jl_get_cpu_name(void); @@ -224,6 +225,18 @@ JL_DLLEXPORT int32_t jl_set_zero_subnormals(int8_t isZero); JL_DLLEXPORT int32_t jl_get_zero_subnormals(void); JL_DLLEXPORT int32_t jl_set_default_nans(int8_t isDefault); JL_DLLEXPORT int32_t jl_get_default_nans(void); + +/** + * System image contents. + * + * These symbols are typically dummy values, unless statically linking + * libjulia-* and the sysimage together (see null_sysimage.c), in which + * case they allow accessing the local copy of the sysimage. + **/ +extern char jl_system_image_data; +extern size_t jl_system_image_size; +extern jl_image_pointers_t jl_image_pointers; + #ifdef __cplusplus } @@ -239,7 +252,7 @@ extern JL_DLLEXPORT bool jl_processor_print_help; * If the detected/specified CPU name is not available on the LLVM version specified, * a fallback CPU name will be used. Unsupported features will be ignored. */ -extern "C" JL_DLLEXPORT std::pair> jl_get_llvm_target(bool imaging, uint32_t &flags) JL_NOTSAFEPOINT; +extern "C" JL_DLLEXPORT std::pair> jl_get_llvm_target(const char *cpu_target, bool imaging, uint32_t &flags) JL_NOTSAFEPOINT; /** * Returns the CPU name and feature string to be used by LLVM disassembler. @@ -263,7 +276,7 @@ struct jl_target_spec_t { /** * Return the list of targets to clone */ -extern "C" JL_DLLEXPORT llvm::SmallVector jl_get_llvm_clone_targets(void) JL_NOTSAFEPOINT; +extern "C" JL_DLLEXPORT llvm::SmallVector jl_get_llvm_clone_targets(const char *cpu_target) JL_NOTSAFEPOINT; // NOLINTEND(clang-diagnostic-return-type-c-linkage) struct FeatureName { const char *name; diff --git a/src/processor_arm.cpp b/src/processor_arm.cpp index 0d9009afabec7..7df2b74773af4 100644 --- a/src/processor_arm.cpp +++ b/src/processor_arm.cpp @@ -1510,7 +1510,7 @@ static inline void disable_depends(FeatureList &features) ::disable_depends(features, Feature::deps, sizeof(Feature::deps) / sizeof(FeatureDep)); } -static const llvm::SmallVector, 0> &get_cmdline_targets(void) +static const llvm::SmallVector, 0> &get_cmdline_targets(const char *cpu_target) { auto feature_cb = [] (const char *str, size_t len, FeatureList &list) { #ifdef _CPU_AARCH64_ @@ -1527,7 +1527,7 @@ static const llvm::SmallVector, 0> &get_cmdline_targets(v set_bit(list, fbit, true); return true; }; - auto &targets = ::get_cmdline_targets(feature_cb); + auto &targets = ::get_cmdline_targets(cpu_target, feature_cb); for (auto &t: targets) { if (auto nname = normalize_cpu_name(t.name)) { t.name = nname; @@ -1590,10 +1590,11 @@ static int max_vector_size(const FeatureList &features) #endif } -static uint32_t sysimg_init_cb(const void *id, jl_value_t **rejection_reason) +static uint32_t sysimg_init_cb(void *ctx, const void *id, jl_value_t **rejection_reason) { // First see what target is requested for the JIT. - auto &cmdline = get_cmdline_targets(); + const char *cpu_target = (const char *)ctx; + auto &cmdline = get_cmdline_targets(cpu_target); TargetData target = arg_target_data(cmdline[0], true); // Then find the best match in the sysimg auto sysimg = deserialize_target_data((const uint8_t*)id); @@ -1617,7 +1618,7 @@ static uint32_t sysimg_init_cb(const void *id, jl_value_t **rejection_reason) return match.best_idx; } -static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason JL_REQUIRE_ROOTED_SLOT) +static uint32_t pkgimg_init_cb(void *ctx, const void *id, jl_value_t **rejection_reason JL_REQUIRE_ROOTED_SLOT) { TargetData target = jit_targets.front(); auto pkgimg = deserialize_target_data((const uint8_t*)id); @@ -1630,9 +1631,9 @@ static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason JL_ return match.best_idx; } -static void ensure_jit_target(bool imaging) +static void ensure_jit_target(const char *cpu_target, bool imaging) { - auto &cmdline = get_cmdline_targets(); + auto &cmdline = get_cmdline_targets(cpu_target); check_cmdline(cmdline, imaging); if (!jit_targets.empty()) return; @@ -1843,36 +1844,36 @@ JL_DLLEXPORT jl_value_t *jl_cpu_has_fma(int bits) #endif } -jl_image_t jl_init_processor_sysimg(void *hdl) +jl_image_t jl_init_processor_sysimg(jl_image_buf_t image, const char *cpu_target) { if (!jit_targets.empty()) jl_error("JIT targets already initialized"); - return parse_sysimg(hdl, sysimg_init_cb); + return parse_sysimg(image, sysimg_init_cb, (void *)cpu_target); } -jl_image_t jl_init_processor_pkgimg(void *hdl) +jl_image_t jl_init_processor_pkgimg(jl_image_buf_t image) { if (jit_targets.empty()) jl_error("JIT targets not initialized"); if (jit_targets.size() > 1) jl_error("Expected only one JIT target"); - return parse_sysimg(hdl, pkgimg_init_cb); + return parse_sysimg(image, pkgimg_init_cb, NULL); } JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char *data) { jl_value_t *rejection_reason = NULL; JL_GC_PUSH1(&rejection_reason); - uint32_t match_idx = pkgimg_init_cb(data, &rejection_reason); + uint32_t match_idx = pkgimg_init_cb(NULL, data, &rejection_reason); JL_GC_POP(); if (match_idx == UINT32_MAX) return rejection_reason; return jl_nothing; } -std::pair> jl_get_llvm_target(bool imaging, uint32_t &flags) +std::pair> jl_get_llvm_target(const char *cpu_target, bool imaging, uint32_t &flags) { - ensure_jit_target(imaging); + ensure_jit_target(cpu_target, imaging); flags = jit_targets[0].en.flags; return get_llvm_target_vec(jit_targets[0]); } @@ -1891,10 +1892,10 @@ const std::pair &jl_get_llvm_disasm_target(void) } #ifndef __clang_gcanalyzer__ -llvm::SmallVector jl_get_llvm_clone_targets(void) +llvm::SmallVector jl_get_llvm_clone_targets(const char *cpu_target) { - auto &cmdline = get_cmdline_targets(); + auto &cmdline = get_cmdline_targets(cpu_target); check_cmdline(cmdline, true); llvm::SmallVector, 0> image_targets; for (auto &arg: cmdline) { diff --git a/src/processor_fallback.cpp b/src/processor_fallback.cpp index f8d9eb9fd9e73..c8c8feb072345 100644 --- a/src/processor_fallback.cpp +++ b/src/processor_fallback.cpp @@ -13,12 +13,12 @@ static inline const std::string &host_cpu_name() return name; } -static const llvm::SmallVector, 0> &get_cmdline_targets(void) +static const llvm::SmallVector, 0> &get_cmdline_targets(const char *cpu_target) { auto feature_cb = [] (const char*, size_t, FeatureList<1>&) { return false; }; - return ::get_cmdline_targets<1>(feature_cb); + return ::get_cmdline_targets<1>(cpu_target, feature_cb); } static llvm::SmallVector, 0> jit_targets; @@ -36,10 +36,11 @@ static TargetData<1> arg_target_data(const TargetData<1> &arg, bool require_host return res; } -static uint32_t sysimg_init_cb(const void *id, jl_value_t **rejection_reason) +static uint32_t sysimg_init_cb(void *ctx, const void *id, jl_value_t **rejection_reason) { // First see what target is requested for the JIT. - auto &cmdline = get_cmdline_targets(); + const char *cpu_target = (const char *)ctx; + auto &cmdline = get_cmdline_targets(cpu_target); TargetData<1> target = arg_target_data(cmdline[0], true); // Find the last name match or use the default one. uint32_t best_idx = 0; @@ -54,7 +55,7 @@ static uint32_t sysimg_init_cb(const void *id, jl_value_t **rejection_reason) return best_idx; } -static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason) +static uint32_t pkgimg_init_cb(void *ctx, const void *id, jl_value_t **rejection_reason) { TargetData<1> target = jit_targets.front(); // Find the last name match or use the default one. @@ -70,9 +71,9 @@ static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason) return best_idx; } -static void ensure_jit_target(bool imaging) +static void ensure_jit_target(const char *cpu_target, bool imaging) { - auto &cmdline = get_cmdline_targets(); + auto &cmdline = get_cmdline_targets(cpu_target); check_cmdline(cmdline, imaging); if (!jit_targets.empty()) return; @@ -115,25 +116,25 @@ get_llvm_target_str(const TargetData<1> &data) using namespace Fallback; -jl_image_t jl_init_processor_sysimg(void *hdl) +jl_image_t jl_init_processor_sysimg(jl_image_buf_t image, const char *cpu_target) { if (!jit_targets.empty()) jl_error("JIT targets already initialized"); - return parse_sysimg(hdl, sysimg_init_cb); + return parse_sysimg(image, sysimg_init_cb, (void *)cpu_target); } -jl_image_t jl_init_processor_pkgimg(void *hdl) +jl_image_t jl_init_processor_pkgimg(jl_image_buf_t image) { if (jit_targets.empty()) jl_error("JIT targets not initialized"); if (jit_targets.size() > 1) jl_error("Expected only one JIT target"); - return parse_sysimg(hdl, pkgimg_init_cb); + return parse_sysimg(image, pkgimg_init_cb, NULL); } -std::pair> jl_get_llvm_target(bool imaging, uint32_t &flags) +std::pair> jl_get_llvm_target(const char *cpu_target, bool imaging, uint32_t &flags) { - ensure_jit_target(imaging); + ensure_jit_target(cpu_target, imaging); flags = jit_targets[0].en.flags; return get_llvm_target_vec(jit_targets[0]); } @@ -145,10 +146,10 @@ const std::pair &jl_get_llvm_disasm_target(void) return res; } #ifndef __clang_gcanalyzer__ -llvm::SmallVector jl_get_llvm_clone_targets(void) +llvm::SmallVector jl_get_llvm_clone_targets(const char *cpu_target) { - auto &cmdline = get_cmdline_targets(); + auto &cmdline = get_cmdline_targets(cpu_target); check_cmdline(cmdline, true); llvm::SmallVector, 0> image_targets; for (auto &arg: cmdline) { @@ -192,7 +193,7 @@ JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char *data) { jl_value_t *rejection_reason = NULL; JL_GC_PUSH1(&rejection_reason); - uint32_t match_idx = pkgimg_init_cb(data, &rejection_reason); + uint32_t match_idx = pkgimg_init_cb(NULL, data, &rejection_reason); JL_GC_POP(); if (match_idx == UINT32_MAX) return rejection_reason; diff --git a/src/processor_x86.cpp b/src/processor_x86.cpp index db954680289ea..dbd3de8bffa0c 100644 --- a/src/processor_x86.cpp +++ b/src/processor_x86.cpp @@ -782,7 +782,7 @@ static inline void disable_depends(FeatureList &features) ::disable_depends(features, Feature::deps, sizeof(Feature::deps) / sizeof(FeatureDep)); } -static const llvm::SmallVector, 0> &get_cmdline_targets(void) +static const llvm::SmallVector, 0> &get_cmdline_targets(const char *cpu_target) { auto feature_cb = [] (const char *str, size_t len, FeatureList &list) { auto fbit = find_feature_bit(feature_names, nfeature_names, str, len); @@ -791,7 +791,7 @@ static const llvm::SmallVector, 0> &get_cmdline_targets(v set_bit(list, fbit, true); return true; }; - auto &targets = ::get_cmdline_targets(feature_cb); + auto &targets = ::get_cmdline_targets(cpu_target, feature_cb); for (auto &t: targets) { if (auto nname = normalize_cpu_name(t.name)) { t.name = nname; @@ -851,10 +851,11 @@ static int max_vector_size(const FeatureList &features) return 16; } -static uint32_t sysimg_init_cb(const void *id, jl_value_t** rejection_reason) +static uint32_t sysimg_init_cb(void *ctx, const void *id, jl_value_t** rejection_reason) { // First see what target is requested for the JIT. - auto &cmdline = get_cmdline_targets(); + const char *cpu_target = (const char *)ctx; + auto &cmdline = get_cmdline_targets(cpu_target); TargetData target = arg_target_data(cmdline[0], true); // Then find the best match in the sysimg auto sysimg = deserialize_target_data((const uint8_t*)id); @@ -897,7 +898,7 @@ static uint32_t sysimg_init_cb(const void *id, jl_value_t** rejection_reason) return match.best_idx; } -static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason) +static uint32_t pkgimg_init_cb(void *ctx, const void *id, jl_value_t **rejection_reason) { TargetData target = jit_targets.front(); auto pkgimg = deserialize_target_data((const uint8_t*)id); @@ -912,9 +913,9 @@ static uint32_t pkgimg_init_cb(const void *id, jl_value_t **rejection_reason) //This function serves as a fallback during bootstrapping, at that point we don't have a sysimage with native code // so we won't call sysimg_init_cb, else this function shouldn't do anything. -static void ensure_jit_target(bool imaging) +static void ensure_jit_target(const char *cpu_target, bool imaging) { - auto &cmdline = get_cmdline_targets(); + auto &cmdline = get_cmdline_targets(cpu_target); check_cmdline(cmdline, imaging); if (!jit_targets.empty()) return; @@ -1058,7 +1059,7 @@ JL_DLLEXPORT jl_value_t* jl_check_pkgimage_clones(char *data) { jl_value_t *rejection_reason = NULL; JL_GC_PUSH1(&rejection_reason); - uint32_t match_idx = pkgimg_init_cb(data, &rejection_reason); + uint32_t match_idx = pkgimg_init_cb(NULL, data, &rejection_reason); JL_GC_POP(); if (match_idx == UINT32_MAX) return rejection_reason; @@ -1075,25 +1076,25 @@ JL_DLLEXPORT jl_value_t *jl_cpu_has_fma(int bits) return jl_false; } -jl_image_t jl_init_processor_sysimg(void *hdl) +jl_image_t jl_init_processor_sysimg(jl_image_buf_t image, const char *cpu_target) { if (!jit_targets.empty()) jl_error("JIT targets already initialized"); - return parse_sysimg(hdl, sysimg_init_cb); + return parse_sysimg(image, sysimg_init_cb, (void *)cpu_target); } -jl_image_t jl_init_processor_pkgimg(void *hdl) +jl_image_t jl_init_processor_pkgimg(jl_image_buf_t image) { if (jit_targets.empty()) jl_error("JIT targets not initialized"); if (jit_targets.size() > 1) jl_error("Expected only one JIT target"); - return parse_sysimg(hdl, pkgimg_init_cb); + return parse_sysimg(image, pkgimg_init_cb, NULL); } -std::pair> jl_get_llvm_target(bool imaging, uint32_t &flags) +std::pair> jl_get_llvm_target(const char *cpu_target, bool imaging, uint32_t &flags) { - ensure_jit_target(imaging); + ensure_jit_target(cpu_target, imaging); flags = jit_targets[0].en.flags; return get_llvm_target_vec(jit_targets[0]); } @@ -1106,9 +1107,10 @@ const std::pair &jl_get_llvm_disasm_target(void) } //This function parses the -C command line to figure out which targets to multiversion to. #ifndef __clang_gcanalyzer__ -llvm::SmallVector jl_get_llvm_clone_targets(void) +llvm::SmallVector jl_get_llvm_clone_targets(const char *cpu_target) { - auto &cmdline = get_cmdline_targets(); + + auto &cmdline = get_cmdline_targets(cpu_target); check_cmdline(cmdline, true); llvm::SmallVector, 0> image_targets; for (auto &arg: cmdline) { diff --git a/src/staticdata.c b/src/staticdata.c index cac3b78545cca..10790422f0539 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -74,6 +74,8 @@ External links: #include // printf #include // PRIxPTR +#include + #include "julia.h" #include "julia_internal.h" #include "julia_gcext.h" @@ -81,8 +83,11 @@ External links: #include "processor.h" #include "serialize.h" -#ifndef _OS_WINDOWS_ +#ifdef _OS_WINDOWS_ +#include +#else #include +#include #endif #include "valgrind.h" @@ -607,8 +612,7 @@ typedef struct { } pkgcachesizes; // --- Static Compile --- -static void *jl_sysimg_handle = NULL; -static jl_image_t sysimage; +static jl_image_buf_t jl_sysimage_buf = { JL_IMAGE_KIND_NONE }; static inline uintptr_t *sysimg_gvars(const char *base, const int32_t *offsets, size_t idx) { @@ -620,28 +624,6 @@ JL_DLLEXPORT int jl_running_on_valgrind(void) return RUNNING_ON_VALGRIND; } -void *system_image_data_unavailable; -extern void * JL_WEAK_SYMBOL_OR_ALIAS_DEFAULT(system_image_data_unavailable) jl_system_image_data; -extern void * JL_WEAK_SYMBOL_OR_ALIAS_DEFAULT(system_image_data_unavailable) jl_system_image_size; -static void jl_load_sysimg_so(void) -{ - const char *sysimg_data; - assert(sysimage.fptrs.ptrs); // jl_init_processor_sysimg should already be run - if (jl_sysimg_handle == jl_exe_handle && - &jl_system_image_data != JL_WEAK_SYMBOL_DEFAULT(system_image_data_unavailable)) - sysimg_data = (const char*)&jl_system_image_data; - else - jl_dlsym(jl_sysimg_handle, "jl_system_image_data", (void **)&sysimg_data, 1); - size_t *plen; - if (jl_sysimg_handle == jl_exe_handle && - &jl_system_image_size != JL_WEAK_SYMBOL_DEFAULT(system_image_data_unavailable)) - plen = (size_t *)&jl_system_image_size; - else - jl_dlsym(jl_sysimg_handle, "jl_system_image_size", (void **)&plen, 1); - jl_restore_system_image_data(sysimg_data, *plen); -} - - // --- serializer --- #define NBOX_C 1024 @@ -3057,33 +3039,175 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli JL_DLLEXPORT size_t ios_write_direct(ios_t *dest, ios_t *src); -// Takes in a path of the form "usr/lib/julia/sys.so" (jl_restore_system_image should be passed the same string) -JL_DLLEXPORT void jl_preload_sysimg_so(const char *fname) +// Takes in a path of the form "usr/lib/julia/sys.so" +JL_DLLEXPORT jl_image_buf_t jl_preload_sysimg(const char *fname) { - if (jl_sysimg_handle) - return; // embedded target already called jl_set_sysimg_so + if (jl_sysimage_buf.kind != JL_IMAGE_KIND_NONE) + return jl_sysimage_buf; char *dot = (char*) strrchr(fname, '.'); int is_ji = (dot && !strcmp(dot, ".ji")); - // Get handle to sys.so - if (!is_ji) // .ji extension => load .ji file only - jl_set_sysimg_so(jl_load_dynamic_library(fname, JL_RTLD_LOCAL | JL_RTLD_NOW, 1)); + if (is_ji) { + // .ji extension => load .ji file only + ios_t f; + + if (ios_file(&f, fname, 1, 0, 0, 0) == NULL) + jl_errorf("System image file \"%s\" not found.", fname); + ios_bufmode(&f, bm_none); + + JL_SIGATOMIC_BEGIN(); + + ios_seek_end(&f); + size_t len = ios_pos(&f); + char *sysimg = (char*)jl_gc_perm_alloc(len, 0, 64, 0); + ios_seek(&f, 0); + + if (ios_readall(&f, sysimg, len) != len) + jl_errorf("Error reading system image file."); + + ios_close(&f); + + JL_SIGATOMIC_END(); + + jl_sysimage_buf = (jl_image_buf_t) { + .kind = JL_IMAGE_KIND_JI, + .handle = NULL, + .pointers = NULL, + .data = sysimg, + .size = len, + .base = 0, + }; + return jl_sysimage_buf; + } else { + // Get handle to sys.so + return jl_set_sysimg_so(jl_load_dynamic_library(fname, JL_RTLD_LOCAL | JL_RTLD_NOW, 1)); + } } -// Allow passing in a module handle directly, rather than a path -JL_DLLEXPORT void jl_set_sysimg_so(void *handle) +typedef void jl_image_unpack_func_t(void *handle, jl_image_buf_t *image); + +static void jl_prefetch_system_image(const char *data, size_t size) +{ + size_t page_size = jl_getpagesize(); /* jl_page_size is not set yet when loading sysimg */ + void *start = (void *)((uintptr_t)data & ~(page_size - 1)); + size_t size_aligned = LLT_ALIGN(size, page_size); +#ifdef _OS_WINDOWS_ + WIN32_MEMORY_RANGE_ENTRY entry = {start, size_aligned}; + PrefetchVirtualMemory(GetCurrentProcess(), 1, &entry, 0); +#else + madvise(start, size_aligned, MADV_WILLNEED); +#endif +} + +JL_DLLEXPORT void jl_image_unpack_uncomp(void *handle, jl_image_buf_t *image) +{ + size_t *plen; + jl_dlsym(handle, "jl_system_image_size", (void **)&plen, 1); + jl_dlsym(handle, "jl_system_image_data", (void **)&image->data, 1); + jl_dlsym(handle, "jl_image_pointers", (void**)&image->pointers, 1); + image->size = *plen; + jl_prefetch_system_image(image->data, image->size); +} + +JL_DLLEXPORT void jl_image_unpack_zstd(void *handle, jl_image_buf_t *image) +{ + size_t *plen; + const char *data; + jl_dlsym(handle, "jl_system_image_size", (void **)&plen, 1); + jl_dlsym(handle, "jl_system_image_data", (void **)&data, 1); + jl_dlsym(handle, "jl_image_pointers", (void **)&image->pointers, 1); + jl_prefetch_system_image(data, *plen); + image->size = ZSTD_getFrameContentSize(data, *plen); + size_t page_size = jl_getpagesize(); /* jl_page_size is not set yet when loading sysimg */ + size_t aligned_size = LLT_ALIGN(image->size, page_size); +#if defined(_OS_WINDOWS_) + size_t large_page_size = GetLargePageMinimum(); + if (image->size > 4 * large_page_size) { + size_t aligned_size = LLT_ALIGN(image->size, large_page_size); + image->data = (char *)VirtualAlloc( + NULL, aligned_size, MEM_COMMIT | MEM_RESERVE | MEM_LARGE_PAGES, PAGE_READWRITE); + } + else { + image->data = (char *)VirtualAlloc(NULL, aligned_size, MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE); + } +#else + image->data = + (char *)mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (!image->data || image->data == (void *)-1) { + jl_printf(JL_STDERR, "ERROR: failed to allocate space for system image\n"); + jl_exit(1); + } + + ZSTD_decompress((void *)image->data, image->size, data, *plen); + size_t len = (*plen) & ~(page_size - 1); +#ifdef _OS_WINDOWS_ + if (len) + VirtualFree((void *)data, len, MEM_RELEASE); +#else + munmap((void *)data, len); +#endif +} + +// From a shared library handle, verify consistency and return a jl_image_buf_t +static jl_image_buf_t get_image_buf(void *handle, int is_pkgimage) { + // verify that the linker resolved the symbols in this image against ourselves (libjulia-internal) void** (*get_jl_RTLD_DEFAULT_handle_addr)(void) = NULL; if (handle != jl_RTLD_DEFAULT_handle) { int symbol_found = jl_dlsym(handle, "get_jl_RTLD_DEFAULT_handle_addr", (void **)&get_jl_RTLD_DEFAULT_handle_addr, 0); if (!symbol_found || (void*)&jl_RTLD_DEFAULT_handle != (get_jl_RTLD_DEFAULT_handle_addr())) - jl_error("System image file failed consistency check: maybe opened the wrong version?"); + jl_error("Image file failed consistency check: maybe opened the wrong version?"); + } + + jl_image_unpack_func_t **unpack; + jl_image_buf_t image = { + .kind = JL_IMAGE_KIND_SO, + .handle = handle, + .pointers = NULL, + .data = NULL, + .size = 0, + .base = 0, + }; + + // verification passed, lookup the buffer pointers + if (jl_system_image_size == 0 || is_pkgimage) { + // in the usual case, the sysimage was not statically linked to libjulia-internal + // look up the external sysimage symbols via the dynamic linker + jl_dlsym(handle, "jl_image_unpack", (void **)&unpack, 1); + (*unpack)(handle, &image); + } + else { + // the sysimage was statically linked directly against libjulia-internal + // use the internal symbols + image.size = jl_system_image_size; + image.pointers = &jl_image_pointers; + image.data = &jl_system_image_data; } - if (jl_options.cpu_target == NULL) - jl_options.cpu_target = "native"; - jl_sysimg_handle = handle; - sysimage = jl_init_processor_sysimg(handle); + +#ifdef _OS_WINDOWS_ + image.base = (intptr_t)handle; +#else + Dl_info dlinfo; + if (dladdr((void*)image.pointers, &dlinfo) != 0) + image.base = (intptr_t)dlinfo.dli_fbase; + else + image.base = 0; +#endif + + return image; +} + +// Allow passing in a module handle directly, rather than a path +JL_DLLEXPORT jl_image_buf_t jl_set_sysimg_so(void *handle) +{ + if (jl_sysimage_buf.kind != JL_IMAGE_KIND_NONE) + return jl_sysimage_buf; + + jl_sysimage_buf = get_image_buf(handle, /* is_pkgimage */ 0); + return jl_sysimage_buf; } #ifndef JL_NDEBUG @@ -3103,7 +3227,8 @@ extern void export_jl_small_typeof(void); // into the native code of the image. See https://github.com/JuliaLang/julia/pull/52123#issuecomment-1959965395. int IMAGE_NATIVE_CODE_TAINTED = 0; -static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl_array_t *depmods, uint64_t checksum, +static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, void *image_handle, + jl_array_t *depmods, uint64_t checksum, /* outputs */ jl_array_t **restored, jl_array_t **init_order, jl_array_t **extext_methods, jl_array_t **internal_methods, jl_array_t **new_ext_cis, jl_array_t **method_roots_list, @@ -3568,8 +3693,7 @@ static void jl_restore_system_image_from_stream_(ios_t *f, jl_image_t *image, jl s.s = &sysimg; jl_update_all_fptrs(&s, image); // fptr relocs and registration if (!ccallable_list) { - // TODO: jl_sysimg_handle or img_handle? - jl_reinit_ccallable(&s.ccallable_list, image_base, jl_sysimg_handle); + jl_reinit_ccallable(&s.ccallable_list, image_base, image_handle); arraylist_free(&s.ccallable_list); } s.s = NULL; @@ -3667,7 +3791,7 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i ios_close(f); ios_static_buffer(f, sysimg, len); pkgcachesizes cachesizes; - jl_restore_system_image_from_stream_(f, image, depmods, checksum, (jl_array_t**)&restored, &init_order, &extext_methods, &internal_methods, &new_ext_cis, &method_roots_list, + jl_restore_system_image_from_stream_(f, image, pkgimage_handle, depmods, checksum, (jl_array_t**)&restored, &init_order, &extext_methods, &internal_methods, &new_ext_cis, &method_roots_list, &ext_targets, &edges, &base, &ccallable_list, &cachesizes); JL_SIGATOMIC_END(); @@ -3716,16 +3840,16 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i return restored; } -static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, uint32_t checksum) +static void jl_restore_system_image_from_stream(ios_t *f, jl_image_t *image, void *image_handle, uint32_t checksum) { JL_TIMING(LOAD_IMAGE, LOAD_Sysimg); - jl_restore_system_image_from_stream_(f, image, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + jl_restore_system_image_from_stream_(f, image, image_handle, NULL, checksum | ((uint64_t)0xfdfcfbfa << 32), NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); } -JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(void* pkgimage_handle, const char *buf, jl_image_t *image, size_t sz, jl_array_t *depmods, int completeinfo, const char *pkgname, int needs_permalloc) +JL_DLLEXPORT jl_value_t *jl_restore_incremental_from_buf(void* pkgimage_handle, jl_image_buf_t buf, jl_image_t *image, jl_array_t *depmods, int completeinfo, const char *pkgname, int needs_permalloc) { ios_t f; - ios_static_buffer(&f, (char*)buf, sz); + ios_static_buffer(&f, (char*)buf.data, buf.size); jl_value_t *ret = jl_restore_package_image_from_stream(pkgimage_handle, &f, image, depmods, completeinfo, pkgname, needs_permalloc); ios_close(&f); return ret; @@ -3744,47 +3868,22 @@ JL_DLLEXPORT jl_value_t *jl_restore_incremental(const char *fname, jl_array_t *d return ret; } -// TODO: need to enforce that the alignment of the buffer is suitable for vectors -JL_DLLEXPORT void jl_restore_system_image(const char *fname) +JL_DLLEXPORT void jl_restore_system_image(jl_image_t *image, jl_image_buf_t buf) { -#ifndef JL_NDEBUG - char *dot = fname ? (char*)strrchr(fname, '.') : NULL; - int is_ji = (dot && !strcmp(dot, ".ji")); - assert((is_ji || jl_sysimg_handle) && "System image file not preloaded"); -#endif + ios_t f; - if (jl_sysimg_handle) { - // load the pre-compiled sysimage from jl_sysimg_handle - jl_load_sysimg_so(); - } - else { - ios_t f; - if (ios_file(&f, fname, 1, 0, 0, 0) == NULL) - jl_errorf("System image file \"%s\" not found.", fname); - ios_bufmode(&f, bm_none); - JL_SIGATOMIC_BEGIN(); - ios_seek_end(&f); - size_t len = ios_pos(&f); - char *sysimg = (char*)jl_gc_perm_alloc(len, 0, 64, 0); - ios_seek(&f, 0); - if (ios_readall(&f, sysimg, len) != len) - jl_errorf("Error reading system image file."); - ios_close(&f); - uint32_t checksum = jl_crc32c(0, sysimg, len); - ios_static_buffer(&f, sysimg, len); - jl_restore_system_image_from_stream(&f, &sysimage, checksum); - ios_close(&f); - JL_SIGATOMIC_END(); - } -} + if (buf.kind == JL_IMAGE_KIND_NONE) + return; + + if (buf.kind == JL_IMAGE_KIND_SO) + assert(image->fptrs.ptrs); // jl_init_processor_sysimg should already be run -JL_DLLEXPORT void jl_restore_system_image_data(const char *buf, size_t len) -{ - ios_t f; JL_SIGATOMIC_BEGIN(); - ios_static_buffer(&f, (char*)buf, len); - uint32_t checksum = jl_crc32c(0, buf, len); - jl_restore_system_image_from_stream(&f, &sysimage, checksum); + ios_static_buffer(&f, (char *)buf.data, buf.size); + + uint32_t checksum = jl_crc32c(0, buf.data, buf.size); + jl_restore_system_image_from_stream(&f, image, buf.handle, checksum); + ios_close(&f); JL_SIGATOMIC_END(); } @@ -3803,12 +3902,11 @@ JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, j #endif jl_errorf("Error opening package file %s: %s\n", fname, reason); } - const char *pkgimg_data; - jl_dlsym(pkgimg_handle, "jl_system_image_data", (void **)&pkgimg_data, 1); - size_t *plen; - jl_dlsym(pkgimg_handle, "jl_system_image_size", (void **)&plen, 1); - jl_image_t pkgimage = jl_init_processor_pkgimg(pkgimg_handle); + jl_image_buf_t buf = get_image_buf(pkgimg_handle, /* is_pkgimage */ 1); + + // Despite the name, this function actually parses the pkgimage + jl_image_t pkgimage = jl_init_processor_pkgimg(buf); if (ignore_native) { // Must disable using native code in possible downstream users of this code: @@ -3817,7 +3915,7 @@ JL_DLLEXPORT jl_value_t *jl_restore_package_image_from_file(const char *fname, j IMAGE_NATIVE_CODE_TAINTED = 1; } - jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_handle, pkgimg_data, &pkgimage, *plen, depmods, completeinfo, pkgname, 0); + jl_value_t* mod = jl_restore_incremental_from_buf(pkgimg_handle, buf, &pkgimage, depmods, completeinfo, pkgname, 0); return mod; } diff --git a/src/threading.c b/src/threading.c index 62af7e60bf0f1..a17d3b371acf5 100644 --- a/src/threading.c +++ b/src/threading.c @@ -250,10 +250,6 @@ void jl_set_pgcstack(jl_gcframe_t **pgcstack) JL_NOTSAFEPOINT { *jl_pgcstack_key() = pgcstack; } -# if JL_USE_IFUNC -JL_DLLEXPORT __attribute__((weak)) -void jl_register_pgcstack_getter(void); -# endif static jl_gcframe_t **jl_get_pgcstack_init(void); static jl_get_pgcstack_func *jl_get_pgcstack_cb = jl_get_pgcstack_init; static jl_gcframe_t **jl_get_pgcstack_init(void) @@ -266,15 +262,8 @@ static jl_gcframe_t **jl_get_pgcstack_init(void) // This is clearly not thread-safe but should be fine since we // make sure the tls states callback is finalized before adding // multiple threads -# if JL_USE_IFUNC - if (jl_register_pgcstack_getter) - jl_register_pgcstack_getter(); - else -# endif - { - jl_get_pgcstack_cb = jl_get_pgcstack_fallback; - jl_pgcstack_key = &jl_pgcstack_addr_fallback; - } + jl_get_pgcstack_cb = jl_get_pgcstack_fallback; + jl_pgcstack_key = &jl_pgcstack_addr_fallback; return jl_get_pgcstack_cb(); } diff --git a/stdlib/Makefile b/stdlib/Makefile index d478d2ad4c188..eb08979987f75 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -19,7 +19,7 @@ $(foreach dir,$(DIRS),$(eval $(call dir_target,$(dir)))) JLLS = DSFMT GMP CURL LIBGIT2 LLVM LIBSSH2 LIBUV MBEDTLS MPFR NGHTTP2 \ BLASTRAMPOLINE OPENBLAS OPENLIBM P7ZIP PCRE LIBSUITESPARSE ZLIB \ - LLVMUNWIND CSL UNWIND LLD + ZSTD LLVMUNWIND CSL UNWIND LLD # Initialize this with JLLs that aren't in "deps/$(LibName).version" JLL_NAMES := MozillaCACerts_jll diff --git a/stdlib/Manifest.toml b/stdlib/Manifest.toml index 5ad675ac21510..489baa7b9732d 100644 --- a/stdlib/Manifest.toml +++ b/stdlib/Manifest.toml @@ -268,6 +268,11 @@ deps = ["Libdl"] uuid = "83775a58-1f1d-513f-b197-d71354ab007a" version = "1.2.13+1" +[[deps.Zstd_jll]] +deps = ["Libdl"] +uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" +version = "1.5.7+1" + [[deps.dSFMT_jll]] deps = ["Artifacts", "Libdl"] uuid = "05ff407c-b0c1-5878-9df8-858cc2e60c36" diff --git a/stdlib/Zstd_jll/Project.toml b/stdlib/Zstd_jll/Project.toml new file mode 100644 index 0000000000000..467516843390a --- /dev/null +++ b/stdlib/Zstd_jll/Project.toml @@ -0,0 +1,15 @@ +name = "Zstd_jll" +uuid = "3161d3a3-bdf6-5164-811a-617609db77b4" +version = "1.5.7+1" + +[deps] +Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" + +[compat] +julia = "1.6" + +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/stdlib/Zstd_jll/src/Zstd_jll.jl b/stdlib/Zstd_jll/src/Zstd_jll.jl new file mode 100644 index 0000000000000..c16413f963d0b --- /dev/null +++ b/stdlib/Zstd_jll/src/Zstd_jll.jl @@ -0,0 +1,73 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## dummy stub for https://github.com/JuliaBinaryWrappers/Zstd_jll.j: +# +baremodule Zstd_jll +using Base, Libdl + +export libzstd, zstd, zstdmt + +# These get calculated in __init__() +libzstd_handle::Ptr{Cvoid} = C_NULL + +if Sys.iswindows() + const libzstd = "libzstd-1.dll" +elseif Sys.isapple() + const libzstd = "@rpath/libzstd.1.dylib" +else + const libzstd = "libzstd.so.1" +end + +if Sys.iswindows() + const zstd_exe = "zstd.exe" + const zstdmt_exe = "zstdmt.exe" +else + const zstd_exe = "zstd" + const zstdmt_exe = "zstdmt" +end + +if Sys.iswindows() + const pathsep = ';' +elseif Sys.isapple() + const pathsep = ':' +else + const pathsep = ':' +end + +if Sys.iswindows() +function adjust_ENV(cmd::Cmd) + dllPATH = Sys.BINDIR + oldPATH = get(ENV, "PATH", "") + newPATH = isempty(oldPATH) ? dllPATH : "$dllPATH$pathsep$oldPATH" + return addenv(cmd, "PATH"=>newPATH) +end +else +adjust_ENV(cmd::Cmd) = cmd +end + +function adjust_ENV() + addPATH = joinpath(Sys.BINDIR, Base.PRIVATE_LIBEXECDIR) + oldPATH = get(ENV, "PATH", "") + newPATH = isempty(oldPATH) ? addPATH : "$addPATH$pathsep$oldPATH" + return ("PATH"=>newPATH,) +end + +function zstd(f::Function; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true) # deprecated, for compat only + withenv((adjust_PATH ? adjust_ENV() : ())...) do + f(zstd()) + end +end +function zstdmt(f::Function; adjust_PATH::Bool = true, adjust_LIBPATH::Bool = true) # deprecated, for compat only + withenv((adjust_PATH ? adjust_ENV() : ())...) do + f(zstdmt()) + end +end +zstd() = adjust_ENV(`$(joinpath(Sys.BINDIR, Base.PRIVATE_LIBEXECDIR, zstd_exe))`) +zstdmt() = adjust_ENV(`$(joinpath(Sys.BINDIR, Base.PRIVATE_LIBEXECDIR, zstdmt_exe))`) + +function __init__() + global libzstd_handle = dlopen(libzstd) + nothing +end + +end # module Zstd_jll diff --git a/stdlib/Zstd_jll/test/runtests.jl b/stdlib/Zstd_jll/test/runtests.jl new file mode 100644 index 0000000000000..5cfa2a1375c73 --- /dev/null +++ b/stdlib/Zstd_jll/test/runtests.jl @@ -0,0 +1,7 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Test, Zstd_jll + +@testset "Zstd_jll" begin + @test ccall((:ZSTD_versionNumber, libzstd), Cuint, ()) == 1_05_07 +end diff --git a/stdlib/stdlib.mk b/stdlib/stdlib.mk index 021639ec0bad0..9670fe49bc0fe 100644 --- a/stdlib/stdlib.mk +++ b/stdlib/stdlib.mk @@ -9,7 +9,7 @@ INDEPENDENT_STDLIBS := \ SparseArrays Statistics StyledStrings SuiteSparse_jll Tar Test TOML Unicode UUIDs \ dSFMT_jll GMP_jll libLLVM_jll LLD_jll LLVMLibUnwind_jll LibUnwind_jll LibUV_jll \ LibCURL_jll LibSSH2_jll LibGit2_jll nghttp2_jll MozillaCACerts_jll MbedTLS_jll \ - MPFR_jll OpenLibm_jll PCRE2_jll p7zip_jll Zlib_jll + MPFR_jll OpenLibm_jll PCRE2_jll p7zip_jll Zlib_jll Zstd_jll STDLIBS := $(STDLIBS_WITHIN_SYSIMG) $(INDEPENDENT_STDLIBS) VERSDIR := v$(shell cut -d. -f1-2 < $(JULIAHOME)/VERSION) diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index 43cdf5e2696a6..1c70c5d3f2cab 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -979,7 +979,7 @@ let exename = `$(Base.julia_cmd().exec[1]) -t 1` p = run(pipeline(`$exename --sysimage=$libjulia`, stderr=err), wait=false) close(err.in) let s = read(err, String) - @test s == "ERROR: System image file failed consistency check: maybe opened the wrong version?\n" + @test s == "ERROR: Image file failed consistency check: maybe opened the wrong version?\n" end @test errors_not_signals(p) @test p.exitcode == 1