mirror of
https://github.com/AngelAuraMC/Amethyst-Android.git
synced 2025-09-11 13:45:48 -04:00
Style[pojavexec]: move hooks to separate dirs, use logging macros
This commit is contained in:
parent
23cc342c95
commit
40e2eb8c21
@ -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");
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
14
app_pojavlauncher/src/main/jni/jvm_hooks/jvm_hooks.h
Normal file
14
app_pojavlauncher/src/main/jni/jvm_hooks/jvm_hooks.h
Normal 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
|
@ -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);
|
||||
}
|
||||
}
|
@ -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
|
||||
|
||||
|
33
app_pojavlauncher/src/main/jni/native_hooks/chmod_hook.c
Normal file
33
app_pojavlauncher/src/main/jni/native_hooks/chmod_hook.c
Normal 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);
|
||||
}
|
86
app_pojavlauncher/src/main/jni/native_hooks/exit_hook.c
Normal file
86
app_pojavlauncher/src/main/jni/native_hooks/exit_hook.c
Normal 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);
|
||||
}
|
15
app_pojavlauncher/src/main/jni/native_hooks/native_hooks.h
Normal file
15
app_pojavlauncher/src/main/jni/native_hooks/native_hooks.h
Normal 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
|
@ -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);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user