diff options
-rw-r--r-- | android/libwrap.c | 95 |
1 files changed, 95 insertions, 0 deletions
diff --git a/android/libwrap.c b/android/libwrap.c new file mode 100644 index 0000000..2a029d6 --- /dev/null +++ b/android/libwrap.c @@ -0,0 +1,95 @@ +// Need android-ndk, compile with: +// i686-linux-android-gcc --sysroot=$ANDROID_NDK/platforms/android-9/arch-x86 -std=c99 -shared -I../src -DANDROID_X86_LINKER -DVERBOSE_FUNCTIONS libwrap.c ../src/wrapper/wrapper.c ../src/jvm/jvm.c -llog -o libwrap.so +// Rename libwrap.so to the library's name you want to middle-man, and the original library to liborig.so +// +// We use logging thread to avoid putting android specific code to verbose/wrapper.h +// Logging thread will catch stdout && stderr. + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <dlfcn.h> +#include <pthread.h> +#include <assert.h> +#include <android/log.h> +#include "jvm/jvm.h" +#include "wrapper/wrapper.h" + +static int pfd[2]; + +static void* +log_thread(void *arg) +{ + char buf[255]; + ssize_t r; + while ((r = read(pfd[0], buf, sizeof(buf) - 1)) > 0) { + r = (r > 0 && buf[r - 1] == '\n' ? r - 1 : r); + buf[r] = 0; + __android_log_write(ANDROID_LOG_INFO, "native", buf); + } + return NULL; +} + +static void +stdlog_workaround(void) +{ + setvbuf(stdout, 0, _IOLBF, 0); + setvbuf(stderr, 0, _IONBF, 0); + pipe(pfd); + dup2(pfd[1], 1); + dup2(pfd[1], 2); + pthread_t thread; + pthread_create(&thread, 0, log_thread, NULL); + pthread_detach(thread); +} + +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved) +{ + stdlog_workaround(); + + __android_log_print(ANDROID_LOG_INFO, "native", "Loading liborig.so"); + void *handle = dlopen("liborig.so", RTLD_NOW); + assert(handle); + + jint (*JNI_OnLoad)(JavaVM*, void*) = dlsym(handle, "JNI_OnLoad"); + assert(JNI_OnLoad); + + struct jvm jvm; + jvm_init(&jvm); + + __android_log_print(ANDROID_LOG_INFO, "native", "Call liborig's JNI_OnLoad with fakevm"); + JNI_OnLoad(&jvm.vm, NULL); + + // Sort of hack, may fail on some libraries maybe. + // Some libs store the vm ^ in above call, we want it to use the real vm though. + // We only pass fake vm in above call to know what natives it registered. + JNI_OnLoad(vm, NULL); + + JNIEnv *env; + if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK) + return -1; + + __android_log_print(ANDROID_LOG_INFO, "native", "Wrapping natives methods"); + + jclass old_klass = jvm.methods[0].method.klass; + JNINativeMethod methods[255]; + for (size_t i = 0, c = 0; c < 255 && i < 255; ++i, ++c) { + if (jvm.methods[i].method.klass != old_klass) { + assert(old_klass > 0); + jclass klass = (*env)->FindClass(env, jvm.objects[(intptr_t)old_klass - 1].klass.name.data); + (*env)->RegisterNatives(env, klass, methods, c); + old_klass = jvm.methods[i].method.klass; + c = 0; + } + + if (!jvm.methods[i].function) + break; + + methods[c].name = jvm.methods[i].method.name.data; + methods[c].signature = jvm.methods[i].method.signature.data; + methods[c].fnPtr = wrapper_create(methods[c].name, jvm.methods[i].function); + } + + return JNI_VERSION_1_6; +} |