#895: Rewrote jfrconv to make it a statically linked executable
This commit is contained in:
@@ -44,6 +44,7 @@ ifeq ($(OS),Darwin)
|
||||
CXXFLAGS += $(FAT_BINARY_FLAGS)
|
||||
PACKAGE_NAME=async-profiler-$(PROFILER_VERSION)-$(OS_TAG)
|
||||
MERGE=false
|
||||
SKIP_IN_RELEASE=true
|
||||
endif
|
||||
else
|
||||
CXXFLAGS += -Wl,-z,defs
|
||||
@@ -102,7 +103,8 @@ $(PACKAGE_NAME).tar.gz: $(PACKAGE_DIR)
|
||||
rm -r $(PACKAGE_DIR)
|
||||
|
||||
$(PACKAGE_NAME).zip: $(PACKAGE_DIR)
|
||||
codesign -s "Developer ID" -o runtime --timestamp -v $(PACKAGE_DIR)/$(ASPROF) $(PACKAGE_DIR)/$(LIB_PROFILER)
|
||||
codesign -s "Developer ID" -o runtime --timestamp -v $(PACKAGE_DIR)/$(ASPROF) $(PACKAGE_DIR)/$(JFRCONV) $(PACKAGE_DIR)/$(LIB_PROFILER)
|
||||
cat build/$(CONVERTER_JAR) >> $(PACKAGE_DIR)/$(JFRCONV)
|
||||
ditto -c -k --keepParent $(PACKAGE_DIR) $@
|
||||
rm -r $(PACKAGE_DIR)
|
||||
|
||||
@@ -119,14 +121,10 @@ build/$(ASPROF): src/main/* src/jattach/* src/fdtransfer.h
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DPROFILER_VERSION=\"$(PROFILER_VERSION)\" -o $@ src/main/*.cpp src/jattach/*.c
|
||||
strip $@
|
||||
|
||||
build/$(JFRCONV): src/launcher/* src/incbin.h $(JAVA_HELPER_CLASSES) build/$(CONVERTER_JAR)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DPROFILER_VERSION=\"$(PROFILER_VERSION)\" $(INCLUDES) -o $@ src/launcher/*.cpp -ldl
|
||||
|
||||
build/$(JFRCONV).exe: src/launcher/* src/incbin.h $(JAVA_HELPER_CLASSES) build/$(CONVERTER_JAR)
|
||||
mkdir -p build/bin build/gensrc
|
||||
(echo -n "const unsigned char CLASS_BYTES[] = {" && hexdump -v -e '1/1 "%u,"' src/helper/one/profiler/EmbeddedClassLoader.class && echo "}; const unsigned char CLASS_BYTES_END = {0};") > build/gensrc/CLASS_BYTES.c
|
||||
(echo -n "const unsigned char CONVERTER_JAR[] = {" && hexdump -v -e '1/1 "%u,"' build/$(CONVERTER_JAR) && echo "}; const unsigned char CONVERTER_JAR_END = {0};") > build/gensrc/CONVERTER_JAR.c
|
||||
cmd.exe /C cl /O2 /DPROFILER_VERSION=\"$(PROFILER_VERSION)\" src/launcher/*.cpp build/gensrc/*.c /Fo:build/gensrc/ /Fe:$@
|
||||
build/$(JFRCONV): src/launcher/* build/$(CONVERTER_JAR)
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -DPROFILER_VERSION=\"$(PROFILER_VERSION)\" -o $@ src/launcher/*.cpp
|
||||
strip $@
|
||||
$(SKIP_IN_RELEASE) cat build/$(CONVERTER_JAR) >> $@
|
||||
|
||||
build/$(LIB_PROFILER): $(SOURCES) $(HEADERS) $(RESOURCES) $(JAVA_HELPER_CLASSES)
|
||||
ifeq ($(MERGE),true)
|
||||
|
||||
Binary file not shown.
@@ -1,143 +0,0 @@
|
||||
/*
|
||||
* Copyright The async-profiler authors
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package one.profiler;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.Buffer;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.zip.DataFormatException;
|
||||
import java.util.zip.Inflater;
|
||||
|
||||
/**
|
||||
* Loads classes from a JAR file embedded in a shared library.
|
||||
*/
|
||||
public class EmbeddedClassLoader extends ClassLoader {
|
||||
private final ByteBuffer jar;
|
||||
private final Map<String, int[]> directory;
|
||||
|
||||
public EmbeddedClassLoader(ClassLoader parent, ByteBuffer jar) {
|
||||
super(parent);
|
||||
this.jar = jar;
|
||||
this.directory = new HashMap<>();
|
||||
|
||||
String specVersion = System.getProperty("java.specification.version");
|
||||
int currentVersion = specVersion == null || specVersion.startsWith("1.") ? 8 : Integer.parseInt(specVersion);
|
||||
|
||||
jar.order(ByteOrder.LITTLE_ENDIAN);
|
||||
int eocd = jar.limit() - 22;
|
||||
if (jar.getInt(eocd) != 0x06054b50) {
|
||||
throw new IllegalStateException("EOCD signature not found");
|
||||
}
|
||||
|
||||
int chdr = jar.getInt(eocd + 16);
|
||||
while (jar.getInt(chdr) == 0x02014b50) {
|
||||
int compressedSize = jar.getInt(chdr + 20);
|
||||
int uncompressedSize = jar.getInt(chdr + 24);
|
||||
int fileNameLength = jar.getShort(chdr + 28) & 0xffff;
|
||||
int extraLength = jar.getInt(chdr + 30);
|
||||
int fileHeaderStart = jar.getInt(chdr + 42);
|
||||
|
||||
byte[] fileNameBytes = new byte[fileNameLength];
|
||||
((Buffer) jar).position(chdr + 46);
|
||||
jar.get(fileNameBytes);
|
||||
|
||||
String fileName = new String(fileNameBytes, StandardCharsets.UTF_8);
|
||||
if (!fileName.endsWith("/")) {
|
||||
int[] entry = {fileHeaderStart, compressedSize, uncompressedSize};
|
||||
if (fileName.startsWith("META-INF/versions/")) {
|
||||
int p = fileName.indexOf('/', 18);
|
||||
if (p > 0 && Integer.parseInt(fileName.substring(18, p)) <= currentVersion) {
|
||||
directory.put(fileName.substring(p + 1), entry);
|
||||
}
|
||||
} else {
|
||||
directory.putIfAbsent(fileName, entry);
|
||||
}
|
||||
}
|
||||
|
||||
chdr += 46 + fileNameLength + (extraLength & 0xffff) + (extraLength >>> 16);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
||||
byte[] data = unzip(name.replace('.', '/').concat(".class"));
|
||||
if (data == null) {
|
||||
throw new ClassNotFoundException(name);
|
||||
}
|
||||
return defineClass(name, data, 0, data.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getResourceAsStream(String name) {
|
||||
byte[] data = unzip(name.startsWith("/") ? name.substring(1) : name);
|
||||
if (data == null) {
|
||||
return super.getResourceAsStream(name);
|
||||
}
|
||||
return new ByteArrayInputStream(data);
|
||||
}
|
||||
|
||||
private byte[] unzip(String name) {
|
||||
int[] entry = directory.get(name);
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int loc = entry[0];
|
||||
if (jar.getInt(loc) != 0x04034b50) {
|
||||
throw new IllegalStateException("LOC signature not found");
|
||||
}
|
||||
|
||||
byte[] compressed = new byte[entry[1]];
|
||||
int extraLength = jar.getInt(loc + 26);
|
||||
ByteBuffer jarCopy = jar.duplicate();
|
||||
((Buffer) jarCopy).position(loc + 30 + (extraLength & 0xffff) + (extraLength >>> 16));
|
||||
jarCopy.get(compressed);
|
||||
|
||||
short method = jar.getShort(loc + 8);
|
||||
if (method == 0) {
|
||||
return compressed;
|
||||
} else if (method != 8) {
|
||||
throw new IllegalStateException("Unsupported compression algorithm");
|
||||
}
|
||||
|
||||
byte[] uncompressed = new byte[entry[2]];
|
||||
Inflater inf = new Inflater(true);
|
||||
try {
|
||||
inf.setInput(compressed);
|
||||
if (inf.inflate(uncompressed) != uncompressed.length) {
|
||||
throw new IllegalStateException("Uncompressed size mismatch");
|
||||
}
|
||||
return uncompressed;
|
||||
} catch (DataFormatException e) {
|
||||
throw new IllegalStateException("Invalid compressed data");
|
||||
} finally {
|
||||
inf.end();
|
||||
}
|
||||
}
|
||||
|
||||
public static Class<?> loadMainClass(ByteBuffer jar) throws ClassNotFoundException {
|
||||
EmbeddedClassLoader loader = new EmbeddedClassLoader(EmbeddedClassLoader.class.getClassLoader(), jar);
|
||||
byte[] manifest = loader.unzip("META-INF/MANIFEST.MF");
|
||||
if (manifest == null) {
|
||||
throw new IllegalStateException("MANIFEST.MF not found");
|
||||
}
|
||||
|
||||
String s = new String(manifest, StandardCharsets.UTF_8);
|
||||
int p = s.indexOf("Main-Class:");
|
||||
if (p < 0) {
|
||||
throw new IllegalStateException("Main-Class attribute not found");
|
||||
}
|
||||
|
||||
int q = s.indexOf('\n', p += 11);
|
||||
String mainClass = (q >= 0 ? s.substring(p, q) : s.substring(p)).trim();
|
||||
return loader.findClass(mainClass);
|
||||
}
|
||||
}
|
||||
+76
-227
@@ -3,273 +3,118 @@
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <jni.h>
|
||||
#include <dirent.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include "../incbin.h"
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
# include <mach-o/dyld.h>
|
||||
# define COMMON_JVM_DIR "/Library/Java/JavaVirtualMachines/"
|
||||
# define CONTENTS_HOME "/Contents/Home"
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#include <dlfcn.h>
|
||||
#include <limits.h>
|
||||
# define COMMON_JVM_DIR "/usr/lib/jvm/"
|
||||
# define CONTENTS_HOME ""
|
||||
#endif
|
||||
|
||||
#define JAVA_EXE "java"
|
||||
|
||||
#define APP_BINARY "jfrconv"
|
||||
|
||||
static const char VERSION_STRING[] =
|
||||
"JFR converter " PROFILER_VERSION " built on " __DATE__ "\n";
|
||||
|
||||
INCLUDE_HELPER_CLASS(EMBEDDED_CLASS_LOADER, CLASS_BYTES, "one/profiler/EmbeddedClassLoader")
|
||||
INCBIN(CONVERTER_JAR, "build/jar/jfr-converter.jar")
|
||||
|
||||
static char exe_path[PATH_MAX];
|
||||
static char java_path[PATH_MAX];
|
||||
|
||||
#if defined(__APPLE__) || defined(_WIN32)
|
||||
// There is no arch subdirectory in macOS JDK bundles
|
||||
# define ARCH ""
|
||||
#elif defined(__x86_64__)
|
||||
# define ARCH "amd64"
|
||||
#elif defined(__i386__)
|
||||
# define ARCH "i386"
|
||||
#elif defined(__arm__) || defined(__thumb__)
|
||||
# define ARCH "arm"
|
||||
#elif defined(__aarch64__)
|
||||
# define ARCH "aarch64"
|
||||
#elif defined(__PPC64__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
|
||||
# define ARCH "ppc64le"
|
||||
#elif defined(__riscv) && (__riscv_xlen == 64)
|
||||
# define ARCH "riscv64"
|
||||
#elif defined(__loongarch_lp64)
|
||||
# define ARCH "loongarch64"
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define COMMON_JVM_DIR "/Library/Java/JavaVirtualMachines/"
|
||||
# define CONTENTS_HOME "/Contents/Home"
|
||||
# define LIB_DIR "lib"
|
||||
# define LIBJVM "libjvm.dylib"
|
||||
# define PATH_SEP ":"
|
||||
# define SLASH '/'
|
||||
#elif defined(_WIN32)
|
||||
# define COMMON_JVM_DIR "C:\\Program Files\\Java\\"
|
||||
# define CONTENTS_HOME ""
|
||||
# define LIB_DIR "bin"
|
||||
# define LIBJVM "jvm.dll"
|
||||
# define PATH_SEP ";"
|
||||
# define SLASH '\\'
|
||||
#else
|
||||
# define COMMON_JVM_DIR "/usr/lib/jvm/"
|
||||
# define CONTENTS_HOME ""
|
||||
# define LIB_DIR "lib"
|
||||
# define LIBJVM "libjvm.so"
|
||||
# define PATH_SEP ":"
|
||||
# define SLASH '/'
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
# define PATH_MAX MAX_PATH
|
||||
# define dlopen(name, _) LoadLibrary(name)
|
||||
# define dlsym(lib, name) GetProcAddress((HMODULE)lib, name)
|
||||
# define realpath(path, buf) _fullpath(buf, path, MAX_PATH)
|
||||
#endif
|
||||
|
||||
|
||||
typedef jint (*JNI_CreateJavaVM_t)(JavaVM **p_vm, void **p_env, void *vm_args);
|
||||
|
||||
static void* load_libjvm(const char* java_home, const char* subdir) {
|
||||
static bool get_exe_path() {
|
||||
#ifdef __APPLE__
|
||||
char buf[PATH_MAX];
|
||||
if (snprintf(buf, sizeof(buf), "%s/%s/" LIBJVM, java_home, subdir) >= sizeof(buf)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct stat statbuf;
|
||||
if (stat(buf, &statbuf) != 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return dlopen(buf, RTLD_NOW);
|
||||
}
|
||||
|
||||
static void* find_libjvm_at(const char* path, const char* path1 = "", const char* path2 = "") {
|
||||
char buf[PATH_MAX];
|
||||
if (snprintf(buf, sizeof(buf), "%s%s%s", path, path1, path2) >= sizeof(buf)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* java_home = realpath(buf, NULL);
|
||||
if (java_home == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* p = strrchr(java_home, SLASH);
|
||||
if (p != NULL) {
|
||||
*p = 0; // strip /java
|
||||
}
|
||||
if ((p = strrchr(java_home, SLASH)) != NULL) {
|
||||
*p = 0; // strip /bin
|
||||
}
|
||||
|
||||
void* libjvm;
|
||||
if ((libjvm = load_libjvm(java_home, LIB_DIR "/server")) == NULL &&
|
||||
(libjvm = load_libjvm(java_home, LIB_DIR "/client")) == NULL &&
|
||||
(libjvm = load_libjvm(java_home, LIB_DIR "/" ARCH "/server")) == NULL &&
|
||||
(libjvm = load_libjvm(java_home, LIB_DIR "/" ARCH "/client")) == NULL &&
|
||||
(libjvm = load_libjvm(java_home, "jre/" LIB_DIR "/" ARCH "/server")) == NULL &&
|
||||
(libjvm = load_libjvm(java_home, "jre/" LIB_DIR "/" ARCH "/client")) == NULL) {
|
||||
// No libjvm.so found at this path
|
||||
}
|
||||
|
||||
free(java_home);
|
||||
return libjvm;
|
||||
}
|
||||
|
||||
static void* find_libjvm() {
|
||||
void* libjvm;
|
||||
char* java_home = getenv("JAVA_HOME");
|
||||
if (java_home != NULL && (libjvm = find_libjvm_at(java_home, "/bin/java")) != NULL) {
|
||||
return libjvm;
|
||||
}
|
||||
|
||||
char* path = getenv("PATH");
|
||||
char* path_copy;
|
||||
if (path != NULL && (path_copy = strdup(path)) != NULL) {
|
||||
for (char* java_bin = strtok(path_copy, PATH_SEP); java_bin != NULL; java_bin = strtok(NULL, PATH_SEP)) {
|
||||
if ((libjvm = find_libjvm_at(java_bin, "/java")) != NULL) {
|
||||
free(path_copy);
|
||||
return libjvm;
|
||||
}
|
||||
}
|
||||
free(path_copy);
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
if ((libjvm = find_libjvm_at("/etc/alternatives/java")) != NULL) {
|
||||
return libjvm;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
WIN32_FIND_DATA entry;
|
||||
HANDLE dir = FindFirstFile(COMMON_JVM_DIR, &entry);
|
||||
if (dir != INVALID_HANDLE_VALUE) {
|
||||
do {
|
||||
if (entry.cFileName[0] != '.' && (entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
if ((libjvm = find_libjvm_at(COMMON_JVM_DIR, entry.cFileName, CONTENTS_HOME "/bin/java")) != NULL) {
|
||||
CloseHandle(dir);
|
||||
return libjvm;
|
||||
}
|
||||
}
|
||||
} while (FindNextFile(dir, &entry));
|
||||
CloseHandle(dir);
|
||||
}
|
||||
uint32_t size = sizeof(buf);
|
||||
return _NSGetExecutablePath(buf, &size) == 0 && realpath(buf, exe_path) != NULL;
|
||||
#else
|
||||
DIR* dir = opendir(COMMON_JVM_DIR);
|
||||
if (dir != NULL) {
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_name[0] != '.' && entry->d_type == DT_DIR) {
|
||||
if ((libjvm = find_libjvm_at(COMMON_JVM_DIR, entry->d_name, CONTENTS_HOME "/bin/java")) != NULL) {
|
||||
closedir(dir);
|
||||
return libjvm;
|
||||
}
|
||||
}
|
||||
if (realpath("/proc/self/exe", exe_path) == NULL) {
|
||||
// realpath() may fail for a path like /proc/[pid]/root/bin/exe
|
||||
ssize_t size = readlink("/proc/self/exe", exe_path, sizeof(exe_path) - 1);
|
||||
if (size < 0) {
|
||||
return false;
|
||||
}
|
||||
closedir(dir);
|
||||
exe_path[size] = 0;
|
||||
}
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int print_exception(JavaVM* vm, JNIEnv* env) {
|
||||
env->ExceptionDescribe();
|
||||
vm->DestroyJavaVM();
|
||||
return 1;
|
||||
}
|
||||
static const char* const* build_cmdline(int argc, char** argv) {
|
||||
const char** cmd = (const char**)malloc((argc + 5) * sizeof(char*));
|
||||
int count = 0;
|
||||
|
||||
static int run_jvm(void* libjvm, int argc, char** argv) {
|
||||
JNI_CreateJavaVM_t JNI_CreateJavaVM = (JNI_CreateJavaVM_t)dlsym(libjvm, "JNI_CreateJavaVM");
|
||||
if (JNI_CreateJavaVM == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
JavaVMOption* options = (JavaVMOption*)alloca((argc + 2) * sizeof(JavaVMOption));
|
||||
int o_count = 0;
|
||||
options[o_count++].optionString = (char*)"-Dsun.java.command=" APP_BINARY;
|
||||
options[o_count++].optionString = (char*)"-Xss2M";
|
||||
cmd[count++] = JAVA_EXE;
|
||||
cmd[count++] = "-Xss2M";
|
||||
|
||||
for (; argc > 0; argc--, argv++) {
|
||||
if ((strncmp(*argv, "-D", 2) == 0 || strncmp(*argv, "-X", 2) == 0) && (*argv)[2] ||
|
||||
strncmp(*argv, "-agent", 6) == 0) {
|
||||
options[o_count++].optionString = *argv;
|
||||
cmd[count++] = *argv;
|
||||
} else if (strncmp(*argv, "-J", 2) == 0) {
|
||||
options[o_count++].optionString = *argv + 2;
|
||||
cmd[count++] = *argv + 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
JavaVM* vm;
|
||||
JNIEnv* env;
|
||||
JavaVMInitArgs args;
|
||||
args.version = JNI_VERSION_1_6;
|
||||
args.nOptions = o_count;
|
||||
args.options = options;
|
||||
args.ignoreUnrecognized = JNI_TRUE;
|
||||
cmd[count++] = "-jar";
|
||||
cmd[count++] = exe_path;
|
||||
|
||||
int res = JNI_CreateJavaVM(&vm, (void**)&env, &args);
|
||||
if (res != 0) {
|
||||
return res;
|
||||
for (; argc > 0; argc--, argv++) {
|
||||
cmd[count++] = *argv;
|
||||
}
|
||||
|
||||
jclass loader = env->DefineClass(EMBEDDED_CLASS_LOADER, NULL, (const jbyte*)CLASS_BYTES, INCBIN_SIZEOF(CLASS_BYTES));
|
||||
if (loader == NULL) {
|
||||
return print_exception(vm, env);
|
||||
cmd[count] = NULL;
|
||||
return cmd;
|
||||
}
|
||||
|
||||
static bool find_java_at(const char* path, const char* path1 = "", const char* path2 = "") {
|
||||
if (snprintf(java_path, sizeof(java_path), "%s%s%s/" JAVA_EXE, path, path1, path2) >= sizeof(java_path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
jmethodID load_main = env->GetStaticMethodID(loader, "loadMainClass", "(Ljava/nio/ByteBuffer;)Ljava/lang/Class;");
|
||||
if (load_main == NULL) {
|
||||
return print_exception(vm, env);
|
||||
struct stat st;
|
||||
return stat(java_path, &st) == 0 && S_ISREG(st.st_mode) && (st.st_mode & S_IXUSR) != 0;
|
||||
}
|
||||
|
||||
static void run_java(char* const* cmd) {
|
||||
// 1. Get java executable from JAVA_HOME
|
||||
char* java_home = getenv("JAVA_HOME");
|
||||
if (java_home != NULL && find_java_at(java_home, "/bin")) {
|
||||
execv(java_path, cmd);
|
||||
}
|
||||
|
||||
jobject jar = env->NewDirectByteBuffer((void*)CONVERTER_JAR, INCBIN_SIZEOF(CONVERTER_JAR));
|
||||
if (jar == NULL) {
|
||||
return print_exception(vm, env);
|
||||
// 2. Try to find java in PATH
|
||||
execvp(JAVA_EXE, cmd);
|
||||
|
||||
// 3. Try /etc/alternatives/java
|
||||
if (find_java_at("/etc/alternatives")) {
|
||||
execv(java_path, cmd);
|
||||
}
|
||||
|
||||
jclass main_class = (jclass)env->CallStaticObjectMethod(loader, load_main, jar);
|
||||
if (main_class == NULL) {
|
||||
return print_exception(vm, env);
|
||||
}
|
||||
|
||||
jmethodID main_method = env->GetStaticMethodID(main_class, "main", "([Ljava/lang/String;)V");
|
||||
if (main_method == NULL) {
|
||||
return print_exception(vm, env);
|
||||
}
|
||||
|
||||
jobjectArray main_args = env->NewObjectArray(argc, env->FindClass("java/lang/String"), NULL);
|
||||
if (main_args != NULL) {
|
||||
for (int i = 0; i < argc; i++) {
|
||||
env->SetObjectArrayElement(main_args, i, env->NewStringUTF(argv[i]));
|
||||
// 4. Look for java in the common directory
|
||||
DIR* dir = opendir(COMMON_JVM_DIR);
|
||||
if (dir != NULL) {
|
||||
struct dirent* entry;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
if (entry->d_name[0] != '.' && entry->d_type == DT_DIR) {
|
||||
if (find_java_at(COMMON_JVM_DIR, entry->d_name, CONTENTS_HOME "/bin")) {
|
||||
execv(java_path, cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
if (env->ExceptionCheck()) {
|
||||
return print_exception(vm, env);
|
||||
}
|
||||
|
||||
env->CallStaticVoidMethod(main_class, main_method, main_args);
|
||||
if (env->ExceptionCheck()) {
|
||||
return print_exception(vm, env);
|
||||
}
|
||||
|
||||
vm->DestroyJavaVM();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@@ -278,11 +123,15 @@ int main(int argc, char** argv) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* libjvm = find_libjvm();
|
||||
if (libjvm == NULL) {
|
||||
fprintf(stderr, "No JDK found. Set JAVA_HOME or ensure java executable is on the PATH.\n");
|
||||
if (!get_exe_path()) {
|
||||
fprintf(stderr, "Failed to get executable path\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return run_jvm(libjvm, argc - 1, argv + 1);
|
||||
const char* const* cmd = build_cmdline(argc - 1, argv + 1);
|
||||
run_java((char* const*)cmd);
|
||||
|
||||
// May reach here only if run_java() fails
|
||||
fprintf(stderr, "No JDK found. Set JAVA_HOME or ensure java executable is on the PATH\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user