Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified FCL/src/main/assets/app_runtime/lwjgl-boat/lwjgl.jar
Binary file not shown.
2 changes: 1 addition & 1 deletion FCL/src/main/assets/app_runtime/lwjgl-boat/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1736478001562
1738372810317
11 changes: 9 additions & 2 deletions FCLauncher/src/main/java/com/tungsten/fclauncher/FCLauncher.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import android.content.Context;
import android.os.Build;
import android.system.ErrnoException;
import android.system.Os;
import android.util.ArrayMap;

Expand Down Expand Up @@ -163,7 +164,7 @@ private static String[] rebaseArgs(FCLConfig config) throws IOException {
String[] args = new String[argList.size()];
for (int i = 0; i < argList.size(); i++) {
String a = argList.get(i).replace("${natives_directory}", getLibraryPath(config.getContext(), config.getJavaPath(), config.getRenderer() == FCLConfig.Renderer.RENDERER_CUSTOM ? RendererPlugin.getSelected().getPath() : null));
args[i] = config.getRenderer() == null ? a : a.replace("${gl_lib_name}", config.getRenderer() == FCLConfig.Renderer.RENDERER_CUSTOM ? RendererPlugin.getSelected().getGlName() : config.getRenderer().getGlLibName());
args[i] = config.getRenderer() == null ? a : a.replace("${gl_lib_name}", config.getRenderer() == FCLConfig.Renderer.RENDERER_CUSTOM ? RendererPlugin.getSelected().getPath() + "/" +RendererPlugin.getSelected().getGlName() : config.getRenderer().getGlLibName());
}
return args;
}
Expand Down Expand Up @@ -362,7 +363,13 @@ private static void setupGraphicAndSoundEngine(FCLConfig config, FCLBridge bridg
}
}
});
bridge.dlopen(RendererPlugin.getSelected().getPath() + "/" + RendererPlugin.getSelected().getGlName());
long handle = bridge.dlopen(RendererPlugin.getSelected().getPath() + "/" + RendererPlugin.getSelected().getGlName());
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
try {
Os.setenv("RENDERER_HANDLE",handle+"",true);
} catch (ErrnoException ignore) {
}
}
// bridge.dlopen(RendererPlugin.getSelected().getPath() + "/" + RendererPlugin.getSelected().getEglName());
} else {
bridge.dlopen(nativeDir + "/" + config.getRenderer().getGlLibName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public FCLBridge() {

public native void setenv(String key, String value);

public native int dlopen(String path);
public native long dlopen(String path);

public native void setLdLibraryPath(String path);

Expand Down
8 changes: 5 additions & 3 deletions FCLauncher/src/main/jni/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ LOCAL_SHARED_LIBRARIES := bytehook
LOCAL_SRC_FILES := fcl/fcl_bridge.c \
fcl/fcl_event.c \
fcl/fcl_loader.c \
fcl/jre_launcher.c \
fcl/utils.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/fcl/include
LOCAL_LDLIBS := -llog -ldl -landroid
Expand All @@ -28,7 +27,8 @@ LOCAL_SRC_FILES := glfw/context.c \
glfw/osmesa_context.c \
glfw/platform.c \
glfw/posix_thread.c \
glfw/posix_time.c
glfw/posix_time.c \
glfw/lwjgl_dlopen_hook.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/fcl/include \
$(LOCAL_PATH)/glfw/include
LOCAL_CFLAGS := -Wall
Expand Down Expand Up @@ -96,7 +96,9 @@ LOCAL_SRC_FILES := \
pojav/ctxbridges/swap_interval_no_egl.c \
pojav/environ/environ.c \
pojav/input_bridge_v3.c \
pojav/virgl/virgl.c
pojav/virgl/virgl.c \
pojav/jre_launcher.c \
pojav/lwjgl_dlopen_hook.c
LOCAL_C_INCLUDES := $(LOCAL_PATH)/pojav
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
LOCAL_CFLAGS += -DADRENO_POSSIBLE
Expand Down
9 changes: 2 additions & 7 deletions FCLauncher/src/main/jni/fcl/fcl_loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,9 @@ JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setenv(JNIE
(*env)->ReleaseStringUTFChars(env, str2, value);
}

JNIEXPORT jint JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_dlopen(JNIEnv* env, jobject jobject, jstring str) {
JNIEXPORT jlong JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_dlopen(JNIEnv* env, jobject jobject, jstring str) {
dlerror();

int ret = 0;
char const* lib_name = (*env)->GetStringUTFChars(env, str, 0);

void* handle;
Expand All @@ -169,12 +168,8 @@ JNIEXPORT jint JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_dlopen(JNIE
char * error = dlerror();
FCL_LOG("DLOPEN: loading %s (error = %s)", lib_name, error);

if (handle == NULL) {
ret = -1;
}

(*env)->ReleaseStringUTFChars(env, str, lib_name);
return ret;
return (jlong) handle;
}

JNIEXPORT void JNICALL Java_com_tungsten_fclauncher_bridge_FCLBridge_setLdLibraryPath(JNIEnv *env, jobject jobject, jstring ldLibraryPath) {
Expand Down
67 changes: 67 additions & 0 deletions FCLauncher/src/main/jni/glfw/lwjgl_dlopen_hook.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
//
// Created by maks on 06.01.2025.
//

#include <android/api-level.h>
#include <android/log.h>
#include <jni.h>

#include <dlfcn.h>
#include <string.h>
#include <stdlib.h>
#include "fcl/include/fcl_internal.h"

extern void* maybe_load_vulkan();

/**
* Basically a verbatim implementation of ndlopen(), found at
* https://github.com/PojavLauncherTeam/lwjgl3/blob/3.3.1/modules/lwjgl/core/src/generated/c/linux/org_lwjgl_system_linux_DynamicLinkLoader.c#L11
* but with our own additions for stuff like vulkanmod.
*/
static jlong ndlopen_bugfix(__attribute__((unused)) JNIEnv *env,
__attribute__((unused)) jclass class,
jlong filename_ptr,
jint jmode) {
const char* filename = (const char*) filename_ptr;
// Oveeride vulkan loading to let us load vulkan ourselves
if(strstr(filename, "libvulkan.so") == filename) {
FCL_LOG("LWJGL linkerhook: replacing load for libvulkan.so with custom driver");
return (jlong) maybe_load_vulkan();
}
if (getenv("RENDERER_HANDLE") != NULL && strstr(filename,"plugin")) {
return (jlong) strtol(getenv("RENDERER_HANDLE"), NULL, 10);
}

// This hook also serves the task of mitigating a bug: the idea is that since, on Android 10 and
// earlier, the linker doesn't really do namespace nesting.
// It is not a problem as most of the libraries are in the launcher path, but when you try to run
// VulkanMod which loads shaderc outside of the default jni libs directory through this method,
// it can't load it because the path is not in the allowed paths for the anonymous namesapce.
// This method fixes the issue by being in libpojavexec, and thus being in the classloader namespace

int mode = (int)jmode;
jlong handle = (jlong) dlopen(filename, mode);
return handle;
}

/**
* Install the LWJGL dlopen hook. This allows us to mitigate linker bugs and add custom library overrides.
*/
void installLwjglDlopenHook(JavaVM* vm) {
FCL_LOG("LwjglLinkerHook:%s", "Installing LWJGL dlopen() hook");
JNIEnv *env = NULL;
(*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_4);
jclass dynamicLinkLoader = (*env)->FindClass(env, "org/lwjgl/system/linux/DynamicLinkLoader");
if(dynamicLinkLoader == NULL) {
FCL_LOG( "LwjglLinkerHook:%s", "Failed to find the target class");
(*env)->ExceptionClear(env);
return;
}
JNINativeMethod ndlopenMethod[] = {
{"ndlopen", "(JI)J", &ndlopen_bugfix}
};
if((*env)->RegisterNatives(env, dynamicLinkLoader, ndlopenMethod, 1) != 0) {
FCL_LOG( "LwjglLinkerHook:%s", "Failed to register the hooked method");
(*env)->ExceptionClear(env);
}
}
18 changes: 18 additions & 0 deletions FCLauncher/src/main/jni/glfw/osmesa_context.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
#include <android/native_window.h>
#include <android/log.h>
#include <jni.h>
#include "fcl_internal.h"

extern void installLwjglDlopenHook(JavaVM* vm);

int (*vtest_main) (int argc, char** argv);
void (*vtest_swap_buffers) (void);
Expand Down Expand Up @@ -468,8 +471,23 @@ GLFWAPI OSMesaContext glfwGetOSMesaContext(GLFWwindow* handle)
return window->context.osmesa.handle;
}

void* maybe_load_vulkan() {
// We use the env var because
// 1. it's easier to do that
// 2. it won't break if something will try to load vulkan and osmesa simultaneously
if(getenv("VULKAN_PTR") == NULL) load_vulkan();
return (void*) strtoul(getenv("VULKAN_PTR"), NULL, 0x10);
}

JNIEXPORT jlong JNICALL
Java_org_lwjgl_vulkan_VK_getVulkanDriverHandle(JNIEnv *env, jclass thiz) {
if (getenv("VULKAN_PTR") == NULL) load_vulkan();
return strtoul(getenv("VULKAN_PTR"), NULL, 0x10);
}

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
if (fcl->android_jvm != vm) {
installLwjglDlopenHook(vm);
}
return JNI_VERSION_1_2;
}
13 changes: 8 additions & 5 deletions FCLauncher/src/main/jni/pojav/egl_bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,17 @@ EXTERNAL_API void* pojavCreateContext(void* contextSrc) {
return br_init_context((basic_render_window_t*)contextSrc);
}

EXTERNAL_API JNIEXPORT jlong JNICALL
Java_org_lwjgl_vulkan_VK_getVulkanDriverHandle(ABI_COMPAT JNIEnv *env, ABI_COMPAT jclass thiz) {
printf("EGLBridge: LWJGL-side Vulkan loader requested the Vulkan handle\n");
// The code below still uses the env var because
void* maybe_load_vulkan() {
// We use the env var because
// 1. it's easier to do that
// 2. it won't break if something will try to load vulkan and osmesa simultaneously
if(getenv("VULKAN_PTR") == NULL) load_vulkan();
return strtoul(getenv("VULKAN_PTR"), NULL, 0x10);
return (void*) strtoul(getenv("VULKAN_PTR"), NULL, 0x10);
}
EXTERNAL_API JNIEXPORT jlong JNICALL
Java_org_lwjgl_vulkan_VK_getVulkanDriverHandle(ABI_COMPAT JNIEnv *env, ABI_COMPAT jclass thiz) {
printf("EGLBridge: LWJGL-side Vulkan loader requested the Vulkan handle\n");
return (jlong) maybe_load_vulkan();
}

EXTERNAL_API void pojavSwapInterval(int interval) {
Expand Down
46 changes: 3 additions & 43 deletions FCLauncher/src/main/jni/pojav/input_bridge_v3.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ jint (*orig_ProcessImpl_forkAndExec)(JNIEnv *env, jobject process, jint mode, jb
static void registerFunctions(JNIEnv *env);

void hookExec();
void installLinkerBugMitigation();
extern void installLwjglDlopenHook();
void installEMUIIteratorMititgation();

JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNIEnv* env, jclass clazz, jint action, jbyteArray copySrc);

jint JNI_OnLoad(JavaVM* vm, __attribute__((unused)) void* reserved) {
Expand All @@ -65,7 +66,7 @@ jint JNI_OnLoad(JavaVM* vm, __attribute__((unused)) void* reserved) {
jobject mouseDownBufferJ = (*pojav_environ->runtimeJNIEnvPtr_JRE)->GetStaticObjectField(pojav_environ->runtimeJNIEnvPtr_JRE, pojav_environ->vmGlfwClass, field_mouseDownBuffer);
pojav_environ->mouseDownBuffer = (*pojav_environ->runtimeJNIEnvPtr_JRE)->GetDirectBufferAddress(pojav_environ->runtimeJNIEnvPtr_JRE, mouseDownBufferJ);
hookExec();
installLinkerBugMitigation();
installLwjglDlopenHook();
installEMUIIteratorMititgation();
}

Expand Down Expand Up @@ -260,47 +261,6 @@ void hookExec() {
printf("Registered forkAndExec\n");
}

/**
* Basically a verbatim implementation of ndlopen(), found at
* https://github.com/PojavLauncherTeam/lwjgl3/blob/3.3.1/modules/lwjgl/core/src/generated/c/linux/org_lwjgl_system_linux_DynamicLinkLoader.c#L11
* The idea is that since, on Android 10 and earlier, the linker doesn't really do namespace nesting.
* It is not a problem as most of the libraries are in the launcher path, but when you try to run
* VulkanMod which loads shaderc outside of the default jni libs directory through this method,
* it can't load it because the path is not in the allowed paths for the anonymous namesapce.
* This method fixes the issue by being in libpojavexec, and thus being in the classloader namespace
*/
jlong ndlopen_bugfix(__attribute__((unused)) JNIEnv *env,
__attribute__((unused)) jclass class,
jlong filename_ptr,
jint jmode) {
const char* filename = (const char*) filename_ptr;
int mode = (int)jmode;
return (jlong) dlopen(filename, mode);
}

/**
* Install the linker bug mitigation for Android 10 and lower. Fixes VulkanMod crashing on these
* Android versions due to missing namespace nesting.
*/
void installLinkerBugMitigation() {
if(android_get_device_api_level() >= 30) return;
FCL_LOG("Api29LinkerFix: API < 30 detected, installing linker bug mitigation");
JNIEnv* env = pojav_environ->runtimeJNIEnvPtr_JRE;
jclass dynamicLinkLoader = (*env)->FindClass(env, "org/lwjgl/system/linux/DynamicLinkLoader");
if(dynamicLinkLoader == NULL) {
FCL_LOG("Api29LinkerFix: Failed to find the target class");
(*env)->ExceptionClear(env);
return;
}
JNINativeMethod ndlopenMethod[] = {
{"ndlopen", "(JI)J", &ndlopen_bugfix}
};
if((*env)->RegisterNatives(env, dynamicLinkLoader, ndlopenMethod, 1) != 0) {
FCL_LOG("Api29LinkerFix: Failed to register the bugfix method");
(*env)->ExceptionClear(env);
}
}

/**
* This function is meant as a substitute for SharedLibraryUtil.getLibraryPath() that just returns 0
* (thus making the parent Java function return null). This is done to avoid using the LWJGL's default function,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
// Boardwalk: missing include
#include <string.h>

#include "include/fcl_internal.h"
#include "fcl/include/fcl_internal.h"


// Uncomment to try redirect signal handling to JVM
Expand Down
70 changes: 70 additions & 0 deletions FCLauncher/src/main/jni/pojav/lwjgl_dlopen_hook.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//
// Created by maks on 06.01.2025.
//

#include <android/api-level.h>
#include <android/log.h>
#include <jni.h>

#include "environ/environ.h"

#include <dlfcn.h>
#include <string.h>
#include <stdlib.h>
#include "fcl/include/fcl_internal.h"

extern void* maybe_load_vulkan();

/**
* Basically a verbatim implementation of ndlopen(), found at
* https://github.com/PojavLauncherTeam/lwjgl3/blob/3.3.1/modules/lwjgl/core/src/generated/c/linux/org_lwjgl_system_linux_DynamicLinkLoader.c#L11
* but with our own additions for stuff like vulkanmod.
*/
static jlong ndlopen_bugfix(__attribute__((unused)) JNIEnv *env,
__attribute__((unused)) jclass class,
jlong filename_ptr,
jint jmode) {
const char* filename = (const char*) filename_ptr;

// Oveeride vulkan loading to let us load vulkan ourselves
if(strstr(filename, "libvulkan.so") == filename) {
FCL_LOG("LWJGL linkerhook: replacing load for libvulkan.so with custom driver");
return (jlong) maybe_load_vulkan();
}

if (getenv("RENDERER_HANDLE") != NULL && strstr(filename,"plugin")) {
return (jlong) strtol(getenv("RENDERER_HANDLE"), NULL, 10);
}

// This hook also serves the task of mitigating a bug: the idea is that since, on Android 10 and
// earlier, the linker doesn't really do namespace nesting.
// It is not a problem as most of the libraries are in the launcher path, but when you try to run
// VulkanMod which loads shaderc outside of the default jni libs directory through this method,
// it can't load it because the path is not in the allowed paths for the anonymous namesapce.
// This method fixes the issue by being in libpojavexec, and thus being in the classloader namespace

int mode = (int)jmode;
jlong handle = (jlong) dlopen(filename, mode);
return handle;
}

/**
* Install the LWJGL dlopen hook. This allows us to mitigate linker bugs and add custom library overrides.
*/
void installLwjglDlopenHook() {
FCL_LOG("LwjglLinkerHook:%s", "Installing LWJGL dlopen() hook");
JNIEnv* env = pojav_environ->runtimeJNIEnvPtr_JRE;
jclass dynamicLinkLoader = (*env)->FindClass(env, "org/lwjgl/system/linux/DynamicLinkLoader");
if(dynamicLinkLoader == NULL) {
FCL_LOG( "LwjglLinkerHook:%s", "Failed to find the target class");
(*env)->ExceptionClear(env);
return;
}
JNINativeMethod ndlopenMethod[] = {
{"ndlopen", "(JI)J", &ndlopen_bugfix}
};
if((*env)->RegisterNatives(env, dynamicLinkLoader, ndlopenMethod, 1) != 0) {
FCL_LOG( "LwjglLinkerHook:%s", "Failed to register the hooked method");
(*env)->ExceptionClear(env);
}
}
8 changes: 8 additions & 0 deletions LWJGL-Boat/src/main/java/org/lwjgl/glfw/GLFW.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
*/
public class GLFW {

static {
try {
System.loadLibrary("glfw");
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
}
}

public static final SharedLibrary GLFW = Library.loadNative(GLFW.class, "org.lwjgl.glfw", Configuration.GLFW_LIBRARY_NAME.get(Platform.mapLibraryNameBundled("glfw")), true);

/** Contains the function pointers loaded from the glfw {@link SharedLibrary}. */
Expand Down