found a better way to load libraries

This commit is contained in:
qimiko committed 2024-01-07 22:01:36 -07:00
1 parent c3c19629de
commit d30d4cb008
4 files changed
+25 -76

No files matched your search

-2
View File
@@ -23,5 +23,3 @@ if(NOT dobby_POPULATED)
target_include_directories(launcherfix PRIVATE ${dobby_SOURCE_DIR}/include)
target_link_libraries(launcherfix dobby)
endif()
target_link_libraries(launcherfix log)
-33
View File
@@ -1,9 +1,6 @@
#include <android/dlext.h>
#include <android/log.h>
#include <dlfcn.h>
#include <jni.h>
#include <string>
#include <unistd.h>
#ifndef DISABLE_LAUNCHER_FIX
#include <dobby.h>
@@ -51,36 +48,6 @@ JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_setOriginalDataPath(
env->ReleaseStringUTFChars(data_path, data_path_str);
}
extern "C"
JNIEXPORT jboolean JNICALL Java_com_geode_launcher_LauncherFix_loadLibraryFromOffset(JNIEnv *env, jobject, jstring library_name, jint fd, jlong offset) {
// loads a given library at an offset and file descriptor
// assumes we have ownership of the file passed in fd
auto is_copy = jboolean();
auto library_cname = env->GetStringUTFChars(library_name, &is_copy);
android_dlextinfo ext_info{};
ext_info.flags = ANDROID_DLEXT_USE_LIBRARY_FD | ANDROID_DLEXT_USE_LIBRARY_FD_OFFSET;
ext_info.library_fd = fd;
ext_info.library_fd_offset = offset;
auto handle = android_dlopen_ext(library_cname, RTLD_NOW | RTLD_GLOBAL, &ext_info);
env->ReleaseStringUTFChars(library_name, library_cname);
close(fd);
if (handle == nullptr) {
auto error = dlerror();
__android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-Fix", "dlopen_ext failed. given: %s\n", error);
return false;
}
// we don't need the library anymore
dlclose(handle);
return true;
}
std::string redirect_path(const char* pathname) {
auto data_path = DataPaths::get_instance().data_path;
auto original_data_path = DataPaths::get_instance().original_data_path;
@@ -2,15 +2,14 @@ package com.geode.launcher
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.os.FileUtils
import android.util.Log
import android.view.View
import android.view.ViewGroup
@@ -118,22 +117,33 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
@SuppressLint("UnsafeDynamicallyLoadedCode")
private fun tryLoadLibrary(packageInfo: PackageInfo, libraryName: String) {
try {
val nativeDir = packageInfo.applicationInfo.nativeLibraryDir
System.load("$nativeDir/lib$libraryName.so")
} catch (ule: UnsatisfiedLinkError) {
loadLibraryFromAssets(libraryName)
}
val nativeDir = getNativeLibraryDirectory(packageInfo.applicationInfo)
System.load("$nativeDir/lib$libraryName.so")
}
private fun loadLibraryFromAssets(libraryName: String) {
// for split apks, loads a library stored inside of the apk
// maybe todo: is there really no way to load these libraries without weird workarounds?
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
loadLibraryFromAssetsNative(libraryName)
} else {
loadLibraryFromAssetsCopy(libraryName)
private fun getNativeLibraryDirectory(applicationInfo: ApplicationInfo): String {
// native libraries have been extracted, so the path is same as usual
if (applicationInfo.flags and ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS == ApplicationInfo.FLAG_EXTRACT_NATIVE_LIBS) {
return applicationInfo.nativeLibraryDir
}
// might be split apks, select the best path for the library
val sourceDirs = applicationInfo.splitSourceDirs
val architecture = LaunchUtils.getApplicationArchitecture()
if (!sourceDirs.isNullOrEmpty()) {
val configAbi = architecture.replace("-", "_")
val abiDir = sourceDirs.find { it.contains(configAbi) }
if (abiDir != null) {
return "$abiDir!/lib/$architecture/"
}
}
// this configuration should never happen!
// non split apk, but libraries are stored in apk
val sourceDir = applicationInfo.sourceDir
return "$sourceDir!/lib/$architecture/"
}
@SuppressLint("UnsafeDynamicallyLoadedCode")
@@ -159,30 +169,6 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
return
}
private fun loadLibraryFromAssetsNative(libraryName: String) {
// loads a library loaded in assets (which points to the apk + an offset)
// these libraries are available to the application after merging
val arch = LaunchUtils.getApplicationArchitecture()
val libraryFd = try {
assets.openNonAssetFd("lib/$arch/lib$libraryName.so")
} catch (_: Exception) {
throw UnsatisfiedLinkError("Could not find library lib$libraryName.so for abi $arch")
}
val fdOffset = libraryFd.startOffset
val fdDescriptor = libraryFd.parcelFileDescriptor.detachFd()
if (!LauncherFix.loadLibraryFromOffset("lib$libraryName.so", fdDescriptor, fdOffset)) {
throw UnsatisfiedLinkError("Failed to load asset lib$libraryName.so")
}
// after the library is opened in native code, it should be available for loading
// you can read more about the behavior (added in android 11):
// https://android.googlesource.com/platform/libcore/+/7f3eb2e0ac87bdea471bc577380cf50025aebde5/ojluni/src/main/java/java/lang/Runtime.java#1060
System.loadLibrary(libraryName)
}
@SuppressLint("UnsafeDynamicallyLoadedCode")
private fun loadGeodeLibrary(): Boolean {
// Load Geode if exists
@@ -10,6 +10,4 @@ object LauncherFix {
external fun setDataPath(dataPath: String)
external fun setOriginalDataPath(dataPath: String)
external fun loadLibraryFromOffset(libraryName: String, fd: Int, offset: Long): Boolean
}