From 693f90b234db37042567a926b5a679840540d2ee Mon Sep 17 00:00:00 2001 From: artdeell Date: Mon, 22 Jan 2024 22:35:59 +0300 Subject: [PATCH] Workaround[lwjgl]: Implement mitigation for an early EMUI linker hang. --- .../main/java/net/kdt/pojavlaunch/Tools.java | 18 +++++++++ .../net/kdt/pojavlaunch/utils/JREUtils.java | 2 + .../src/main/jni/input_bridge_v3.c | 38 +++++++++++++++++++ app_pojavlauncher/src/main/jni/utils.h | 1 + 4 files changed, 59 insertions(+) diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java index 9a612d7cf4..294f0c9259 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java @@ -87,6 +87,7 @@ import java.util.Arrays; import java.util.Date; import java.util.List; +import java.util.Locale; import java.util.Map; @SuppressWarnings("IOStreamConstructor") @@ -1093,6 +1094,23 @@ public static int mesureTextviewHeight(TextView t) { return t.getMeasuredHeight(); } + /** + * Check if the device is one of the devices that may be affected by the hanging linker issue. + * The device is affected if the linker causes the process to lock up when dlopen() is called within + * dl_iterate_phdr(). + * For now, the only affected firmware that I know of is Android 5.1, EMUI 3.1 on MTK-based Huawei + * devices. + * @return if the device is affected by the hanging linker issue. + */ + public static boolean deviceHasHangingLinker() { + // Android Oreo and onwards have GSIs and most phone firmwares at that point were not modified + // *that* intrusively. So assume that we are not affected. + if(SDK_INT >= Build.VERSION_CODES.O) return false; + // Since the affected function in LWJGL is rarely used (and when used, it's mainly for debug prints) + // we can make the search scope a bit more broad and check if we are running on a Huawei device. + return Build.MANUFACTURER.toLowerCase(Locale.ROOT).contains("huawei"); + } + public static class RenderersList { public final List rendererIds; public final String[] rendererDisplayNames; diff --git a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java index 0094e8d9cb..a145609b10 100644 --- a/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java +++ b/app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/utils/JREUtils.java @@ -196,6 +196,8 @@ public static void setJavaEnvironment(Activity activity, String jreHome) throws envMap.put("POJAV_ZINK_PREFER_SYSTEM_DRIVER", "1"); if(PREF_VSYNC_IN_ZINK) envMap.put("POJAV_VSYNC_IN_ZINK", "1"); + if(Tools.deviceHasHangingLinker()) + envMap.put("POJAV_EMUI_ITERATOR_MITIGATE", "1"); // The OPEN GL version is changed according diff --git a/app_pojavlauncher/src/main/jni/input_bridge_v3.c b/app_pojavlauncher/src/main/jni/input_bridge_v3.c index 3dfd0cc366..e877e57efc 100644 --- a/app_pojavlauncher/src/main/jni/input_bridge_v3.c +++ b/app_pojavlauncher/src/main/jni/input_bridge_v3.c @@ -61,6 +61,7 @@ jint JNI_OnLoad(JavaVM* vm, __attribute__((unused)) void* reserved) { pojav_environ->mouseDownBuffer = (*pojav_environ->runtimeJNIEnvPtr_JRE)->GetDirectBufferAddress(pojav_environ->runtimeJNIEnvPtr_JRE, mouseDownBufferJ); hookExec(); installLinkerBugMitigation(); + installEMUIIteratorMititgation(); } if(pojav_environ->dalvikJavaVMPtr == vm) { @@ -294,6 +295,43 @@ void installLinkerBugMitigation() { } } +/** + * 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, + * which will hang the crappy EMUI linker by dlopen()ing inside of dl_iterate_phdr(). + * @return 0, to make the parent Java function return null immediately. + * For reference: https://github.com/PojavLauncherTeam/lwjgl3/blob/fix_huawei_hang/modules/lwjgl/core/src/main/java/org/lwjgl/system/SharedLibraryUtil.java + */ +jint getLibraryPath_fix(__attribute__((unused)) JNIEnv *env, + __attribute__((unused)) jclass class, + __attribute__((unused)) jlong pLibAddress, + __attribute__((unused)) jlong sOutAddress, + __attribute__((unused)) jint bufSize){ + return 0; +} + +/** + * Install the linker hang mitigation that is meant to prevent linker hangs on old EMUI firmware. + */ +void installEMUIIteratorMititgation() { + if(getenv("POJAV_EMUI_ITERATOR_MITIGATE") == NULL) return; + __android_log_print(ANDROID_LOG_INFO, "EMUIIteratorFix", "Installing..."); + JNIEnv* env = pojav_environ->runtimeJNIEnvPtr_JRE; + jclass sharedLibraryUtil = (*env)->FindClass(env, "org/lwjgl/system/SharedLibraryUtil"); + if(sharedLibraryUtil == NULL) { + __android_log_print(ANDROID_LOG_ERROR, "EMUIIteratorFix", "Failed to find the target class"); + (*env)->ExceptionClear(env); + return; + } + JNINativeMethod getLibraryPathMethod[] = { + {"getLibraryPath", "(JJI)I", &getLibraryPath_fix} + }; + if((*env)->RegisterNatives(env, sharedLibraryUtil, getLibraryPathMethod, 1) != 0) { + __android_log_print(ANDROID_LOG_ERROR, "EMUIIteratorFix", "Failed to register the mitigation method"); + (*env)->ExceptionClear(env); + } +} + void critical_set_stackqueue(jboolean use_input_stack_queue) { pojav_environ->isUseStackQueueCall = (int) use_input_stack_queue; } diff --git a/app_pojavlauncher/src/main/jni/utils.h b/app_pojavlauncher/src/main/jni/utils.h index ed2acbf8e9..4a03c726a2 100644 --- a/app_pojavlauncher/src/main/jni/utils.h +++ b/app_pojavlauncher/src/main/jni/utils.h @@ -11,5 +11,6 @@ jstring convertStringJVM(JNIEnv* srcEnv, JNIEnv* dstEnv, jstring srcStr); void hookExec(); void installLinkerBugMitigation(); +void installEMUIIteratorMititgation(); JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNIEnv* env, jclass clazz, jint action, jbyteArray copySrc);