Style[pojavexec]: move hooks to separate dirs, use logging macros

This commit is contained in:
artdeell 2025-01-24 10:33:50 +03:00 committed by Maksim Belov
parent 23cc342c95
commit 40e2eb8c21
18 changed files with 267 additions and 216 deletions

View File

@ -104,7 +104,8 @@ public class JREUtils {
public void run() {
try {
if (logcatPb == null) {
logcatPb = new ProcessBuilder().command("logcat", /* "-G", "1mb", */ "-v", "brief", "-s", "jrelog:I", "LIBGL:I", "NativeInput").redirectErrorStream(true);
// No filtering by tag anymore as that relied on incorrect log levels set in log.h
logcatPb = new ProcessBuilder().command("logcat", /* "-G", "1mb", */ "-v", "brief", "-s", "jrelog", "LIBGL", "NativeInput").redirectErrorStream(true);
}
Log.i("jrelog-logcat","Clearing logcat");

View File

@ -28,12 +28,13 @@ LOCAL_SRC_FILES := \
ctxbridges/osmesa_loader.c \
ctxbridges/swap_interval_no_egl.c \
environ/environ.c \
jvm_hooks/emui_iterator_fix_hook.c \
jvm_hooks/java_exec_hooks.c \
jvm_hooks/lwjgl_dlopen_hook.c \
input_bridge_v3.c \
jre_launcher.c \
utils.c \
stdio_is.c \
java_exec_hooks.c \
lwjgl_dlopen_hook.c \
driver_helper/nsbypass.c
ifeq ($(TARGET_ARCH_ABI),arm64-v8a)
@ -45,7 +46,9 @@ include $(CLEAR_VARS)
LOCAL_MODULE := exithook
LOCAL_LDLIBS := -ldl -llog
LOCAL_SHARED_LIBRARIES := bytehook pojavexec
LOCAL_SRC_FILES := exit_hook.c
LOCAL_SRC_FILES := \
native_hooks/exit_hook.c \
native_hooks/chmod_hook.c
include $(BUILD_SHARED_LIBRARY)
#ifeq ($(TARGET_ARCH_ABI),arm64-v8a)

View File

@ -1,5 +1,4 @@
#include <EGL/egl.h>
#include <android/log.h>
#include <android/native_window.h>
#include <android/native_window_jni.h>
#include <string.h>
@ -11,11 +10,13 @@
#include "gl_bridge.h"
#include "egl_loader.h"
#define TAG __FILE_NAME__
#include <log.h>
//
// Created by maks on 17.09.2022.
//
static const char* g_LogTag = "GLBridge";
static __thread gl_render_window_t* currentBundle;
static EGLDisplay g_EglDisplay;
@ -23,13 +24,11 @@ bool gl_init() {
if(!dlsym_EGL()) return false;
g_EglDisplay = eglGetDisplay_p(EGL_DEFAULT_DISPLAY);
if (g_EglDisplay == EGL_NO_DISPLAY) {
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s",
"eglGetDisplay_p(EGL_DEFAULT_DISPLAY) returned EGL_NO_DISPLAY");
LOGE("%s", "eglGetDisplay_p(EGL_DEFAULT_DISPLAY) returned EGL_NO_DISPLAY");
return false;
}
if (eglInitialize_p(g_EglDisplay, 0, 0) != EGL_TRUE) {
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "eglInitialize_p() failed: %04x",
eglGetError_p());
LOGE("eglInitialize_p() failed: %04x", eglGetError_p());
return false;
}
return true;
@ -62,14 +61,12 @@ gl_render_window_t* gl_init_context(gl_render_window_t *share) {
EGLint num_configs = 0;
if (eglChooseConfig_p(g_EglDisplay, egl_attributes, NULL, 0, &num_configs) != EGL_TRUE) {
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "eglChooseConfig_p() failed: %04x",
eglGetError_p());
LOGE("eglChooseConfig_p() failed: %04x", eglGetError_p());
free(bundle);
return NULL;
}
if (num_configs == 0) {
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "%s",
"eglChooseConfig_p() found no matching config");
LOGE("%s", "eglChooseConfig_p() found no matching config");
free(bundle);
return NULL;
}
@ -96,8 +93,7 @@ gl_render_window_t* gl_init_context(gl_render_window_t *share) {
bundle->context = eglCreateContext_p(g_EglDisplay, bundle->config, share == NULL ? EGL_NO_CONTEXT : share->context, egl_context_attributes);
if (bundle->context == EGL_NO_CONTEXT) {
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "eglCreateContext_p() finished with error: %04x",
eglGetError_p());
LOGE("eglCreateContext_p() finished with error: %04x", eglGetError_p());
free(bundle);
return NULL;
}
@ -110,14 +106,14 @@ void gl_swap_surface(gl_render_window_t* bundle) {
}
if(bundle->surface != NULL) eglDestroySurface_p(g_EglDisplay, bundle->surface);
if(bundle->newNativeSurface != NULL) {
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "Switching to new native surface");
LOGI("Switching to new native surface");
bundle->nativeSurface = bundle->newNativeSurface;
bundle->newNativeSurface = NULL;
ANativeWindow_acquire(bundle->nativeSurface);
ANativeWindow_setBuffersGeometry(bundle->nativeSurface, 0, 0, bundle->format);
bundle->surface = eglCreateWindowSurface_p(g_EglDisplay, bundle->config, bundle->nativeSurface, NULL);
}else{
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "No new native surface, switching to 1x1 pbuffer");
LOGI("No new native surface, switching to 1x1 pbuffer");
bundle->nativeSurface = NULL;
const EGLint pbuffer_attrs[] = {EGL_WIDTH, 1 , EGL_HEIGHT, 1, EGL_NONE};
bundle->surface = eglCreatePbufferSurface_p(g_EglDisplay, bundle->config, pbuffer_attrs);
@ -136,11 +132,11 @@ void gl_make_current(gl_render_window_t* bundle) {
bool hasSetMainWindow = false;
if(pojav_environ->mainWindowBundle == NULL) {
pojav_environ->mainWindowBundle = (basic_render_window_t*)bundle;
__android_log_print(ANDROID_LOG_INFO, g_LogTag, "Main window bundle is now %p", pojav_environ->mainWindowBundle);
LOGI("Main window bundle is now %p", pojav_environ->mainWindowBundle);
pojav_environ->mainWindowBundle->newNativeSurface = pojav_environ->pojavWindow;
hasSetMainWindow = true;
}
__android_log_print(ANDROID_LOG_INFO, g_LogTag, "Making current, surface=%p, nativeSurface=%p, newNativeSurface=%p", bundle->surface, bundle->nativeSurface, bundle->newNativeSurface);
LOGI("Making current, surface=%p, nativeSurface=%p, newNativeSurface=%p", bundle->surface, bundle->nativeSurface, bundle->newNativeSurface);
if(bundle->surface == NULL) { //it likely will be on the first run
gl_swap_surface(bundle);
}
@ -152,7 +148,7 @@ void gl_make_current(gl_render_window_t* bundle) {
gl_swap_surface((gl_render_window_t*)pojav_environ->mainWindowBundle);
pojav_environ->mainWindowBundle = NULL;
}
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "eglMakeCurrent returned with error: %04x", eglGetError_p());
LOGE("eglMakeCurrent returned with error: %04x", eglGetError_p());
}
}
@ -170,14 +166,14 @@ void gl_swap_buffers() {
currentBundle->newNativeSurface = NULL;
gl_swap_surface(currentBundle);
eglMakeCurrent_p(g_EglDisplay, currentBundle->surface, currentBundle->surface, currentBundle->context);
__android_log_print(ANDROID_LOG_INFO, g_LogTag, "The window has died, awaiting window change");
LOGI("The window has died, awaiting window change");
}
}
void gl_setup_window() {
if(pojav_environ->mainWindowBundle != NULL) {
__android_log_print(ANDROID_LOG_INFO, g_LogTag, "Main window bundle is not NULL, changing state");
LOGI("Main window bundle is not NULL, changing state");
pojav_environ->mainWindowBundle->state = STATE_RENDERER_NEW_WINDOW;
pojav_environ->mainWindowBundle->newNativeSurface = pojav_environ->pojavWindow;
}
@ -192,14 +188,14 @@ void gl_swap_interval(int swapInterval) {
JNIEXPORT void JNICALL
Java_org_lwjgl_opengl_PojavRendererInit_nativeInitGl4esInternals(JNIEnv *env, jclass clazz,
jobject function_provider) {
__android_log_print(ANDROID_LOG_INFO, g_LogTag, "GL4ES internals initializing...");
LOGI("GL4ES internals initializing...");
jclass funcProviderClass = (*env)->GetObjectClass(env, function_provider);
jmethodID method_getFunctionAddress = (*env)->GetMethodID(env, funcProviderClass, "getFunctionAddress", "(Ljava/lang/CharSequence;)J");
#define GETSYM(N) ((*env)->CallLongMethod(env, function_provider, method_getFunctionAddress, (*env)->NewStringUTF(env, N)));
void (*set_getmainfbsize)(void (*new_getMainFBSize)(int* width, int* height)) = (void*)GETSYM("set_getmainfbsize");
if(set_getmainfbsize != NULL) {
__android_log_print(ANDROID_LOG_INFO, g_LogTag, "GL4ES internals initialized dimension callback");
LOGI("GL4ES internals initialized dimension callback");
set_getmainfbsize(gl4esi_get_display_dimensions);
}

View File

@ -4,10 +4,10 @@
#include <malloc.h>
#include <string.h>
#include <environ/environ.h>
#include <android/log.h>
#include "osm_bridge.h"
#define TAG __FILE_NAME__
#include <log.h>
static const char* g_LogTag = "GLBridge";
static __thread osm_render_window_t* currentBundle;
// a tiny buffer for rendering when there's nowhere t render
static char no_render_buffer[4];
@ -49,13 +49,13 @@ void osm_set_no_render_buffer(ANativeWindow_Buffer* buffer) {
void osm_swap_surfaces(osm_render_window_t* bundle) {
if(bundle->nativeSurface != NULL && bundle->newNativeSurface != bundle->nativeSurface) {
if(!bundle->disable_rendering) {
__android_log_print(ANDROID_LOG_INFO, g_LogTag, "Unlocking for cleanup...");
LOGI("Unlocking for cleanup...");
ANativeWindow_unlockAndPost(bundle->nativeSurface);
}
ANativeWindow_release(bundle->nativeSurface);
}
if(bundle->newNativeSurface != NULL) {
__android_log_print(ANDROID_LOG_ERROR, g_LogTag, "Switching to new native surface");
LOGI("Switching to new native surface");
bundle->nativeSurface = bundle->newNativeSurface;
bundle->newNativeSurface = NULL;
ANativeWindow_acquire(bundle->nativeSurface);
@ -63,8 +63,7 @@ void osm_swap_surfaces(osm_render_window_t* bundle) {
bundle->disable_rendering = false;
return;
}else {
__android_log_print(ANDROID_LOG_ERROR, g_LogTag,
"No new native surface, switching to dummy framebuffer");
LOGI("No new native surface, switching to dummy framebuffer");
bundle->nativeSurface = NULL;
osm_set_no_render_buffer(&bundle->buffer);
bundle->disable_rendering = true;
@ -96,7 +95,7 @@ void osm_make_current(osm_render_window_t* bundle) {
currentBundle = bundle;
if(pojav_environ->mainWindowBundle == NULL) {
pojav_environ->mainWindowBundle = (basic_render_window_t*) bundle;
__android_log_print(ANDROID_LOG_INFO, g_LogTag, "Main window bundle is now %p", pojav_environ->mainWindowBundle);
LOGI("Main window bundle is now %p", pojav_environ->mainWindowBundle);
pojav_environ->mainWindowBundle->newNativeSurface = pojav_environ->pojavWindow;
hasSetMainWindow = true;
}
@ -130,7 +129,7 @@ void osm_swap_buffers() {
void osm_setup_window() {
if(pojav_environ->mainWindowBundle != NULL) {
__android_log_print(ANDROID_LOG_INFO, g_LogTag, "Main window bundle is not NULL, changing state");
LOGI("Main window bundle is not NULL, changing state");
pojav_environ->mainWindowBundle->state = STATE_RENDERER_NEW_WINDOW;
pojav_environ->mainWindowBundle->newNativeSurface = pojav_environ->pojavWindow;
}

View File

@ -5,9 +5,11 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <android/log.h>
#include <android/native_window.h>
#define TAG __FILE_NAME__
#include <log.h>
// Taken from https://android.googlesource.com/platform/frameworks/native/+/41abd67/include/ui/egl/android_natives.h
// Might be outdated, if you can find a more recent version please add it there
// region android_native_base_t definition
@ -227,17 +229,17 @@ void setNativeWindowSwapInterval(struct ANativeWindow* nativeWindow, int swapInt
}
struct ANativeWindow_real* nativeWindowReal = (struct ANativeWindow_real*) nativeWindow;
if(nativeWindowReal->common.magic != ANDROID_NATIVE_WINDOW_MAGIC) {
__android_log_print(ANDROID_LOG_WARN, "SwapIntervalNoEGL", "ANativeWindow magic does not match. Expected %i, got %i",
LOGW("ANativeWindow magic does not match. Expected %i, got %i",
ANDROID_NATIVE_WINDOW_MAGIC, nativeWindowReal->common.magic);
return;
}
if(nativeWindowReal->common.version != sizeof(struct ANativeWindow_real)) {
__android_log_print(ANDROID_LOG_WARN, "SwapIntervalNoEGL", "ANativeWindow version does not match. Expected %i, got %i",
LOGW("ANativeWindow version does not match. Expected %i, got %i",
sizeof(struct ANativeWindow_real), nativeWindowReal->common.version);
return;
}
int error;
if((error = nativeWindowReal->setSwapInterval(nativeWindow, swapInterval)) != 0) {
__android_log_print(ANDROID_LOG_WARN, "SwapIntervalNoEGL", "Failed to set swap interval: %s", strerror(-error));
LOGW("Failed to set swap interval: %s", strerror(-error));
}
}

View File

@ -82,7 +82,6 @@ bool linker_ns_load(const char* lib_search_path) {
// load the two functions we need
android_create_namespace = dlsym(ld_android_handle, "__loader_android_create_namespace");
ld_android_link_namespaces_t android_link_namespaces = dlsym(ld_android_handle, "__loader_android_link_namespaces");
__android_log_print(ANDROID_LOG_INFO, "nsbypass", "found functions at %p %p", android_create_namespace, android_link_namespaces);
if(android_create_namespace == NULL || android_link_namespaces == NULL) {
dlclose(ld_android_handle);
return false;

View File

@ -7,11 +7,14 @@
#include <assert.h>
#include <string.h>
#include "environ.h"
#define TAG __FILE_NAME__
#include <log.h>
struct pojav_environ_s *pojav_environ;
__attribute__((constructor)) void env_init() {
char* strptr_env = getenv("POJAV_ENVIRON");
if(strptr_env == NULL) {
__android_log_print(ANDROID_LOG_INFO, "Environ", "No environ found, creating...");
LOGI("No environ found, creating...");
pojav_environ = malloc(sizeof(struct pojav_environ_s));
assert(pojav_environ);
memset(pojav_environ, 0 , sizeof(struct pojav_environ_s));
@ -19,8 +22,8 @@ __attribute__((constructor)) void env_init() {
setenv("POJAV_ENVIRON", strptr_env, 1);
free(strptr_env);
}else{
__android_log_print(ANDROID_LOG_INFO, "Environ", "Found existing environ: %s", strptr_env);
LOGI("Found existing environ: %s", strptr_env);
pojav_environ = (void*) strtoul(strptr_env, NULL, 0x10);
}
__android_log_print(ANDROID_LOG_INFO, "Environ", "%p", pojav_environ);
LOGI("%p", pojav_environ);
}

View File

@ -1,115 +0,0 @@
//
// Created by maks on 15.01.2025.
//
#include <jni.h>
#include <unistd.h>
#include <stdbool.h>
#include <bytehook.h>
#include <dlfcn.h>
#include <android/log.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include "stdio_is.h"
static _Atomic bool exit_tripped = false;
typedef void (*exit_func)(int);
static void custom_exit(int code) {
// If the exit was already done (meaning it is recursive or from a different thread), pass the call through
if(exit_tripped) {
BYTEHOOK_CALL_PREV(custom_exit, exit_func, code);
BYTEHOOK_POP_STACK();
return;
}
exit_tripped = true;
// Perform a nominal exit, as we expect.
nominal_exit(code, false);
BYTEHOOK_POP_STACK();
}
// Hooks for chmod and fchmod that always return success.
// This allows older Android versions to work with Java NIO zipfs inside of the Pojav folder.
typedef int (*chmod_func)(const char*, mode_t);
typedef int (*fchmod_func)(int, mode_t);
#define TEMPLATE_HOOK(X, Y, Z, W) static int X(Y, mode_t mode) { \
int result = BYTEHOOK_CALL_PREV(X, Z, W, mode); \
if(result != 0) errno = 0; \
BYTEHOOK_POP_STACK(); \
return 0; \
} \
TEMPLATE_HOOK(custom_chmod, const char* filename, chmod_func, filename)
TEMPLATE_HOOK(custom_fchmod, int fd, fchmod_func, fd)
#undef TEMPLATE_HOOK
static void custom_atexit() {
// Same as custom_exit, but without the code or the exit passthrough.
if(exit_tripped) {
return;
}
exit_tripped = true;
nominal_exit(0, false);
}
typedef bytehook_stub_t (*bytehook_hook_all_t)(const char *callee_path_name, const char *sym_name, void *new_func,
bytehook_hooked_t hooked, void *hooked_arg);
static void create_chmod_hooks(bytehook_hook_all_t bytehook_hook_all_p) {
bytehook_stub_t stub_chmod = bytehook_hook_all_p(NULL, "chmod", &custom_chmod, NULL, NULL);
bytehook_stub_t stub_fchmod = bytehook_hook_all_p(NULL, "fchmod", &custom_fchmod, NULL, NULL);
__android_log_print(ANDROID_LOG_INFO, "chmod_hook", "Successfully initialized chmod hooks, stubs: %p %p", stub_chmod, stub_fchmod);
}
static void create_hooks(bytehook_hook_all_t bytehook_hook_all_p) {
bytehook_stub_t stub_exit = bytehook_hook_all_p(NULL, "exit", &custom_exit, NULL, NULL);
__android_log_print(ANDROID_LOG_INFO, "exit_hook", "Successfully initialized exit hook, stub: %p", stub_exit);
// TODO: figure out proper android version where these should apply
create_chmod_hooks(bytehook_hook_all_p);
}
static bool init_hooks() {
void* bytehook_handle = dlopen("libbytehook.so", RTLD_NOW);
if(bytehook_handle == NULL) {
goto dlerror;
}
bytehook_hook_all_t bytehook_hook_all_p;
int (*bytehook_init_p)(int mode, bool debug);
bytehook_hook_all_p = dlsym(bytehook_handle, "bytehook_hook_all");
bytehook_init_p = dlsym(bytehook_handle, "bytehook_init");
if(bytehook_hook_all_p == NULL || bytehook_init_p == NULL) {
goto dlerror;
}
int bhook_status = bytehook_init_p(BYTEHOOK_MODE_AUTOMATIC, false);
if(bhook_status == BYTEHOOK_STATUS_CODE_OK) {
create_hooks(bytehook_hook_all_p);
return true;
} else {
__android_log_print(ANDROID_LOG_INFO, "exit_hook", "bytehook_init failed (%i)", bhook_status);
dlclose(bytehook_handle);
return false;
}
dlerror:
if(bytehook_handle != NULL) dlclose(bytehook_handle);
__android_log_print(ANDROID_LOG_ERROR, "exit_hook", "Failed to load hook library: %s", dlerror());
return false;
}
JNIEXPORT void JNICALL
Java_net_kdt_pojavlaunch_utils_JREUtils_initializeHooks(JNIEnv *env, jclass clazz) {
bool hooks_ready = init_hooks();
if(!hooks_ready){
// If we can't hook, register atexit(). This won't report a proper error code,
// but it will prevent a SIGSEGV or a SIGABRT from the depths of Dalvik that happens
// on exit().
atexit(custom_atexit);
}
}

View File

@ -19,9 +19,11 @@
#include <stdatomic.h>
#include <math.h>
#define TAG __FILE_NAME__
#include "log.h"
#include "utils.h"
#include "environ/environ.h"
#include "jvm_hooks/jvm_hooks.h"
#define EVENT_TYPE_CHAR 1000
#define EVENT_TYPE_CHAR_MODS 1001
@ -30,12 +32,11 @@
#define EVENT_TYPE_MOUSE_BUTTON 1006
#define EVENT_TYPE_SCROLL 1007
static void installEMUIIteratorMititgation(JNIEnv *vmEnv);
static void registerFunctions(JNIEnv *env);
jint JNI_OnLoad(JavaVM* vm, __attribute__((unused)) void* reserved) {
if (pojav_environ->dalvikJavaVMPtr == NULL) {
__android_log_print(ANDROID_LOG_INFO, "Native", "Saving DVM environ...");
LOGI("Saving DVM environ...");
//Save dalvik global JavaVM pointer
pojav_environ->dalvikJavaVMPtr = vm;
JNIEnv *dvEnv;
@ -45,7 +46,7 @@ jint JNI_OnLoad(JavaVM* vm, __attribute__((unused)) void* reserved) {
pojav_environ->method_onGrabStateChanged = (*dvEnv)->GetStaticMethodID(dvEnv, pojav_environ->bridgeClazz, "onGrabStateChanged", "(Z)V");
pojav_environ->isUseStackQueueCall = JNI_FALSE;
} else if (pojav_environ->dalvikJavaVMPtr != vm) {
__android_log_print(ANDROID_LOG_INFO, "Native", "Saving JVM environ...");
LOGI("Saving JVM environ...");
pojav_environ->runtimeJavaVMPtr = vm;
JNIEnv *vmEnv;
(*vm)->GetEnv(vm, (void**) &vmEnv, JNI_VERSION_1_4);
@ -231,42 +232,6 @@ void sendData(int type, int i1, int i2, int i3, int i4) {
atomic_fetch_add_explicit(&pojav_environ->eventCounter, 1, memory_order_acquire);
}
/**
* 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.
*/
static void installEMUIIteratorMititgation(JNIEnv *env) {
if(getenv("POJAV_EMUI_ITERATOR_MITIGATE") == NULL) return;
__android_log_print(ANDROID_LOG_INFO, "EMUIIteratorFix", "Installing...");
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;
}
@ -308,7 +273,7 @@ JNIEXPORT jboolean JNICALL JavaCritical_org_lwjgl_glfw_CallbackBridge_nativeSetI
#ifdef DEBUG
LOGD("Debug: Changing input state, isReady=%d, pojav_environ->isUseStackQueueCall=%d\n", inputReady, pojav_environ->isUseStackQueueCall);
#endif
__android_log_print(ANDROID_LOG_INFO, "NativeInput", "Input ready: %i", inputReady);
LOGI("Input ready: %i", inputReady);
pojav_environ->isInputReady = inputReady;
return pojav_environ->isUseStackQueueCall;
}
@ -550,9 +515,9 @@ static void registerFunctions(JNIEnv *env) {
bool use_critical_cc = tryCriticalNative(env);
jclass bridge_class = (*env)->FindClass(env, "org/lwjgl/glfw/CallbackBridge");
if(use_critical_cc) {
__android_log_print(ANDROID_LOG_INFO, "pojavexec", "CriticalNative is available. Enjoy the 4.6x times faster input!");
LOGI("CriticalNative is available. Enjoy the 4.6x times faster input!");
}else{
__android_log_print(ANDROID_LOG_INFO, "pojavexec", "CriticalNative is not available. Upgrade, maybe?");
LOGI("CriticalNative is not available. Upgrade, maybe?");
}
(*env)->RegisterNatives(env,
bridge_class,

View File

@ -0,0 +1,45 @@
//
// Created by maks on 23.01.2025.
//
#include "jvm_hooks.h"
#include <stdlib.h>
#define TAG __FILE_NAME__
#include <log.h>
/**
* 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(JNIEnv *env) {
if(getenv("POJAV_EMUI_ITERATOR_MITIGATE") == NULL) return;
LOGI("Installing...");
jclass sharedLibraryUtil = (*env)->FindClass(env, "org/lwjgl/system/SharedLibraryUtil");
if(sharedLibraryUtil == NULL) {
LOGE("Failed to find target class");
(*env)->ExceptionClear(env);
return;
}
JNINativeMethod getLibraryPathMethod[] = {
{"getLibraryPath", "(JJI)I", &getLibraryPath_fix}
};
if((*env)->RegisterNatives(env, sharedLibraryUtil, getLibraryPathMethod, 1) != 0) {
LOGE("Failed to register the mitigation method");
(*env)->ExceptionClear(env);
}
}

View File

@ -2,15 +2,14 @@
// Created by maks on 05.01.2025.
//
#include <jni.h>
#include "jvm_hooks.h"
#include <libgen.h>
#include <string.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <environ/environ.h>
#include <android/log.h>
#include <utils.h>
#include "environ/environ.h"
#include "utils.h"
static jint (*orig_ProcessImpl_forkAndExec)(JNIEnv *env, jobject process, jint mode, jbyteArray helperpath, jbyteArray prog, jbyteArray argBlock, jint argc, jbyteArray envBlock, jint envc, jbyteArray dir, jintArray std_fds, jboolean redirectErrorStream);

View File

@ -0,0 +1,14 @@
//
// Created by maks on 23.01.2025.
//
#ifndef POJAVLAUNCHER_JVM_HOOKS_H
#define POJAVLAUNCHER_JVM_HOOKS_H
#include <jni.h>
void installEMUIIteratorMititgation(JNIEnv *env);
void installLwjglDlopenHook(JNIEnv *env);
void hookExec(JNIEnv *env);
#endif //POJAVLAUNCHER_JVM_HOOKS_H

View File

@ -2,16 +2,19 @@
// Created by maks on 06.01.2025.
//
#include <android/api-level.h>
#include <android/log.h>
#include <jni.h>
#include "jvm_hooks.h"
#include <environ/environ.h>
#include <android/api-level.h>
#include "environ/environ.h"
#include <dlfcn.h>
#include <string.h>
#include <stdlib.h>
#define TAG __FILE_NAME__
#include <log.h>
extern void* maybe_load_vulkan();
/**
@ -46,10 +49,10 @@ static jlong ndlopen_bugfix(__attribute__((unused)) JNIEnv *env,
* Install the LWJGL dlopen hook. This allows us to mitigate linker bugs and add custom library overrides.
*/
void installLwjglDlopenHook(JNIEnv *env) {
__android_log_print(ANDROID_LOG_INFO, "LwjglLinkerHook", "Installing LWJGL dlopen() hook");
LOGI("Installing LWJGL dlopen() hook");
jclass dynamicLinkLoader = (*env)->FindClass(env, "org/lwjgl/system/linux/DynamicLinkLoader");
if(dynamicLinkLoader == NULL) {
__android_log_print(ANDROID_LOG_ERROR, "LwjglLinkerHook", "Failed to find the target class");
LOGE("Failed to find the target class");
(*env)->ExceptionClear(env);
return;
}
@ -57,7 +60,7 @@ void installLwjglDlopenHook(JNIEnv *env) {
{"ndlopen", "(JI)J", &ndlopen_bugfix}
};
if((*env)->RegisterNatives(env, dynamicLinkLoader, ndlopenMethod, 1) != 0) {
__android_log_print(ANDROID_LOG_ERROR, "LwjglLinkerHook", "Failed to register the hooked method");
LOGE("Failed to register the hooked method");
(*env)->ExceptionClear(env);
}
}

View File

@ -1,6 +1,9 @@
#ifdef __ANDROID__
#ifndef POJAVLAUNCHER_LOG_H
#define POJAVLAUNCHER_LOG_H
#include <android/log.h>
#ifndef TAG
#define TAG "jrelog"
#endif
@ -8,12 +11,14 @@
extern "C" {
#endif
#define LOGE(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_SILENT, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_SILENT, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,33 @@
//
// Created by maks on 23.01.2025.
//
#include "native_hooks.h"
#include <unistd.h>
#include <errno.h>
#define TAG __FILE_NAME__
#include <log.h>
// Hooks for chmod and fchmod that always return success.
// This allows older Android versions to work with Java NIO zipfs inside of the Pojav folder.
typedef int (*chmod_func)(const char*, mode_t);
typedef int (*fchmod_func)(int, mode_t);
#define TEMPLATE_HOOK(X, Y, Z, W) static int X(Y, mode_t mode) { \
int result = BYTEHOOK_CALL_PREV(X, Z, W, mode); \
if(result != 0) errno = 0; \
BYTEHOOK_POP_STACK(); \
return 0; \
} \
TEMPLATE_HOOK(custom_chmod, const char* filename, chmod_func, filename)
TEMPLATE_HOOK(custom_fchmod, int fd, fchmod_func, fd)
#undef TEMPLATE_HOOK
void create_chmod_hooks(bytehook_hook_all_t bytehook_hook_all_p) {
bytehook_stub_t stub_chmod = bytehook_hook_all_p(NULL, "chmod", &custom_chmod, NULL, NULL);
bytehook_stub_t stub_fchmod = bytehook_hook_all_p(NULL, "fchmod", &custom_fchmod, NULL, NULL);
LOGI("Successfully initialized chmod hooks, stubs: %p %p", stub_chmod, stub_fchmod);
}

View File

@ -0,0 +1,86 @@
//
// Created by maks on 15.01.2025.
//
#include "native_hooks.h"
#include <jni.h>
#include <stdbool.h>
#include <bytehook.h>
#include <dlfcn.h>
#include <stdlib.h>
#include "stdio_is.h"
#define TAG __FILE_NAME__
#include <log.h>
static _Atomic bool exit_tripped = false;
static int exit_code = 0;
typedef void (*exit_func)(int);
// Use the exit hook *only* to store the exit code.
static void custom_exit(int code) {
exit_code = code;
BYTEHOOK_CALL_PREV(custom_exit, exit_func, code);
BYTEHOOK_POP_STACK();
}
static void custom_atexit() {
if(exit_tripped) {
return;
}
exit_tripped = true;
nominal_exit(exit_code, false);
}
static void create_hooks(bytehook_hook_all_t bytehook_hook_all_p) {
bytehook_stub_t stub_exit = bytehook_hook_all_p(NULL, "exit", &custom_exit, NULL, NULL);
LOGI("Successfully initialized exit hook, stub: %p", stub_exit);
// Only apply chmod hooks on devices where the game directory is in games/PojavLauncher
// which is below API 29
if(android_get_device_api_level() < 29) {
create_chmod_hooks(bytehook_hook_all_p);
}
}
static bool init_hooks() {
void* bytehook_handle = dlopen("libbytehook.so", RTLD_NOW);
if(bytehook_handle == NULL) {
goto dlerror;
}
bytehook_hook_all_t bytehook_hook_all_p;
int (*bytehook_init_p)(int mode, bool debug);
bytehook_hook_all_p = dlsym(bytehook_handle, "bytehook_hook_all");
bytehook_init_p = dlsym(bytehook_handle, "bytehook_init");
if(bytehook_hook_all_p == NULL || bytehook_init_p == NULL) {
goto dlerror;
}
int bhook_status = bytehook_init_p(BYTEHOOK_MODE_AUTOMATIC, false);
if(bhook_status == BYTEHOOK_STATUS_CODE_OK) {
create_hooks(bytehook_hook_all_p);
return true;
} else {
LOGE("bytehook_init failed (%i)", bhook_status);
dlclose(bytehook_handle);
return false;
}
dlerror:
if(bytehook_handle != NULL) dlclose(bytehook_handle);
LOGE("Failed to load hook library: %s", dlerror());
return false;
}
JNIEXPORT void JNICALL
Java_net_kdt_pojavlaunch_utils_JREUtils_initializeHooks(JNIEnv *env, jclass clazz) {
bool hooks_ready = init_hooks();
if(!hooks_ready) {
LOGE("Failed to initialize native hooks!");
}
// Always register atexit, because that's what we will call our exit from.
// We only use the hook to capture the exit code.
atexit(custom_atexit);
}

View File

@ -0,0 +1,15 @@
//
// Created by maks on 23.01.2025.
//
#ifndef POJAVLAUNCHER_NATIVE_HOOKS_H
#define POJAVLAUNCHER_NATIVE_HOOKS_H
#include <bytehook.h>
typedef bytehook_stub_t (*bytehook_hook_all_t)(const char *callee_path_name, const char *sym_name, void *new_func,
bytehook_hooked_t hooked, void *hooked_arg);
void create_chmod_hooks(bytehook_hook_all_t bytehook_hook_all_p);
#endif //POJAVLAUNCHER_NATIVE_HOOKS_H

View File

@ -11,8 +11,6 @@ jobjectArray convert_from_char_array(JNIEnv *env, char **charArray, int num_rows
void free_char_array(JNIEnv *env, jobjectArray jstringArray, const char **charArray);
jstring convertStringJVM(JNIEnv* srcEnv, JNIEnv* dstEnv, jstring srcStr);
void hookExec(JNIEnv *env);
void installLwjglDlopenHook(JNIEnv *env);
JNIEnv* get_attached_env(JavaVM* jvm);
JNIEXPORT jstring JNICALL Java_org_lwjgl_glfw_CallbackBridge_nativeClipboard(JNIEnv* env, jclass clazz, jint action, jbyteArray copySrc);