refactor source tree organization, switch to meson

This commit is contained in:
Mis012
2022-10-02 23:06:56 +02:00
parent 2f785e2a59
commit 449090143e
296 changed files with 171615 additions and 69 deletions

314
src/main-executable/main.c Normal file
View File

@@ -0,0 +1,314 @@
#include <gtk/gtk.h>
#include "../api-impl-jni/defines.h"
#include "../api-impl-jni/util.h"
#include <dlfcn.h>
#include <errno.h>
#include <stdio.h>
#include <sys/stat.h>
// generated by meson
#include "config.h"
GtkWidget *window;
// standard Gtk Application stuff, more or less
gboolean app_exit(GtkWindow* self, JNIEnv *env) // TODO: do more cleanup?
{
/* -- run the main activity's onDestroy -- */
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onDestroy, NULL);
return false;
}
// FIXME: used by hacks in GLSurfaceView
int FIXME__WIDTH;
int FIXME__HEIGHT;
char *construct_classpath(char *prefix, char *path_to_prepend, char **cp_array, size_t len)
{
size_t result_len = strlen(prefix);
for(int i = 0; i < len; i++) {
result_len += strlen(path_to_prepend) + strlen(cp_array[i]) + 1; // the 1 is for either : or the final \0
}
char *result = malloc(result_len);
strcpy(result, prefix);
for(int i = 0; i < len; i++) {
strcat(result, path_to_prepend);
strcat(result, cp_array[i]);
if (i < (len - 1))
strcat(result, ":");
}
return result;
}
char *construct_boot_classpath(char *prefix, char **cp_array, size_t len)
{
char *android_root_path = getenv("ANDROID_ROOT");
char *framework_dir = "/framework/";
char *framework_dir_path = malloc(strlen(android_root_path) + strlen(framework_dir) + 1);
strcpy(framework_dir_path, android_root_path);
strcat(framework_dir_path, framework_dir);
char *result = construct_classpath(prefix, framework_dir_path, cp_array, len);
free(framework_dir_path);
return result;
}
JNIEnv* create_vm(char *api_impl_jar, char *apk_classpath, char *microg_apk) {
JavaVM* jvm;
JNIEnv* env;
JavaVMInitArgs args;
JavaVMOption options[3];
args.version = JNI_VERSION_1_6;
args.nOptions = 3;
// TODO: should probably not hardcode this
char *boot_cp_arr[] = {
"apache-xml-hostdex.jar",
"core-junit-hostdex.jar",
"core-libart-hostdex.jar",
"dex-host.jar",
"dx.jar",
"hamcrest-hostdex.jar",
"jarjar.jar",
};
options[0].optionString = construct_boot_classpath("-Xbootclasspath:", boot_cp_arr, ARRAY_SIZE(boot_cp_arr));
// micorg is purposefully after the apk, so that we get the correct resources.arsc
// TODO: request resources.arsc from concrete classloader instead of taking the first one in classpath
char *cp_array[] = {
api_impl_jar,
apk_classpath,
microg_apk,
};
options[1].optionString = construct_classpath("-Djava.class.path=", "", cp_array, ARRAY_SIZE(cp_array));
options[2].optionString = "-verbose:jni";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
int ret = JNI_CreateJavaVM(&jvm, (void **)&env, &args);
if (ret<0){
printf("Unable to Launch JVM\n");
} else {
printf("JVM launched successfully\n");
}
return env;
}
// this is an extern "C" function which we added to art in order to simplify calling the underlying c++ method,
// which we use to inject our JNI library into the correct classloader without having to load it from java code
bool HAXX__JavaVMExt__LoadNativeLibrary(JNIEnv* env, char *path, jobject class_loader, char** error_msg);
#define API_IMPL_JAR_PATH_LIBDIR INSTALL_LIBDIR "/java/dex/api-impl.jar"
#define API_IMPL_JAR_PATH_LOCAL "./api-impl.jar"
#define MICROG_APK_PATH_LIBDIR INSTALL_LIBDIR "/java/dex/com.google.android.gms.apk"
#define MICROG_APK_PATH_LOCAL "./com.google.android.gms.apk"
struct jni_callback_data { char *apk_main_activity_class; uint32_t window_width; uint32_t window_height;};
static void open(GtkApplication *app, GFile** files, gint nfiles, const gchar* hint, struct jni_callback_data *d)
{
//TODO: pass all files to classpath
/*
printf("nfiles: %d\n", nfiles);
for(int i = 0; i < nfiles; i++) {
printf(">- [%s]\n", g_file_get_path(files[i]));
}
*/
char *api_impl_jar;
char *microg_apk;
int errno_libdir;
int errno_localdir;
int ret;
char *apk_classpath = g_file_get_path(files[0]);
if(apk_classpath == NULL) {
printf("error: the specified file path doesn't seem to be valid\n");
exit(1);
}
struct stat dont_care;
ret = stat(API_IMPL_JAR_PATH_LOCAL, &dont_care);
errno_localdir = errno;
if(!ret) {
api_impl_jar = API_IMPL_JAR_PATH_LOCAL;
} else {
ret = stat(API_IMPL_JAR_PATH_LIBDIR, &dont_care); // for running out of builddir
errno_libdir = errno;
if(!ret) {
api_impl_jar = API_IMPL_JAR_PATH_LIBDIR;
} else {
printf("error: can't stat api-impl.jar; tried:\n"
"\t\"" API_IMPL_JAR_PATH_LOCAL "\", got - %s\n"
"\t\"" API_IMPL_JAR_PATH_LIBDIR "\", got - %s\n",
strerror(errno_localdir), strerror(errno_libdir));
exit(1);
}
}
ret = stat(MICROG_APK_PATH_LOCAL, &dont_care);
errno_localdir = errno;
if(!ret) {
microg_apk = MICROG_APK_PATH_LOCAL;
} else {
ret = stat(MICROG_APK_PATH_LIBDIR, &dont_care); // for running out of builddir
errno_libdir = errno;
if(!ret) {
microg_apk = MICROG_APK_PATH_LIBDIR;
} else {
printf("warning: can't stat com.google.android.gms.apk; tried:\n"
"\t\"" MICROG_APK_PATH_LOCAL "\", got - %s\n"
"\t\"" MICROG_APK_PATH_LIBDIR "\", got - %s\n",
strerror(errno_localdir), strerror(errno_libdir));
}
}
//FIXME
ret = stat("./data", &dont_care);
if(ret) {
printf("FIXME: the app's data dir is currently hardcoded to \"./data\" (this should arguably be fixed); can't stat \"./data\": %s\n", strerror(errno));
printf("also FIXME: \"./data/lib/\" must exist, or we crash\n");
printf("please note that the app's data dir currently needs special attention - it's where we read assets and apktool-preprocessed resources from\n");
printf("do also note that any app you run will use './data' directly as it's data dir, not './data/<package_name>' - this means you can't run multiple apps at once, unless the contents of their data folders happen to not conflict\n");
exit(1);
}
JNIEnv* env = create_vm(api_impl_jar, apk_classpath, microg_apk);
if(!d->apk_main_activity_class) {
printf("error: missing required option --launch-activity <activity>.\nyou can specify --help to see the list of options\n");
exit(1);
}
set_up_handle_cache(env, d->apk_main_activity_class);
jclass display_class = (*env)->FindClass(env, "android/view/Display");
_SET_STATIC_INT_FIELD(display_class, "window_width", d->window_width);
_SET_STATIC_INT_FIELD(display_class, "window_height", d->window_height);
FIXME__WIDTH = d->window_width;
FIXME__HEIGHT = d->window_height;
window = gtk_application_window_new (app);
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.set_window, _INTPTR(window));
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
gtk_window_set_title(GTK_WINDOW(window), "com.example.demo_application");
gtk_window_set_default_size(GTK_WINDOW(window), d->window_width, d->window_height);
g_signal_connect(window, "close-request", G_CALLBACK (app_exit), env);
// TODO: set icon according to how android gets it for the purposes of displaying it in the launcher
// gtk_window_set_icon_name(window, "weather-clear");
gtk_widget_show(window);
/* -- register our JNI library under the appropriate classloader -- */
/* 'android/view/View' is part of the "hax.dex" package, any other function from that package would serve just as well */
jmethodID getClassLoader = _METHOD((*env)->FindClass(env, "java/lang/Class"), "getClassLoader", "()Ljava/lang/ClassLoader;");
jobject class_loader = (*env)->CallObjectMethod(env, handle_cache.view.class, getClassLoader);
char* reason = NULL;
if (!HAXX__JavaVMExt__LoadNativeLibrary(env, "libtranslation_layer_main.so", _REF(class_loader), &reason)) {
printf("[main] dvmLoadNativeCode failed for libtranslation_layer_main.so: %s", reason);
exit(1);
}
free(reason);
/* -- run the main activity's onCreate -- */
(*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onCreate, NULL);
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);
// TODO: some apps wait for this to actually do stuff
/* (*env)->CallVoidMethod(env, handle_cache.apk_main_activity.object, handle_cache.apk_main_activity.onWindowFocusChanged, true);
if((*env)->ExceptionCheck(env))
(*env)->ExceptionDescribe(env);*/
}
static void activate(GtkApplication *app, struct jni_callback_data *d)
{
printf("error: usage: ./main [app.apk] [path/to/activity]\nyou can specify --help to see the list of options\n");
exit(1);
}
void init_cmd_parameters(GApplication *app, struct jni_callback_data *d)
{
const GOptionEntry cmd_params[] =
{
{
.long_name = "launch-activity",
.short_name = 'l',
.flags = G_OPTION_FLAG_NONE,
.arg = G_OPTION_ARG_STRING,
.arg_data = &d->apk_main_activity_class,
.description = "the fully quilifed name of the activity you wish to launch (usually the apk's main activity)",
.arg_description = NULL,
},
{
.long_name = "window-width",
.short_name = 'w',
.flags = G_OPTION_FLAG_NONE,
.arg = G_OPTION_ARG_INT,
.arg_data = &d->window_width,
.description = "window width to launch with (some apps react poorly to runtime window size adjustments)",
.arg_description = NULL,
},
{
.long_name = "window-height",
.short_name = 'h',
.flags = G_OPTION_FLAG_NONE,
.arg = G_OPTION_ARG_INT,
.arg_data = &d->window_height,
.description = "window height to launch with (some apps react poorly to runtime window size adjustments)",
.arg_description = NULL,
},
{NULL}
};
g_application_add_main_option_entries (G_APPLICATION (app), cmd_params);
}
void init__r_debug();
int main(int argc, char **argv/*, JNIEnv *env*/)
{
GtkApplication *app;
int status;
/* this has to be done in the main executable, so might as well do it here*/
init__r_debug();
struct jni_callback_data *callback_data = malloc(sizeof(struct jni_callback_data));
callback_data->apk_main_activity_class = NULL;
callback_data->window_width = 960;
callback_data->window_height = 540;
app = gtk_application_new("com.example.demo_application", G_APPLICATION_NON_UNIQUE | G_APPLICATION_HANDLES_OPEN);
// cmdline related setup
init_cmd_parameters(G_APPLICATION(app), callback_data);
g_application_set_option_context_summary(G_APPLICATION(app), "actual usage:\nLD_PRELOAD=libpthread_bio.so ./dalvik/dalvik -verbose:jni -cp hax_arsc_parser.dex:hax_xmlpull.dex:hax.dex:main.dex:${1}:com.google.android.gms.apk org/launch/main ${2}\nwhere ${1} is the path to the apk and ${2} is the cmdline");
g_signal_connect(app, "activate", G_CALLBACK (activate), callback_data);
g_signal_connect(app, "open", G_CALLBACK (open), callback_data);
status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
const char dl_loader[] __attribute__((section(".interp"))) =
"/lib/ld-linux.so.2";

View File

@@ -0,0 +1,36 @@
#include <stdio.h>
#include <stdlib.h>
// for getting _r_debug out of the dynamic section
// this is needed by the shim bionic linker to register stuff with gdb
#include <elf.h>
#include <link.h>
// the dynamic section
extern Elf64_Dyn _DYNAMIC[];
extern struct r_debug *_r_debug_ptr;
// this has to be called from the main executable, since that's the only one guaranteed to have the debug section filled in
void init__r_debug() {
#if defined(_r_debug)
// _r_debug is defined by glibc and is declared as extern in link.h
_r_debug_ptr = &_r_debug;
#else
int i = 0;
Elf64_Dyn current;
do {
current = _DYNAMIC[i];
if(current.d_tag == DT_DEBUG) {
_r_debug_ptr = (struct r_debug *)current.d_un.d_ptr;
break;
}
i++;
} while(current.d_tag != 0);
if(!_r_debug_ptr) {
fprintf(stderr, "error: no DEBUG tag in the dynamic section, treating this as fatal\n");
exit(1);
}
#endif
}