--- old/common/autoconf/build-aux/config.guess +++ new/common/autoconf/build-aux/config.guess @@ -30,6 +30,17 @@ DIR=`dirname $0` OUT=`. $DIR/autoconf-config.guess` +# config.guess doesn't identify systems running the musl C library, and will +# instead return a string with a -gnu suffix. This block detects musl and +# modifies the string to have a -musl suffix instead. +echo $OUT | grep -- -linux- > /dev/null 2> /dev/null +if test $? = 0; then + ldd_version=`ldd --version 2>&1 | head -1 | cut -f1 -d' '` + if [ x"${ldd_version}" = x"musl" ]; then + OUT=`echo $OUT | sed 's/-gnu/-musl/'` + fi +fi + # Test and fix solaris on x86_64 echo $OUT | grep i386-pc-solaris > /dev/null 2> /dev/null if test $? = 0; then --- old/hotspot/make/lib/CompileJvm.gmk +++ new/hotspot/make/lib/CompileJvm.gmk @@ -135,6 +135,7 @@ -DHOTSPOT_BUILD_USER='"$(USERNAME)"' \ -DHOTSPOT_VM_DISTRO='"$(HOTSPOT_VM_DISTRO)"' \ -DCPU='"$(OPENJDK_TARGET_CPU_VM_VERSION)"' \ + -DLIBC='"musl"' \ # # -DDONT_USE_PRECOMPILED_HEADER will exclude all includes in precompiled.hpp. --- old/hotspot/src/os/linux/vm/os_linux.cpp +++ new/hotspot/src/os/linux/vm/os_linux.cpp @@ -98,7 +98,6 @@ # include # include # include -# include # include # include # include @@ -496,6 +495,11 @@ // detecting pthread library void os::Linux::libpthread_init() { +#if !defined(__GLIBC__) && !defined(__UCLIBC__) + // Hard code Alpine Linux supported musl compatible settings + os::Linux::set_glibc_version("glibc 2.9"); + os::Linux::set_libpthread_version("NPTL"); +#else // Save glibc and pthread version strings. #if !defined(_CS_GNU_LIBC_VERSION) || \ !defined(_CS_GNU_LIBPTHREAD_VERSION) @@ -513,6 +517,7 @@ str = (char *)malloc(n, mtInternal); confstr(_CS_GNU_LIBPTHREAD_VERSION, str, n); os::Linux::set_libpthread_version(str); +#endif } ///////////////////////////////////////////////////////////////////////////// @@ -2803,17 +2808,32 @@ extern "C" JNIEXPORT void numa_warn(int number, char *where, ...) { } extern "C" JNIEXPORT void numa_error(char *where) { } +static void* dlvsym_if_available(void* handle, const char* name, const char* version) { + typedef void* (*dlvsym_func_type)(void* handle, const char* name, const char* version); + static dlvsym_func_type dlvsym_func; + static bool initialized = false; + if (!initialized) { + dlvsym_func = (dlvsym_func_type)dlsym(RTLD_NEXT, "dlvsym"); + initialized = true; + } + + if (dlvsym_func != NULL) { + void *f = dlvsym_func(handle, name, version); + if (f != NULL) { + return f; + } + } + + return dlsym(handle, name); +} + // If we are running with libnuma version > 2, then we should // be trying to use symbols with versions 1.1 // If we are running with earlier version, which did not have symbol versions, // we should use the base version. void* os::Linux::libnuma_dlsym(void* handle, const char *name) { - void *f = dlvsym(handle, name, "libnuma_1.1"); - if (f == NULL) { - f = dlsym(handle, name); - } - return f; + return dlvsym_if_available(handle, name, "libnuma_1.1"); } bool os::Linux::libnuma_init() { --- old/hotspot/src/os/linux/vm/os_linux.inline.hpp +++ new/hotspot/src/os/linux/vm/os_linux.inline.hpp @@ -31,7 +31,7 @@ #include #include -#include +#include #include // File names are case-sensitive on windows only --- old/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp +++ new/hotspot/src/os_cpu/linux_x86/vm/os_linux_x86.cpp @@ -73,7 +73,6 @@ # include # include # include -# include #ifdef AMD64 #define REG_SP REG_RSP --- old/hotspot/src/share/vm/gc/shared/genCollectedHeap.cpp +++ new/hotspot/src/share/vm/gc/shared/genCollectedHeap.cpp @@ -1017,7 +1017,7 @@ static ScratchBlock *removeSmallestScratch(ScratchBlock **prev_ptr) { bool first = true; size_t min_size = 0; // "first" makes this conceptually infinite. - ScratchBlock **smallest_ptr, *smallest; + ScratchBlock **smallest_ptr = NULL, *smallest; ScratchBlock *cur = *prev_ptr; while (cur) { assert(*prev_ptr == cur, "just checking"); --- old/hotspot/src/share/vm/runtime/vm_version.cpp +++ new/hotspot/src/share/vm/runtime/vm_version.cpp @@ -263,7 +263,7 @@ #endif #define INTERNAL_VERSION_SUFFIX VM_RELEASE ")" \ - " for " OS "-" CPU FLOAT_ARCH_STR \ + " for " OS "-" CPU FLOAT_ARCH_STR LIBC \ " JRE (" VERSION_STRING "), built on " __DATE__ " " __TIME__ \ " by " XSTR(HOTSPOT_BUILD_USER) " with " HOTSPOT_BUILD_COMPILER --- old/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp +++ new/hotspot/src/share/vm/utilities/globalDefinitions_gcc.hpp @@ -216,7 +216,7 @@ #elif defined(__APPLE__) inline int g_isnan(double f) { return isnan(f); } #elif defined(LINUX) || defined(_ALLBSD_SOURCE) -inline int g_isnan(float f) { return isnanf(f); } +inline int g_isnan(float f) { return isnan(f); } inline int g_isnan(double f) { return isnan(f); } #else #error "missing platform-specific definition here" --- old/hotspot/test/runtime/StackGuardPages/exeinvoke.c +++ new/hotspot/test/runtime/StackGuardPages/exeinvoke.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -91,6 +92,20 @@ } } +int get_java_stacksize () { + size_t stacksize; + pthread_attr_t attr; + JDK1_1InitArgs jdk_args; + + jdk_args.version = JNI_VERSION_1_1; + JNI_GetDefaultJavaVMInitArgs(&jdk_args); + if (jdk_args.javaStackSize <= 0) { + fprintf(stderr, "Test ERROR. Can't get a valid value for the default stacksize.\n"); + exit(7); + } + return jdk_args.javaStackSize; +} + void *run_java_overflow (void *p) { JNIEnv *env; jclass class_id; @@ -254,13 +269,19 @@ exit(7); } + int stack_size = get_java_stacksize(); pthread_t thr; + pthread_attr_t thread_attr; + pthread_attr_init(&thread_attr); + pthread_attr_setstacksize(&thread_attr, stack_size); + if (argc > 1 && strcmp(argv[1], "test_java_overflow") == 0) { printf("\nTesting JAVA_OVERFLOW\n"); printf("Testing stack guard page behaviour for other thread\n"); - pthread_create (&thr, NULL, run_java_overflow, NULL); + + pthread_create (&thr, &thread_attr, run_java_overflow, NULL); pthread_join (thr, NULL); printf("Testing stack guard page behaviour for initial thread\n"); @@ -273,7 +294,7 @@ printf("\nTesting NATIVE_OVERFLOW\n"); printf("Testing stack guard page behaviour for other thread\n"); - pthread_create (&thr, NULL, run_native_overflow, NULL); + pthread_create (&thr, &thread_attr, run_native_overflow, NULL); pthread_join (thr, NULL); printf("Testing stack guard page behaviour for initial thread\n"); --- old/jdk/make/lib/CoreLibraries.gmk +++ new/jdk/make/lib/CoreLibraries.gmk @@ -339,6 +339,7 @@ endif LIBJLI_CFLAGS += $(addprefix -I, $(LIBJLI_SRC_DIRS)) +LIBJLI_CFLAGS += -DLIBC=\"musl\" ifneq ($(USE_EXTERNAL_LIBZ), true) LIBJLI_CFLAGS += $(ZLIB_CPPFLAGS) --- old/jdk/src/java.base/linux/native/libnet/linux_close.c +++ new/jdk/src/java.base/linux/native/libnet/linux_close.c @@ -58,7 +58,7 @@ /* * Signal to unblock thread */ -static int sigWakeup = (__SIGRTMAX - 2); +static int sigWakeup; /* * fdTable holds one entry per file descriptor, up to a certain @@ -147,6 +147,7 @@ /* * Setup the signal handler */ + sigWakeup = SIGRTMAX - 2; sa.sa_handler = sig_wakeup; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); --- old/jdk/src/java.base/unix/native/include/jvm_md.h +++ new/jdk/src/java.base/unix/native/include/jvm_md.h @@ -65,7 +65,7 @@ #include #include #include -#include +#include /* O Flags */ --- old/jdk/src/java.base/unix/native/libjava/childproc.c +++ new/jdk/src/java.base/unix/native/libjava/childproc.c @@ -237,7 +237,13 @@ { if (envp == NULL || (char **) envp == environ) { execvp(file, (char **) argv); - return; + // ENOEXEC indicates that the file header was not recognized. The musl C + // library does not implement the fallback to /bin/sh for that case, so fall + // through to the code below which implements that fallback using + // execve_with_shell_fallback. + if (errno != ENOEXEC) { + return; + } } if (*file == '\0') { --- old/jdk/src/java.base/unix/native/libjava/jdk_util_md.h +++ new/jdk/src/java.base/unix/native/libjava/jdk_util_md.h @@ -37,7 +37,7 @@ #define ISNAND(d) isnan(d) #elif defined(__linux__) || defined(_ALLBSD_SOURCE) #include -#define ISNANF(f) isnanf(f) +#define ISNANF(f) isnan(f) #define ISNAND(d) isnan(d) #elif defined(_AIX) #include --- old/jdk/src/java.base/unix/native/libjli/java_md_solinux.c +++ new/jdk/src/java.base/unix/native/libjli/java_md_solinux.c @@ -241,6 +241,39 @@ char *dmllp = NULL; char *p; /* a utility pointer */ +#ifdef __linux +#ifndef LIBC +#error "LIBC not set" +#endif + + if (strcmp(LIBC, "musl") == 0) { + /* + * The musl library loader requires LD_LIBRARY_PATH to be set in + * order to correctly resolve the dependency libjava.so has on libjvm.so. + * + * Specifically, it differs from glibc in the sense that even if + * libjvm.so has already been loaded it will not be considered a + * candidate for resolving the dependency unless the *full* path + * of the already loaded library matches the dependency being loaded. + * + * libjvm.so is being loaded by the launcher using a long path to + * dlopen, not just the basename of the library. Typically this + * is something like "../lib/server/libjvm.so". However, if/when + * libjvm.so later tries to dlopen libjava.so (which it does in + * order to get access to a few functions implemented in + * libjava.so) the musl loader will, as part of loading + * dependent libraries, try to load libjvm.so using only its + * basename "libjvm.so". Since this does not match the longer + * path path it was first loaded with, the already loaded + * library is not considered a candidate, and the loader will + * instead look for libjvm.so elsewhere. If it's not in + * LD_LIBRARY_PATH the dependency load will fail, and libjava.so + * will therefore fail as well. + */ + return JNI_TRUE; + } +#endif + #ifdef AIX /* We always have to set the LIBPATH on AIX because ld doesn't support $ORIGIN. */ return JNI_TRUE; --- old/jdk/src/java.base/unix/native/libnet/net_util_md.h +++ new/jdk/src/java.base/unix/native/libnet/net_util_md.h @@ -27,7 +27,7 @@ #define NET_UTILS_MD_H #include -#include +#include #include /************************************************************************ --- old/jdk/src/java.base/unix/native/libnio/ch/NativeThread.c +++ new/jdk/src/java.base/unix/native/libnio/ch/NativeThread.c @@ -36,7 +36,7 @@ #include #include /* Also defined in net/linux_close.c */ - #define INTERRUPT_SIGNAL (__SIGRTMAX - 2) + #define INTERRUPT_SIGNAL (SIGRTMAX - 2) #elif _AIX #include #include --- old/jdk/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c +++ new/jdk/src/java.desktop/unix/native/libawt_xawt/xawt/XToolkit.c @@ -27,9 +27,6 @@ #include #include #include -#ifdef __linux__ -#include -#endif #include #include @@ -786,26 +783,6 @@ } return ret; } - -#ifdef __linux__ -void print_stack(void) -{ - void *array[10]; - size_t size; - char **strings; - size_t i; - - size = backtrace (array, 10); - strings = backtrace_symbols (array, size); - - fprintf (stderr, "Obtained %zd stack frames.\n", size); - - for (i = 0; i < size; i++) - fprintf (stderr, "%s\n", strings[i]); - - free (strings); -} -#endif Window get_xawt_root_shell(JNIEnv *env) { static jclass classXRootWindow = NULL; --- old/jdk/test/java/lang/ProcessBuilder/Basic.java +++ new/jdk/test/java/lang/ProcessBuilder/Basic.java @@ -391,8 +391,8 @@ if (failed != 0) throw new Error("null PATH"); } else if (action.equals("PATH search algorithm")) { equal(System.getenv("PATH"), "dir1:dir2:"); - check(new File("/bin/true").exists()); - check(new File("/bin/false").exists()); + check(new File(TrueExe.path()).exists()); + check(new File(FalseExe.path()).exists()); String[] cmd = {"prog"}; ProcessBuilder pb1 = new ProcessBuilder(cmd); ProcessBuilder pb2 = new ProcessBuilder(cmd); @@ -433,13 +433,13 @@ checkPermissionDenied(pb); // continue searching if EACCES - copy("/bin/true", "dir2/prog"); + copy(TrueExe.path(), "dir2/prog"); equal(run(pb).exitValue(), True.exitValue()); new File("dir1/prog").delete(); new File("dir2/prog").delete(); new File("dir2/prog").mkdirs(); - copy("/bin/true", "dir1/prog"); + copy(TrueExe.path(), "dir1/prog"); equal(run(pb).exitValue(), True.exitValue()); // Check empty PATH component means current directory. @@ -455,10 +455,10 @@ pb.command(command); File prog = new File("./prog"); // "Normal" binaries - copy("/bin/true", "./prog"); + copy(TrueExe.path(), "./prog"); equal(run(pb).exitValue(), True.exitValue()); - copy("/bin/false", "./prog"); + copy(FalseExe.path(), "./prog"); equal(run(pb).exitValue(), False.exitValue()); prog.delete(); @@ -513,12 +513,12 @@ new File("dir2/prog").delete(); new File("prog").delete(); new File("dir3").mkdirs(); - copy("/bin/true", "dir1/prog"); - copy("/bin/false", "dir3/prog"); + copy(TrueExe.path(), "dir1/prog"); + copy(FalseExe.path(), "dir3/prog"); pb.environment().put("PATH","dir3"); equal(run(pb).exitValue(), True.exitValue()); - copy("/bin/true", "dir3/prog"); - copy("/bin/false", "dir1/prog"); + copy(TrueExe.path(), "dir3/prog"); + copy(FalseExe.path(), "dir1/prog"); equal(run(pb).exitValue(), False.exitValue()); } finally { @@ -615,6 +615,13 @@ new File("/bin/false").exists()); } + static class BusyBox { + public static boolean is() { return is; } + private static final boolean is = + (! Windows.is() && + new File("/bin/busybox").exists()); + } + static class UnicodeOS { public static boolean is() { return is; } private static final String osName = System.getProperty("os.name"); @@ -653,6 +660,45 @@ } } + // On alpine linux, /bin/true and /bin/false are just links to /bin/busybox. + // Some tests copy /bin/true and /bin/false to files with a different filename. + // However, copying the busbox executable into a file with a different name + // won't result in the expected return codes. As workaround, we create + // executable files that can be copied and produce the exepected return + // values. We use this workaround, if we find the busybox executable. + + private static class TrueExe { + public static String path() { return path; } + private static final String path = path0(); + private static String path0(){ + if (!BusyBox.is()) { + return "/bin/true"; + } + else { + File trueExe = new File("true"); + setFileContents(trueExe, "#!/bin/true\n"); + trueExe.setExecutable(true); + return trueExe.getAbsolutePath(); + } + } + } + + private static class FalseExe { + public static String path() { return path; } + private static final String path = path0(); + private static String path0(){ + if (!BusyBox.is()) { + return "/bin/false"; + } + else { + File falseExe = new File("false"); + setFileContents(falseExe, "#!/bin/false\n"); + falseExe.setExecutable(true); + return falseExe.getAbsolutePath(); + } + } + } + static class EnglishUnix { private static final Boolean is = (! Windows.is() && isEnglish("LANG") && isEnglish("LC_ALL")); @@ -1956,7 +2002,7 @@ //---------------------------------------------------------------- try { new File("suBdiR").mkdirs(); - copy("/bin/true", "suBdiR/unliKely"); + copy(TrueExe.path(), "suBdiR/unliKely"); final ProcessBuilder pb = new ProcessBuilder(new String[]{"unliKely"}); pb.environment().put("PATH", "suBdiR"); --- old/jdk/test/java/lang/ProcessHandle/InfoTest.java +++ new/jdk/test/java/lang/ProcessHandle/InfoTest.java @@ -293,7 +293,14 @@ } if (info.command().isPresent()) { String command = info.command().get(); - String expected = Platform.isWindows() ? "sleep.exe" : "sleep"; + String expected = "sleep"; + if (Platform.isWindows()) { + expected = "sleep.exe"; + } else if (new File("/bin/busybox").exists()) { + // With busybox sleep is just a sym link to busybox. + // The busbox executable is seen as ProcessHandle.Info command. + expected = "busybox"; + } Assert.assertTrue(command.endsWith(expected), "Command: expected: \'" + expected + "\', actual: " + command); --- old/make/ReleaseFile.gmk +++ new/make/ReleaseFile.gmk @@ -50,6 +50,7 @@ $(call info-file-item, "IMPLEMENTOR", "$(COMPANY_NAME)") $(call info-file-item, "OS_NAME", "$(RELEASE_FILE_OS_NAME)") $(call info-file-item, "OS_ARCH", "$(RELEASE_FILE_OS_ARCH)") + $(call info-file-item, "LIBC", "musl") endef # Param 1 - The file containing the MODULES list