diff --git a/make/Main.gmk b/make/Main.gmk index d0568509a4e5b..b7d126f17174c 100644 --- a/make/Main.gmk +++ b/make/Main.gmk @@ -461,9 +461,9 @@ $(eval $(call SetupTarget, symbols-image, \ TARGET := symbols, \ )) -$(eval $(call SetupTarget, static-launcher, \ +$(eval $(call SetupTarget, static-launchers, \ MAKEFILE := StaticLibs, \ - TARGET := static-launcher, \ + TARGET := static-launchers, \ DEPS := hotspot-static-libs static-libs, \ )) @@ -1290,7 +1290,7 @@ ifeq ($(call isTargetOs, macosx), true) legacy-images: mac-legacy-jre-bundle endif -static-exploded-image: static-launcher exploded-image +static-exploded-image: static-launchers exploded-image # These targets build the various documentation images docs-jdk-image: docs-jdk diff --git a/make/ModuleWrapper.gmk b/make/ModuleWrapper.gmk index b3ddf940e008c..26d0b500624fc 100644 --- a/make/ModuleWrapper.gmk +++ b/make/ModuleWrapper.gmk @@ -64,6 +64,23 @@ ifeq ($(MAKEFILE_PREFIX), Lib) endif endif +ifeq ($(MAKEFILE_PREFIX), Launcher) + # We need to keep track of which launchers are created by this module. This + # information is required for static builds, to know which relaunchers to + # create. The variable $(MODULE)_INCLUDED_LAUNCHERS is added to for each call + # to SetupBuildLauncher. The file module-included-launchers.txt is then read + # in StaticLibs.gmk. + ifneq ($($(MODULE)_INCLUDED_LAUNCHERS), ) + LAUNCHERS_LIST := $(SUPPORT_OUTPUTDIR)/modules_static-launchers/$(MODULE)/module-included-launchers.txt + + $(LAUNCHERS_LIST): $(TARGETS) + $(call MakeDir, $(@D)) + $(ECHO) $($(MODULE)_INCLUDED_LAUNCHERS) > $@ + + TARGETS += $(LAUNCHERS_LIST) + endif +endif + # Setup copy rules from the modules directories to the jdk image directory. ifeq ($(call isTargetOs, windows), true) TO_BIN_FILTER := %$(SHARED_LIBRARY_SUFFIX) %.diz %.pdb %.map diff --git a/make/StaticLibs.gmk b/make/StaticLibs.gmk index 3cf2a4dd136b1..6e2a931206d5e 100644 --- a/make/StaticLibs.gmk +++ b/make/StaticLibs.gmk @@ -48,8 +48,8 @@ ifneq ($(word 2, $(wildcard $(HOTSPOT_STATIC_LIB_PATH))), ) endif # Find all modules with static libraries -STATIC_LIB_MODULES := $(patsubst $(SUPPORT_OUTPUTDIR)/modules_static-libs/%, \ - %, $(wildcard $(SUPPORT_OUTPUTDIR)/modules_static-libs/*)) +STATIC_LIB_MODULES := $(sort $(patsubst $(SUPPORT_OUTPUTDIR)/modules_static-libs/%, \ + %, $(wildcard $(SUPPORT_OUTPUTDIR)/modules_static-libs/*))) # Filter out known broken libraries. This is a temporary measure until # proper support for these libraries can be provided. @@ -120,13 +120,18 @@ else $(error Unsupported platform) endif +################################################################################ +# Build the java static launcher +################################################################################ $(eval $(call SetupBuildLauncher, java, \ ENABLE_ARG_FILES := true, \ EXPAND_CLASSPATH_WILDCARDS := true, \ EXTRA_RCFLAGS := $(JAVA_RCFLAGS), \ VERSION_INFO_RESOURCE := $(JAVA_VERSION_INFO_RESOURCE), \ OPTIMIZATION := HIGH, \ + MACOSX_PRIVILEGED := true, \ STATIC_LAUNCHER := true, \ + CFLAGS := -DSTATIC_BUILD, \ LDFLAGS := $(LDFLAGS_STATIC_JDK), \ LIBS := $(STATIC_LIBS) $(EXTERNAL_LIBS), \ LINK_TYPE := C++, \ @@ -143,7 +148,53 @@ TARGETS += $(java) JAVA_LAUNCHER := $(BUILD_LAUNCHER_java_TARGET) -static-launcher: $(java) +static-launchers: $(java) + +################################################################################ +# Build relaunchers (thin wrappers calling the java binary) for all other +# JDK launchers. +################################################################################ + +RELAUNCHER_SRC := $(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/launcher + +# $1: The module name +# $2: The launcher name +define SetupRelauncher + $1_$2_LAUNCHER_ARGS_LINE := $$(shell cat $$(SUPPORT_OUTPUTDIR)/static-native/relaunchers/$1/$2-relauncher-arguments.txt) + # Restore |||| with space + $1_$2_LAUNCHER_ARGS := '{ $$(subst ||||,$(SPACE),$$(strip $$(foreach a, $$($1_$2_LAUNCHER_ARGS_LINE), "-J$$a"$$(COMMA) )) ) }' + + $$(eval $$(call SetupJdkExecutable, BUILD_relauncher_$2, \ + NAME := $2, \ + EXTRA_FILES := $$(RELAUNCHER_SRC)/relauncher.c, \ + CFLAGS := -DLAUNCHER_ARGS=$$($1_$2_LAUNCHER_ARGS), \ + LIBS_windows := shlwapi.lib, \ + OUTPUT_DIR := $$(STATIC_LAUNCHER_OUTPUT_DIR), \ + OBJECT_DIR := $$(STATIC_LAUNCHER_OUTPUT_DIR)/relaunchers/$2, \ + )) + + TARGETS += $$(BUILD_relauncher_$2) + + RELAUNCHERS += $$(BUILD_relauncher_$2_TARGET) + static-launchers: $$(BUILD_relauncher_$2) +endef + +# Find all modules with launchers +LAUNCHER_MODULES := $(sort $(patsubst $(SUPPORT_OUTPUTDIR)/modules_static-launchers/%, \ + %, $(wildcard $(SUPPORT_OUTPUTDIR)/modules_static-launchers/*))) + +# Find launchers for each module +$(foreach module, $(LAUNCHER_MODULES), \ + $(eval LAUNCHERS_$(module) := $(if $(wildcard \ + $(SUPPORT_OUTPUTDIR)/modules_static-launchers/$(module)/module-included-launchers.txt), \ + $(shell cat \ + $(SUPPORT_OUTPUTDIR)/modules_static-launchers/$(module)/module-included-launchers.txt))) \ +) + +# For all launchers (except java and javaw), setup a relauncher build +$(foreach module, $(LAUNCHER_MODULES), \ + $(foreach launcher, $(filter-out java javaw, $(LAUNCHERS_$(module))), \ + $(eval $(call SetupRelauncher,$(module),$(launcher))))) ################################################################################ # @@ -185,26 +236,26 @@ TARGETS += $(copy-from-jdk-image) $(copy-from-jdk-image): | static-jdk-info -$(eval $(call SetupCopyFiles, copy-static-launcher, \ - FILES := $(JAVA_LAUNCHER), \ +$(eval $(call SetupCopyFiles, copy-static-launchers, \ + FILES := $(JAVA_LAUNCHER) $(RELAUNCHERS), \ DEST := $(STATIC_JDK_IMAGE_DIR)/bin, \ )) -TARGETS += $(copy-static-launcher) +TARGETS += $(copy-static-launchers) -$(eval $(call SetupCopyFiles, copy-static-launcher-debuginfo, \ +$(eval $(call SetupCopyFiles, copy-static-launchers-debuginfo, \ SRC := $(STATIC_LAUNCHER_OUTPUT_DIR), \ DEST := $(STATIC_JDK_IMAGE_DIR)/bin, \ FILES := $(call FindDebuginfoFiles, $(STATIC_LAUNCHER_OUTPUT_DIR)), \ )) -TARGETS += $(copy-static-launcher-debuginfo) +TARGETS += $(copy-static-launchers-debuginfo) -static-jdk-image: $(copy-from-jdk-image) $(copy-static-launcher) $(copy-static-launcher-debuginfo) +static-jdk-image: $(copy-from-jdk-image) $(copy-static-launchers) $(copy-static-launchers-debuginfo) TARGETS += static-jdk-image -.PHONY: static-launcher static-jdk-image +.PHONY: static-launchers static-jdk-image ################################################################################ diff --git a/make/common/modules/LauncherCommon.gmk b/make/common/modules/LauncherCommon.gmk index 700c0de74d564..558c911ff9f2a 100644 --- a/make/common/modules/LauncherCommon.gmk +++ b/make/common/modules/LauncherCommon.gmk @@ -74,19 +74,35 @@ define SetupBuildLauncherBody $1_MAIN_MODULE := $(MODULE) + # Record the fact that this launcher is part of the current module. This + # variable stores information about all created launchers, and is read by + # ModuleWrapper. + $$(MODULE)_INCLUDED_LAUNCHERS += $1 + + $1_RELAUNCHER_ARGUMENTS := + ifneq ($$($1_MAIN_CLASS), ) $1_JAVA_ARGS += -Xms8m $1_LAUNCHER_CLASS := -m $$($1_MAIN_MODULE)/$$($1_MAIN_CLASS) endif - ifeq ($$($1_EXPAND_CLASSPATH_WILDCARDS), true) - $1_CFLAGS += -DEXPAND_CLASSPATH_WILDCARDS + ifeq ($$($1_ENABLE_ARG_FILES), true) + $1_CFLAGS += -DDISABLE_ARGFILE=JNI_FALSE + else + $1_CFLAGS += -DDISABLE_ARGFILE=JNI_TRUE + # This must be the first argument given, if it should be present + $1_RELAUNCHER_ARGUMENTS += -DjavaLauncherArgFiles=false endif - ifeq ($$($1_ENABLE_ARG_FILES), true) - $1_CFLAGS += -DENABLE_ARG_FILES + ifeq ($$($1_EXPAND_CLASSPATH_WILDCARDS), true) + $1_CFLAGS += -DCLASSPATH_WILDCARDS=JNI_TRUE + else + $1_CFLAGS += -DCLASSPATH_WILDCARDS=JNI_FALSE + $1_RELAUNCHER_ARGUMENTS += -DjavaLauncherWildcards=false endif + $1_RELAUNCHER_ARGUMENTS += -DjavaLauncherProgname=$1 + ifeq ($(call isTargetOs, windows), true) ifeq ($$($1_WINDOWS_JAVAW), true) $1_CFLAGS += -DJAVAW @@ -94,9 +110,14 @@ define SetupBuildLauncherBody endif ifneq ($$($1_JAVA_ARGS), ) - $1_JAVA_ARGS_STR := '{ $$(strip $$(foreach a, \ - $$(addprefix -J, $$($1_JAVA_ARGS)) $$($1_LAUNCHER_CLASS), "$$a"$(COMMA) )) }' + $1_PREFIXED_JAVA_ARGS := $$(addprefix -J, $$($1_JAVA_ARGS)) \ + $$($1_LAUNCHER_CLASS) + $1_JAVA_ARGS_STR := '{ $$(strip $$(foreach a, $$($1_PREFIXED_JAVA_ARGS), \ + "$$a"$(COMMA) )) }' $1_CFLAGS += -DJAVA_ARGS=$$($1_JAVA_ARGS_STR) + # To preserve spaces, substitute them with a hopefully unique pattern + $1_RELAUNCHER_ARGUMENTS += \ + -DjavaLauncherArgs=$$(subst $$(SPACE),||||,$$($1_PREFIXED_JAVA_ARGS)) endif ifeq ($(call isTargetOs, macosx), true) @@ -172,6 +193,20 @@ define SetupBuildLauncherBody )) $1 += $$(BUILD_LAUNCHER_$1) + + $1_RELAUNCHER_ARGUMENTS_FILE := \ + $$(SUPPORT_OUTPUTDIR)/static-native/relaunchers/$$(MODULE)/$1-relauncher-arguments.txt + + $1_VARDEPS := $$($1_RELAUNCHER_ARGUMENTS) + $1_VARDEPS_FILE := $$(call DependOnVariable, $1_VARDEPS, \ + $$($1_RELAUNCHER_ARGUMENTS_FILE).vardeps) + + $$($1_RELAUNCHER_ARGUMENTS_FILE): + $$(call MakeDir, $$(@D)) + $$(ECHO) '$$($1_RELAUNCHER_ARGUMENTS)' > $$@ + + $1 += $$($1_RELAUNCHER_ARGUMENTS_FILE) + TARGETS += $$($1) $$(BUILD_LAUNCHER_$1): $$(BUILD_PLIST_$1) diff --git a/make/modules/java.base/Launcher.gmk b/make/modules/java.base/Launcher.gmk index c249656f188ca..3a3920acb1265 100644 --- a/make/modules/java.base/Launcher.gmk +++ b/make/modules/java.base/Launcher.gmk @@ -73,7 +73,7 @@ ifeq ($(call isTargetOs, linux), true) $(eval $(call SetupJdkExecutable, BUILD_JEXEC, \ NAME := jexec, \ - SRC := $(TOPDIR)/src/$(MODULE)/unix/native/launcher, \ + EXTRA_FILES := $(TOPDIR)/src/$(MODULE)/unix/native/launcher/jexec.c, \ OPTIMIZATION := LOW, \ EXTRA_HEADER_DIRS := libjli, \ CFLAGS_linux := -fPIC, \ diff --git a/src/java.base/share/native/launcher/defines.h b/src/java.base/share/native/launcher/defines.h deleted file mode 100644 index a07a1db17a6fc..0000000000000 --- a/src/java.base/share/native/launcher/defines.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2005, 2024, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -#ifndef _DEFINES_H -#define _DEFINES_H - -#include "java.h" - -#define STR_HELPER(x) #x -#define STR(x) STR_HELPER(x) - -/* - * This file contains commonly defined constants used only by main.c - * and should not be included by another file. - */ -#ifndef VERSION_STRING -/* make sure the compilation fails */ -#error "VERSION_STRING must be defined" -#endif - -/* Unused, but retained for JLI_Launch compatibility*/ -#define DOT_VERSION "0.0" - -#ifdef JAVA_ARGS -#ifdef PROGNAME -static const char* const_progname = PROGNAME; -#else -static char* const_progname = NULL; -#endif -static const char* const_jargs[] = JAVA_ARGS; -#else /* !JAVA_ARGS */ -static const char* const_progname = "java"; -static const char** const_jargs = NULL; -#endif /* JAVA_ARGS */ - -#ifdef LAUNCHER_NAME -static const char* const_launcher = LAUNCHER_NAME; -#else /* LAUNCHER_NAME */ -static char* const_launcher = NULL; -#endif /* LAUNCHER_NAME */ - -#ifdef EXPAND_CLASSPATH_WILDCARDS -static const jboolean const_cpwildcard = JNI_TRUE; -#else -static const jboolean const_cpwildcard = JNI_FALSE; -#endif /* EXPAND_CLASSPATH_WILDCARDS */ - -#ifdef ENABLE_ARG_FILES -static const jboolean const_disable_argfile = JNI_FALSE; -#else -static const jboolean const_disable_argfile = JNI_TRUE; -#endif -#endif /*_DEFINES_H */ diff --git a/src/java.base/share/native/launcher/main.c b/src/java.base/share/native/launcher/main.c index 4e42e03dfc7c6..13cd3eeda4bcf 100644 --- a/src/java.base/share/native/launcher/main.c +++ b/src/java.base/share/native/launcher/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1995, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,10 +30,56 @@ * tools. The rest of the files will be linked in. */ -#include "defines.h" +#include "java.h" #include "jli_util.h" #include "jni.h" +// Unused, but retained for JLI_Launch compatibility +#define DOT_VERSION "0.0" + +// This is reported when requesting a full version +static char* launcher = LAUNCHER_NAME; + +// This is used as the name of the executable in the help message +static char* progname = PROGNAME; + +#ifdef JAVA_ARGS +static const char* jargs[] = JAVA_ARGS; +#else +static const char** jargs = NULL; +#endif +static int jargc; + +static jboolean cpwildcard = CLASSPATH_WILDCARDS; +static jboolean disable_argfile = DISABLE_ARGFILE; + +#ifdef STATIC_BUILD +static void check_relauncher_argument(char* arg) { + if (strcmp(arg, "-J-DjavaLauncherWildcards=false") == 0) { + cpwildcard = JNI_FALSE; + } + if (strncmp(arg, "-J-DjavaLauncherProgname=", 26) == 0) { + progname = arg + 26; + } + if (strncmp(arg, "-J-DjavaLauncherArgs=", 21) == 0) { + char* java_args_ptr = arg + 21; + size_t java_args_len = strlen(arg) - 21; + + JLI_List java_args = JLI_List_new(java_args_len); + char* next_space; + while ((next_space = strchr(java_args_ptr, ' ')) != NULL) { + size_t next_arg_len = next_space - java_args_ptr; + JLI_List_addSubstring(java_args, java_args_ptr, next_arg_len); + java_args_ptr = next_space + 1; + } + JLI_List_add(java_args, java_args_ptr); + + jargc = (int) java_args->size; + jargs = (const char**) java_args->elements; + } +} +#endif + /* * Entry point. */ @@ -44,7 +90,7 @@ char **__initenv; int WINAPI WinMain(HINSTANCE inst, HINSTANCE previnst, LPSTR cmdline, int cmdshow) { - const jboolean const_javaw = JNI_TRUE; + const jboolean javaw = JNI_TRUE; __initenv = _environ; @@ -52,19 +98,25 @@ WinMain(HINSTANCE inst, HINSTANCE previnst, LPSTR cmdline, int cmdshow) JNIEXPORT int main(int argc, char **argv) { - const jboolean const_javaw = JNI_FALSE; + const jboolean javaw = JNI_FALSE; #endif /* JAVAW */ int margc; char** margv; - int jargc; - const char** jargv = const_jargs; - jargc = (sizeof(const_jargs) / sizeof(char *)) > 1 - ? sizeof(const_jargs) / sizeof(char *) + jargc = (sizeof(jargs) / sizeof(char *)) > 1 + ? sizeof(jargs) / sizeof(char *) : 0; // ignore the null terminator index - JLI_InitArgProcessing(jargc > 0, const_disable_argfile); +#ifdef STATIC_BUILD + // Relaunchers always give -J-DjavaLauncherArgFiles as the first argument, if present + // We must check disable_argfile before calling JLI_InitArgProcessing. + if (argc > 1 && strcmp(argv[1], "-J-DjavaLauncherArgFiles=false") == 0) { + disable_argfile = JNI_TRUE; + } +#endif + + JLI_InitArgProcessing(jargc > 0, disable_argfile); #ifdef _WIN32 { @@ -103,6 +155,9 @@ main(int argc, char **argv) StdArg *stdargs = JLI_GetStdArgs(); for (i = 0 ; i < margc ; i++) { margv[i] = stdargs[i].arg; +#ifdef STATIC_BUILD + check_relauncher_argument(margv[i]); +#endif } margv[i] = NULL; } @@ -127,6 +182,9 @@ main(int argc, char **argv) } // Iterate the rest of command line for (i = 1; i < argc; i++) { +#ifdef STATIC_BUILD + check_relauncher_argument(argv[i]); +#endif JLI_List argsInFile = JLI_PreprocessArg(argv[i], JNI_TRUE); if (NULL == argsInFile) { JLI_List_add(args, JLI_StringDup(argv[i])); @@ -148,12 +206,12 @@ main(int argc, char **argv) } #endif /* WIN32 */ return JLI_Launch(margc, margv, - jargc, jargv, + jargc, jargs, 0, NULL, VERSION_STRING, DOT_VERSION, - (const_progname != NULL) ? const_progname : *margv, - (const_launcher != NULL) ? const_launcher : *margv, + progname, + launcher, jargc > 0, - const_cpwildcard, const_javaw, 0); + cpwildcard, javaw, 0); } diff --git a/src/java.base/unix/native/launcher/relauncher.c b/src/java.base/unix/native/launcher/relauncher.c new file mode 100644 index 0000000000000..f95e3b38caa55 --- /dev/null +++ b/src/java.base/unix/native/launcher/relauncher.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include +#include + +#define JAVA_EXECUTABLE_NAME "java" + +#ifndef LAUNCHER_ARGS +#error LAUNCHER_ARGS must be defined +#endif + +static char *launcher_args[] = LAUNCHER_ARGS; + +int main(int argc, char *argv[]) { + //////////////////////////////////////////////////////////////////////////// + // Create a fully qualified path to the java executable in the same + // directory as this file resides in. + + char *our_full_path = realpath(argv[0], NULL); + if (our_full_path == NULL) { + perror("failed to get the full path of the executable"); + return 1; + } + + char *last_slash_pos = strrchr(our_full_path, '/'); + if (last_slash_pos == NULL) { + fprintf(stderr, "no '/' found in the full path of the executable\n"); + return 1; + } + + size_t base_length = last_slash_pos - our_full_path + 1; + size_t java_path_length = base_length + strlen(JAVA_EXECUTABLE_NAME) + 1; + + char *java_path = malloc(java_path_length); + if (java_path == NULL) { + perror("malloc failed"); + return 1; + } + + memcpy(java_path, our_full_path, base_length); + strcpy(java_path + base_length, JAVA_EXECUTABLE_NAME); + + //////////////////////////////////////////////////////////////////////////// + // Build the argument list: our executable name + launcher args + users args + + int launcher_argsc = sizeof(launcher_args) / sizeof(char *); + + char **java_args = malloc((launcher_argsc + argc + 1) * sizeof(char *)); + if (java_args == NULL) { + perror("malloc failed"); + return 1; + } + + // Our executable name + java_args[0] = argv[0]; + + // Launcher arguments + for (int i = 0; i < launcher_argsc; i++) { + java_args[i + 1] = launcher_args[i]; + } + + // User arguments + for (int i = 1; i < argc; i++) { + java_args[launcher_argsc + i] = argv[i]; + } + + java_args[launcher_argsc + argc] = NULL; + + //////////////////////////////////////////////////////////////////////////// + // Finally execute the real java process with the constructed arguments + + if (getenv("_JAVA_LAUNCHER_DEBUG")) { + char *program_name = basename(argv[0]); + + fprintf(stderr, "%s: executing: '%s'", program_name, java_path); + for (int i = 0; java_args[i] != NULL; i++) { + fprintf(stderr, " '%s' ", java_args[i]); + } + fprintf(stderr, "\n"); + } + + execv(java_path, java_args); + + // Should not reach here, unless something went wrong + perror("execv failed"); + return 1; +} diff --git a/src/java.base/windows/native/launcher/relauncher.c b/src/java.base/windows/native/launcher/relauncher.c new file mode 100644 index 0000000000000..74e9fbc64b088 --- /dev/null +++ b/src/java.base/windows/native/launcher/relauncher.c @@ -0,0 +1,242 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#include +#include +#include +#include +#include + +#define JAVA_EXECUTABLE_NAME "java.exe" + +#ifndef LAUNCHER_ARGS +#error LAUNCHER_ARGS must be defined +#endif + +static char* launcher_args[] = LAUNCHER_ARGS; + +char* quote_argument(char* arg) { + // See https://learn.microsoft.com/en-us/archive/blogs/twistylittlepassagesallalike/everyone-quotes-command-line-arguments-the-wrong-way + // for an explanation of how to properly quote command lines for CreateProcess + size_t arg_length = strlen(arg); + + if (strcspn(arg, " \t\n\v\"") == arg_length) { + // No quoting is needed + return arg; + } + + // Worst-case buffer size: all characters need a backslash, and starting + end quotes + size_t buffer_size = arg_length * 2 + 3; + char* buffer = malloc(buffer_size); + if (buffer == NULL) { + return NULL; + } + + int backslashes = 0; + char* write_pos = buffer; + char* read_pos = arg; + + // Start with a quote character + *write_pos++ = '"'; + + while (*read_pos) { + while (*read_pos == '\\') { + read_pos++; + backslashes++; + } + + if (*read_pos == '"') { + // Any potential backslashes before a quote needs to be doubled, + // and the quote needs to be escaped with an additional backslash + for (int i = 0; i < backslashes * 2 + 1; i++) { + *write_pos++ = '\\'; + } + *write_pos++ = *read_pos++; + backslashes = 0; + } else { + // Backslashes not preceeding a quote is copied without escaping + for (int i = 0; i < backslashes; i++) { + *write_pos++ = '\\'; + } + if (*read_pos) { + *write_pos++ = *read_pos++; + backslashes = 0; + } + } + } + + // If the string ended with backslashes, they need to be doubled before + // the final quote character + for (int i = 0; i < backslashes; i++) { + *write_pos++ = '\\'; + } + *write_pos++ = '"'; + *write_pos = '\0'; + + return buffer; +} + +int main(int argc, char* argv[]) { + //////////////////////////////////////////////////////////////////////////// + // Create a fully qualified path to the java executable in the same + // directory as this file resides in. + + // Calculate path length first + DWORD our_full_path_len = GetFullPathName(argv[0], 0, NULL, NULL); + if (our_full_path_len == 0) { + fprintf(stderr, "failed to get the full path of the executable: %lu\n", GetLastError()); + return 1; + } + + char* our_full_path = malloc(our_full_path_len + 1); + if (our_full_path == NULL) { + perror("malloc failed"); + return 1; + } + + if (GetFullPathName(argv[0], our_full_path_len + 1, our_full_path, NULL) == 0) { + fprintf(stderr, "failed to get the full path of the executable: %lu\n", GetLastError()); + return 1; + } + + char *last_slash_pos = strrchr(our_full_path, '\\'); + if (last_slash_pos == NULL) { + fprintf(stderr, "no '\\' found in the full path of the executable\n"); + return 1; + } + + size_t base_length = last_slash_pos - our_full_path + 1; + size_t java_path_length = base_length + strlen(JAVA_EXECUTABLE_NAME) + 1; + + char *java_path = malloc(java_path_length); + if (java_path == NULL) { + perror("malloc failed"); + return 1; + } + + memcpy(java_path, our_full_path, base_length); + strcpy(java_path + base_length, JAVA_EXECUTABLE_NAME); + + //////////////////////////////////////////////////////////////////////////// + // Build the argument list: our executable name + launcher args + users args + + int launcher_argsc = sizeof(launcher_args) / sizeof(char *); + + char **java_args = malloc((launcher_argsc + argc + 1) * sizeof(char *)); + if (java_args == NULL) { + perror("malloc failed"); + return 1; + } + + // Our executable name (should not be quoted) + java_args[0] = argv[0]; + + // Launcher arguments + for (int i = 0; i < launcher_argsc; i++) { + char* quoted = quote_argument(launcher_args[i]); + if (quoted == NULL) { + perror("malloc failed"); + return 1; + } + java_args[i + 1] = quoted; + } + + // User arguments + for (int i = 1; i < argc; i++) { + char* quoted = quote_argument(argv[i]); + if (quoted == NULL) { + perror("malloc failed"); + return 1; + } + java_args[launcher_argsc + i] = quoted; + } + + java_args[launcher_argsc + argc] = NULL; + + // Windows needs the command line as a single string, not as an array of char* + size_t total_length = 0; + for (int i = 0; java_args[i] != NULL; i++) { + char* arg = java_args[i]; + total_length += strlen(java_args[i]) + 1; + } + + char* command_line = malloc(total_length); + if (command_line == NULL) { + perror("malloc failed"); + return 1; + } + + // Concatenate the quoted arguments with a space between them + char* write_pos = command_line; + for (int i = 0; java_args[i] != NULL; i++) { + size_t arg_len = strlen(java_args[i]); + memcpy(write_pos, java_args[i], arg_len); + write_pos += arg_len; + + // Append a space + *write_pos++ = ' '; + } + + // Replace the last space with a null terminator + write_pos--; + *write_pos = '\0'; + + //////////////////////////////////////////////////////////////////////////// + // Finally execute the real java process with the constructed arguments + + if (GetEnvironmentVariable("_JAVA_LAUNCHER_DEBUG", NULL, 0)) { + char *program_name = PathFindFileName(argv[0]); + + fprintf(stderr, "%s: executing: '%s' '%s'\n", program_name, java_path, command_line); + } + + STARTUPINFO si; + PROCESS_INFORMATION pi; + + memset(&si, 0, sizeof(si)); + memset(&pi, 0, sizeof(pi)); + + // Windows has no equivalent of exec, so start the process and wait for it + // to finish, to be able to return the same exit code + if (!CreateProcess(java_path, command_line, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) { + fprintf(stderr, "CreateProcess failed: %lu\n", GetLastError()); + return 1; + } + + if (WaitForSingleObject(pi.hProcess, INFINITE) == WAIT_FAILED) { + fprintf(stderr, "WaitForSingleObject failed: %lu\n", GetLastError()); + return 1; + } + + DWORD exit_code; + if (!GetExitCodeProcess(pi.hProcess, &exit_code)) { + fprintf(stderr, "GetExitCodeProcess failed: %lu\n", GetLastError()); + return 1; + } + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return exit_code; +} diff --git a/test/hotspot/jtreg/ProblemList-StaticJdk.txt b/test/hotspot/jtreg/ProblemList-StaticJdk.txt index cb727e470f324..d1d7739e34b39 100644 --- a/test/hotspot/jtreg/ProblemList-StaticJdk.txt +++ b/test/hotspot/jtreg/ProblemList-StaticJdk.txt @@ -1,38 +1,3 @@ -# Require javac -runtime/HiddenClasses/DefineHiddenClass.java 8346719 generic-all - -# Require jstack -runtime/Thread/TestThreadDumpClassInitMonitor.java 8346719 generic-all -runtime/Thread/TestThreadDumpSMRInfo.java 8346719 generic-all -serviceability/tmtools/jstack/DaemonThreadTest.java 8346719 generic-all -serviceability/tmtools/jstack/JstackThreadTest.java 8346719 generic-all -serviceability/tmtools/jstack/SpreadLockTest.java 8346719 generic-all -serviceability/tmtools/jstack/ThreadNamesTest.java 8346719 generic-all -serviceability/tmtools/jstack/TraveledLockTest.java 8346719 generic-all -serviceability/tmtools/jstack/WaitNotifyThreadTest.java 8346719 generic-all -serviceability/tmtools/jstat/GcCapacityTest.java 8346719 generic-all -serviceability/tmtools/jstat/GcCauseTest01.java 8346719 generic-all -serviceability/tmtools/jstat/GcCauseTest02.java 8346719 generic-all -serviceability/tmtools/jstat/GcCauseTest03.java 8346719 generic-all -serviceability/tmtools/jstat/GcNewTest.java 8346719 generic-all -serviceability/tmtools/jstat/GcTest01.java 8346719 generic-all -serviceability/tmtools/jstat/GcTest02.java 8346719 generic-all - -# Require jcmd -serviceability/HeapDump/DuplicateArrayClassesTest.java 8346719 generic-all -serviceability/HeapDump/FieldsInInstanceTest.java 8346719 generic-all -serviceability/attach/ConcAttachTest.java 8346719 generic-all -serviceability/attach/RemovingUnixDomainSocketTest.java 8346719 generic-all -serviceability/jvmti/vthread/HeapDump/VThreadInHeapDump.java#default 8346719 generic-all -serviceability/jvmti/vthread/HeapDump/VThreadInHeapDump.java#no-vmcontinuations 8346719 generic-all - -# Require jhsdb -serviceability/sa/ClhsdbCDSCore.java 8346719 generic-all -serviceability/sa/ClhsdbFindPC.java#no-xcomp-core 8346719 generic-all -serviceability/sa/ClhsdbFindPC.java#xcomp-core 8346719 generic-all -serviceability/sa/ClhsdbPmap.java#core 8346719 generic-all -serviceability/sa/ClhsdbPstack.java#core 8346719 generic-all - # Dynamically link with JDK/VM native libraries gtest/GTestWrapper.java 8356201 generic-all gtest/LargePageGtests.java#use-large-pages 8356201 generic-all