mirror of
https://github.com/geode-sdk/android-launcher.git
synced 2026-10-24 16:59:22 +00:00
get rid of dobby
looks like chaos has been waiting for us
This commit is contained in:
5 files changed
+221
-198
No files matched your search
@@ -29,7 +29,7 @@ android {
|
|||||||
@Suppress("UnstableApiUsage")
|
@Suppress("UnstableApiUsage")
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
arguments("-DUSE_TULIPHOOK:BOOL=OFF", "-DANDROID_STL=c++_shared")
|
arguments("-DANDROID_STL=c++_shared")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,41 +7,10 @@ add_library(launcherfix SHARED launcher-fix.cpp)
|
|||||||
target_compile_features(launcherfix PUBLIC cxx_std_20)
|
target_compile_features(launcherfix PUBLIC cxx_std_20)
|
||||||
set_target_properties(launcherfix PROPERTIES CXX_EXTENSIONS OFF)
|
set_target_properties(launcherfix PROPERTIES CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
include(FetchContent)
|
set_target_properties(launcherfix PROPERTIES
|
||||||
|
INTERPROCEDURAL_OPTIMIZATION ON
|
||||||
option(USE_TULIPHOOK "enables choice of hooking library" OFF)
|
C_VISIBILITY_PRESET hidden
|
||||||
if (USE_TULIPHOOK)
|
CXX_VISIBILITY_PRESET hidden
|
||||||
FetchContent_Declare(TulipHook
|
)
|
||||||
GIT_REPOSITORY https://github.com/geode-sdk/TulipHook.git
|
|
||||||
GIT_TAG v3.1.4
|
|
||||||
)
|
|
||||||
|
|
||||||
set(TULIP_LINK_SOURCE ON CACHE INTERNAL "")
|
|
||||||
FetchContent_MakeAvailable(TulipHook)
|
|
||||||
|
|
||||||
target_compile_definitions(launcherfix PRIVATE -DUSE_TULIPHOOK=1)
|
|
||||||
|
|
||||||
target_link_libraries(launcherfix PRIVATE TulipHook TulipHookInclude)
|
|
||||||
else()
|
|
||||||
FetchContent_Declare(dobby
|
|
||||||
# this is the last version that builds on android at all
|
|
||||||
GIT_REPOSITORY https://github.com/jmpews/Dobby
|
|
||||||
GIT_TAG 0932d69c320e786672361ab53825ba8f4245e9d3
|
|
||||||
)
|
|
||||||
|
|
||||||
FetchContent_GetProperties(dobby)
|
|
||||||
if(NOT dobby_POPULATED)
|
|
||||||
FetchContent_Populate(dobby)
|
|
||||||
|
|
||||||
# fixes Dobby build
|
|
||||||
# add_compile_definitions(typeof=__typeof__)
|
|
||||||
set(DOBBY_DEBUG OFF CACHE INTERNAL "" FORCE)
|
|
||||||
set(DOBBY_GENERATE_SHARED OFF CACHE INTERNAL "" FORCE)
|
|
||||||
|
|
||||||
add_subdirectory("${dobby_SOURCE_DIR}" ${dobby_BINARY_DIR} EXCLUDE_FROM_ALL)
|
|
||||||
target_include_directories(launcherfix PRIVATE ${dobby_SOURCE_DIR}/include)
|
|
||||||
target_link_libraries(launcherfix PRIVATE dobby)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_link_libraries(launcherfix PRIVATE log)
|
target_link_libraries(launcherfix PRIVATE log)
|
||||||
+209
-151
@@ -6,16 +6,11 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#include <link.h>
|
#include <link.h>
|
||||||
|
#include <span>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
|
||||||
#ifndef DISABLE_LAUNCHER_FIX
|
#define LOG_TAG "GeodeLauncher-Fix"
|
||||||
|
|
||||||
#ifdef USE_TULIPHOOK
|
|
||||||
#include <tulip/TulipHook.hpp>
|
|
||||||
#else
|
|
||||||
#include <dobby.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
class DataPaths {
|
class DataPaths {
|
||||||
public:
|
public:
|
||||||
@@ -23,6 +18,8 @@ public:
|
|||||||
std::string data_path{};
|
std::string data_path{};
|
||||||
std::string load_symbols_from{};
|
std::string load_symbols_from{};
|
||||||
|
|
||||||
|
bool patch_exceptions{};
|
||||||
|
|
||||||
static DataPaths& get_instance() {
|
static DataPaths& get_instance() {
|
||||||
static auto paths_instance = DataPaths();
|
static auto paths_instance = DataPaths();
|
||||||
return paths_instance;
|
return paths_instance;
|
||||||
@@ -32,6 +29,22 @@ private:
|
|||||||
DataPaths() {}
|
DataPaths() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ElfDynamicState {
|
||||||
|
std::uint32_t* hash_table{};
|
||||||
|
char* str_table{};
|
||||||
|
ElfW(Sym)* sym_table{};
|
||||||
|
|
||||||
|
void* plt_table{};
|
||||||
|
std::uint64_t plt_table_size_bytes{};
|
||||||
|
bool plt_table_rela{};
|
||||||
|
|
||||||
|
std::uint64_t soname_idx{};
|
||||||
|
|
||||||
|
bool is_empty() const {
|
||||||
|
return !hash_table || !str_table || !sym_table || !plt_table;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
extern "C"
|
extern "C"
|
||||||
JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_setDataPath(
|
JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_setDataPath(
|
||||||
JNIEnv *env,
|
JNIEnv *env,
|
||||||
@@ -74,9 +87,30 @@ JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_setOriginalDataPath(
|
|||||||
env->ReleaseStringUTFChars(data_path, data_path_str);
|
env->ReleaseStringUTFChars(data_path, data_path_str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_enableExceptionsRenaming(JNIEnv*, jobject) {
|
||||||
|
DataPaths::get_instance().patch_exceptions = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// copied from tuliphook source :)
|
||||||
|
bool set_mprotect(std::uintptr_t address, size_t size, uint32_t protection) {
|
||||||
|
auto const page_size = getpagesize();
|
||||||
|
auto const page_mask = page_size - 1;
|
||||||
|
|
||||||
|
auto const ptr = reinterpret_cast<uintptr_t>(address);
|
||||||
|
auto const aligned_ptr = ptr & (~page_mask);
|
||||||
|
auto const begin_size = ptr - aligned_ptr;
|
||||||
|
auto const page_count = (begin_size + size + page_mask) / page_size;
|
||||||
|
auto const aligned_size = page_count * page_size;
|
||||||
|
|
||||||
|
auto status = mprotect(reinterpret_cast<void*>(aligned_ptr), aligned_size, protection);
|
||||||
|
|
||||||
|
return status == 0;
|
||||||
|
}
|
||||||
|
|
||||||
// i copied this function from the android linker
|
// i copied this function from the android linker
|
||||||
std::uint32_t elfhash(const char *_name) {
|
std::uint32_t elfhash(const char *_name) {
|
||||||
const unsigned char *name = reinterpret_cast<const unsigned char*>(_name);
|
const auto *name = reinterpret_cast<const unsigned char*>(_name);
|
||||||
std::uint32_t h = 0, g;
|
std::uint32_t h = 0, g;
|
||||||
|
|
||||||
while (*name) {
|
while (*name) {
|
||||||
@@ -108,82 +142,162 @@ bool patch_symbol(std::uint32_t* hash_table, char* str_table, ElfW(Sym)* sym_tab
|
|||||||
if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL && ELF_ST_TYPE(sym->st_info) == STT_FUNC) {
|
if (ELF_ST_BIND(sym->st_info) == STB_GLOBAL && ELF_ST_TYPE(sym->st_info) == STT_FUNC) {
|
||||||
// we found it! now go rename the symbol
|
// we found it! now go rename the symbol
|
||||||
|
|
||||||
#ifdef USE_TULIPHOOK
|
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Disabling symbol at %p (%s)", sym_name, sym_name);
|
||||||
auto res = tulip::hook::writeMemory(sym_name, SYMBOL_PATCH_VALUE.data(), SYMBOL_PATCH_VALUE.size());
|
|
||||||
if (!res) {
|
if (!set_mprotect(reinterpret_cast<std::uintptr_t>(sym_name), SYMBOL_PATCH_VALUE.size(), PROT_READ | PROT_WRITE)) {
|
||||||
__android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-fix", "failed to patch symbol: %s", res.unwrapErr().c_str());
|
__android_log_print(ANDROID_LOG_WARN, LOG_TAG, "failed to patch symbol: could not set writable flag");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(sym_name, SYMBOL_PATCH_VALUE.data(), SYMBOL_PATCH_VALUE.size());
|
||||||
|
|
||||||
|
if (!set_mprotect(reinterpret_cast<std::uintptr_t>(sym_name), SYMBOL_PATCH_VALUE.size(), PROT_READ)) {
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, LOG_TAG, "failed to patch symbol: could not remove writable flag");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
DobbyCodePatch(reinterpret_cast<void*>(sym_name), SYMBOL_PATCH_VALUE.data(), SYMBOL_PATCH_VALUE.size());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-fix", "could not find symbol %s to patch (hash: %u)", orig_name, hash);
|
__android_log_print(ANDROID_LOG_WARN, LOG_TAG, "could not find symbol %s to patch (hash: %u)", orig_name, hash);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> get_symbols_listing() {
|
// this is every function that i thought would be relevant
|
||||||
auto symbol_path = DataPaths::get_instance().load_symbols_from;
|
constexpr std::array remove_symbols{
|
||||||
if (symbol_path.empty()) {
|
"__gxx_personality_v0",
|
||||||
// this is every function that i thought would be relevant
|
"__cxa_throw",
|
||||||
return {
|
"__cxa_rethrow",
|
||||||
"__gxx_personality_v0",
|
"__cxa_allocate_exception",
|
||||||
"__cxa_throw",
|
"__cxa_end_catch",
|
||||||
"__cxa_rethrow",
|
"__cxa_begin_catch",
|
||||||
"__cxa_allocate_exception",
|
"__cxa_guard_abort",
|
||||||
"__cxa_end_catch",
|
"__cxa_guard_acquire",
|
||||||
"__cxa_begin_catch",
|
"__cxa_guard_release",
|
||||||
"__cxa_guard_abort",
|
"__cxa_free_exception"
|
||||||
"__cxa_guard_acquire",
|
|
||||||
"__cxa_guard_release",
|
|
||||||
"__cxa_free_exception"
|
|
||||||
|
|
||||||
// android 10's libc doesn't have these symbols
|
// android 10's libc doesn't have these symbols
|
||||||
// and the ndk is forbidden from exporting them!! so keep it for old mods...
|
// and the ndk is forbidden from exporting them!! so keep it for old mods...
|
||||||
// "_Unwind_RaiseException",
|
// "_Unwind_RaiseException",
|
||||||
// "_Unwind_Resume"
|
// "_Unwind_Resume"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool perform_symbol_patches(dl_phdr_info* info, const ElfDynamicState& state) {
|
||||||
|
// patch symbol names
|
||||||
|
auto so_name = state.str_table + state.soname_idx;
|
||||||
|
if (strcmp(so_name, "libcocos2dcpp.so") != 0) {
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, LOG_TAG, "DT_SONAME gives us %s! that's not good", so_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ifstream symbol_file{symbol_path};
|
auto has_error = false;
|
||||||
if (!symbol_file) {
|
for (const auto& symbol : remove_symbols) {
|
||||||
__android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-fix", "failed to read symbol file at %s", symbol_path.c_str());
|
if (!patch_symbol(state.hash_table, state.str_table, state.sym_table, symbol)) {
|
||||||
return {};
|
has_error = true;
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string> symbols_list{};
|
|
||||||
std::string current_line{};
|
|
||||||
while (std::getline(symbol_file, current_line)) {
|
|
||||||
if (!current_line.empty()) {
|
|
||||||
symbols_list.push_back(current_line);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return symbols_list;
|
return !has_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__LP64__)
|
||||||
|
#define ELF_R_SYM(i) ELF64_R_SYM(i)
|
||||||
|
#define ELF_R_TYPE(i) ELF64_R_TYPE(i)
|
||||||
|
#else
|
||||||
|
#define ELF_R_SYM(i) ELF32_R_SYM(i)
|
||||||
|
#define ELF_R_TYPE(i) ELF32_R_TYPE(i)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename T, typename... Types>
|
||||||
|
concept is_one_of = (std::same_as<T, Types> || ...);
|
||||||
|
|
||||||
|
template <typename T> requires is_one_of<T, ElfW(Rela), ElfW(Rel)>
|
||||||
|
bool rewrite_symbol_reloc(std::uintptr_t addr, T& r, ElfW(Sym)* sym, void* func) {
|
||||||
|
auto type = ELF_R_TYPE(r.r_info);
|
||||||
|
|
||||||
|
// we're only interested in JUMP_SLOT symbols
|
||||||
|
if (type != R_AARCH64_JUMP_SLOT && type != R_ARM_JUMP_SLOT) {
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, LOG_TAG, "asked to patch unknown symbol type: %llu", type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto write_addr = reinterpret_cast<std::uintptr_t*>(addr);
|
||||||
|
auto val = reinterpret_cast<std::uintptr_t>(func);
|
||||||
|
if constexpr (std::is_same_v<T, ElfW(Rela)>) {
|
||||||
|
// reloc = S + A
|
||||||
|
val += r.r_addend;
|
||||||
|
}
|
||||||
|
|
||||||
|
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Patching relocation at vaddr %#lx (offset=%#llx): %#lx => %#lx", addr, r.r_offset, *write_addr, val);
|
||||||
|
|
||||||
|
if (!set_mprotect(addr, sizeof(std::uintptr_t), PROT_WRITE | PROT_READ)) {
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Failed to patch relocation: failed to add writable flag");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// why use memcpy when you can do this :)
|
||||||
|
*write_addr = val;
|
||||||
|
|
||||||
|
if (!set_mprotect(addr, sizeof(std::uintptr_t), PROT_READ)) {
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, LOG_TAG, "Failed to patch relocation: failed to remove writable flag");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* fopen_hook(const char* pathname, const char* mode);
|
||||||
|
int rename_hook(const char* old_path, const char* new_path);
|
||||||
|
|
||||||
|
template <typename T> requires is_one_of<T, ElfW(Rela), ElfW(Rel)>
|
||||||
|
bool perform_reloc_patches(dl_phdr_info* info, const ElfDynamicState& state) {
|
||||||
|
auto table_size = state.plt_table_size_bytes / sizeof(T);
|
||||||
|
|
||||||
|
auto failed = false;
|
||||||
|
|
||||||
|
for (auto& plt : std::span{
|
||||||
|
reinterpret_cast<T*>(state.plt_table), table_size
|
||||||
|
}) {
|
||||||
|
auto sym = ELF_R_SYM(plt.r_info);
|
||||||
|
if (sym == 0) continue;
|
||||||
|
|
||||||
|
auto sym_data = state.sym_table + sym;
|
||||||
|
auto sym_name = state.str_table + sym_data->st_name;
|
||||||
|
|
||||||
|
auto reloc_addr = info->dlpi_addr + plt.r_offset;
|
||||||
|
|
||||||
|
if (strcmp(sym_name, "fopen") == 0) {
|
||||||
|
if (!rewrite_symbol_reloc(reloc_addr, plt, sym_data, reinterpret_cast<void*>(&fopen_hook))) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
} else if (strcmp(sym_name, "rename") == 0) {
|
||||||
|
if (!rewrite_symbol_reloc(reloc_addr, plt, sym_data, reinterpret_cast<void*>(&rename_hook))) {
|
||||||
|
failed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !failed;
|
||||||
}
|
}
|
||||||
|
|
||||||
int on_dl_iterate(dl_phdr_info* info, size_t size, void* data) {
|
int on_dl_iterate(dl_phdr_info* info, size_t size, void* data) {
|
||||||
// this is probably going to be gd
|
// this is probably going to be gd
|
||||||
if (info->dlpi_name != nullptr && strstr(info->dlpi_name, "libcocos2dcpp.so") != nullptr) {
|
if (info->dlpi_name != nullptr && strstr(info->dlpi_name, "libcocos2dcpp.so") != nullptr) {
|
||||||
|
__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Base address of libcocos2dcpp: %#llx", info->dlpi_addr);
|
||||||
|
|
||||||
// step 1: get the dynamic table
|
// step 1: get the dynamic table
|
||||||
std::uintptr_t dyn_addr = 0u;
|
std::uintptr_t dyn_addr = 0u;
|
||||||
|
|
||||||
for (auto i = 0u; i < info->dlpi_phnum; i++) {
|
for (auto& phdr : std::span{ info->dlpi_phdr, info->dlpi_phnum}) {
|
||||||
auto phdr = info->dlpi_phdr + i;
|
if (phdr.p_type == PT_DYNAMIC) {
|
||||||
|
dyn_addr = info->dlpi_addr + phdr.p_vaddr;
|
||||||
if (phdr->p_type == PT_DYNAMIC) {
|
|
||||||
dyn_addr = info->dlpi_addr + phdr->p_vaddr;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dyn_addr == 0u) {
|
if (dyn_addr == 0u) {
|
||||||
__android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-fix", "failed to find libcocos dynamic section");
|
__android_log_print(ANDROID_LOG_WARN, LOG_TAG, "failed to find libcocos dynamic section");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,26 +305,40 @@ int on_dl_iterate(dl_phdr_info* info, size_t size, void* data) {
|
|||||||
auto dyn_entry = reinterpret_cast<ElfW(Dyn)*>(dyn_addr);
|
auto dyn_entry = reinterpret_cast<ElfW(Dyn)*>(dyn_addr);
|
||||||
auto dyn_end_reached = false;
|
auto dyn_end_reached = false;
|
||||||
|
|
||||||
std::uintptr_t sym_table_addr = 0u;
|
ElfDynamicState state{};
|
||||||
std::uintptr_t str_table_addr = 0u;
|
|
||||||
std::uintptr_t hash_table_addr = 0u;
|
|
||||||
std::uintptr_t soname_idx = 0u;
|
|
||||||
|
|
||||||
while (!dyn_end_reached) {
|
while (!dyn_end_reached) {
|
||||||
auto tag = dyn_entry->d_tag;
|
auto tag = dyn_entry->d_tag;
|
||||||
|
|
||||||
switch (dyn_entry->d_tag) {
|
switch (dyn_entry->d_tag) {
|
||||||
case DT_SYMTAB:
|
case DT_SYMTAB:
|
||||||
sym_table_addr = info->dlpi_addr + dyn_entry->d_un.d_val;
|
state.sym_table = reinterpret_cast<ElfW(Sym)*>(info->dlpi_addr + dyn_entry->d_un.d_val);
|
||||||
break;
|
break;
|
||||||
case DT_STRTAB:
|
case DT_STRTAB:
|
||||||
str_table_addr = info->dlpi_addr + dyn_entry->d_un.d_val;
|
state.str_table = reinterpret_cast<char*>(info->dlpi_addr + dyn_entry->d_un.d_val);
|
||||||
break;
|
break;
|
||||||
case DT_HASH:
|
case DT_HASH:
|
||||||
hash_table_addr = info->dlpi_addr + dyn_entry->d_un.d_val;
|
state.hash_table = reinterpret_cast<std::uint32_t*>(info->dlpi_addr + dyn_entry->d_un.d_val);
|
||||||
break;
|
break;
|
||||||
case DT_SONAME:
|
case DT_SONAME:
|
||||||
soname_idx = dyn_entry->d_un.d_val;
|
state.soname_idx = dyn_entry->d_un.d_val;
|
||||||
|
break;
|
||||||
|
case DT_JMPREL:
|
||||||
|
state.plt_table = reinterpret_cast<void*>(info->dlpi_addr + dyn_entry->d_un.d_val);
|
||||||
|
break;
|
||||||
|
case DT_PLTREL: {
|
||||||
|
// for Android, arm64 = DT_RELA, arm32 = DT_REL
|
||||||
|
// could just gate this based on architecture but whatever
|
||||||
|
|
||||||
|
auto val = dyn_entry->d_un.d_val;
|
||||||
|
state.plt_table_rela = val == DT_RELA;
|
||||||
|
if (val != DT_RELA && val != DT_REL) {
|
||||||
|
__android_log_print(ANDROID_LOG_WARN, LOG_TAG, "unrecognized PLTREL type %llx", val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case DT_PLTRELSZ:
|
||||||
|
state.plt_table_size_bytes = dyn_entry->d_un.d_val;
|
||||||
break;
|
break;
|
||||||
case DT_NULL:
|
case DT_NULL:
|
||||||
dyn_end_reached = true;
|
dyn_end_reached = true;
|
||||||
@@ -220,48 +348,38 @@ int on_dl_iterate(dl_phdr_info* info, size_t size, void* data) {
|
|||||||
dyn_entry++;
|
dyn_entry++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hash_table_addr == 0u || str_table_addr == 0u || sym_table_addr == 0u) {
|
if (state.is_empty()) {
|
||||||
__android_log_print(ANDROID_LOG_ERROR, "GeodeLauncher-fix", "failed to parse dynamic section (at least one table is null)");
|
__android_log_print(ANDROID_LOG_ERROR, LOG_TAG, "failed to parse dynamic section (at least one table is null)");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// patch symbol names
|
bool failed = state.plt_table_rela
|
||||||
auto hash_table = reinterpret_cast<std::uint32_t*>(hash_table_addr);
|
? !perform_reloc_patches<ElfW(Rela)>(info, state)
|
||||||
auto str_table = reinterpret_cast<char*>(str_table_addr);
|
: !perform_reloc_patches<ElfW(Rel)>(info, state);
|
||||||
auto sym_table = reinterpret_cast<ElfW(Sym)*>(sym_table_addr);
|
|
||||||
|
|
||||||
auto so_name = str_table + soname_idx;
|
if (DataPaths::get_instance().patch_exceptions) {
|
||||||
if (strcmp(so_name, "libcocos2dcpp.so") != 0) {
|
if (!perform_symbol_patches(info, state)) {
|
||||||
__android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-fix", "DT_SONAME gives us %s! that's not good", so_name);
|
failed = true;
|
||||||
}
|
|
||||||
|
|
||||||
auto has_error = false;
|
|
||||||
auto symbols_listing = get_symbols_listing();
|
|
||||||
for (const auto& symbol : symbols_listing) {
|
|
||||||
if (!patch_symbol(hash_table, str_table, sym_table, symbol.c_str())) {
|
|
||||||
has_error = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_error) {
|
if (failed) {
|
||||||
__android_log_print(
|
__android_log_print(
|
||||||
ANDROID_LOG_INFO,
|
ANDROID_LOG_INFO,
|
||||||
"GeodeLauncher-fix",
|
LOG_TAG,
|
||||||
"symbol patch diagnostics\nlibrary path: %s\naddrs: base=%p sym=%p str=%p hash=%p",
|
"symbol patch diagnostics\nlibrary path: %s\naddrs: base=%p sym=%p str=%p hash=%p plt=%p",
|
||||||
info->dlpi_name, reinterpret_cast<void*>(info->dlpi_addr), sym_table, str_table, hash_table
|
info->dlpi_name, reinterpret_cast<void*>(info->dlpi_addr), state.sym_table, state.str_table, state.hash_table, state.plt_table
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// this should be called after gd is loaded but before geode
|
// this should be called after gd is loaded but before geode
|
||||||
extern "C"
|
extern "C" JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_performPatches(JNIEnv*, jobject) {
|
||||||
JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_performExceptionsRenaming(JNIEnv*, jobject) {
|
|
||||||
dl_iterate_phdr(on_dl_iterate, nullptr);
|
dl_iterate_phdr(on_dl_iterate, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -281,15 +399,13 @@ std::optional<std::string> redirect_path(const char* pathname) {
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
FILE* (*fopen_original)(const char *pathname, const char *mode);
|
|
||||||
FILE* fopen_hook(const char* pathname, const char* mode) {
|
FILE* fopen_hook(const char* pathname, const char* mode) {
|
||||||
auto path_str = redirect_path(pathname);
|
auto path_str = redirect_path(pathname);
|
||||||
auto redirect = path_str.has_value() ? path_str->c_str() : pathname;
|
auto redirect = path_str.has_value() ? path_str->c_str() : pathname;
|
||||||
|
|
||||||
return fopen_original(redirect, mode);
|
return fopen(redirect, mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
int (*rename_original)(const char* old_path, const char* new_path);
|
|
||||||
int rename_hook(const char* old_path, const char* new_path) {
|
int rename_hook(const char* old_path, const char* new_path) {
|
||||||
auto old_path_str = redirect_path(old_path);
|
auto old_path_str = redirect_path(old_path);
|
||||||
auto old_redirect = old_path_str ? old_path_str->c_str() : old_path;
|
auto old_redirect = old_path_str ? old_path_str->c_str() : old_path;
|
||||||
@@ -297,63 +413,5 @@ int rename_hook(const char* old_path, const char* new_path) {
|
|||||||
auto new_path_str = redirect_path(new_path);
|
auto new_path_str = redirect_path(new_path);
|
||||||
auto new_redirect = new_path_str ? new_path_str->c_str() : new_path;
|
auto new_redirect = new_path_str ? new_path_str->c_str() : new_path;
|
||||||
|
|
||||||
return rename_original(old_redirect, new_redirect);
|
return rename(old_redirect, new_redirect);
|
||||||
}
|
|
||||||
|
|
||||||
bool hook_function(void* addr, auto* hook, auto** orig) {
|
|
||||||
#ifdef USE_TULIPHOOK
|
|
||||||
using namespace tulip::hook;
|
|
||||||
|
|
||||||
HandlerMetadata metadata = {
|
|
||||||
std::make_shared<PlatformConvention>(),
|
|
||||||
AbstractFunction::from(hook)
|
|
||||||
};
|
|
||||||
|
|
||||||
auto handler_result = createHandler(addr, metadata);
|
|
||||||
if (!handler_result) {
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-Fix",
|
|
||||||
"failed to create handler: %s\n", handler_result.unwrapErr().c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto handler = *handler_result;
|
|
||||||
|
|
||||||
HookMetadata hook_metadata = {0};
|
|
||||||
|
|
||||||
createHook(handler, reinterpret_cast<void*>(hook), hook_metadata);
|
|
||||||
|
|
||||||
WrapperMetadata wrapper_metadata = {
|
|
||||||
std::make_unique<PlatformConvention>(),
|
|
||||||
AbstractFunction::from(hook)
|
|
||||||
};
|
|
||||||
|
|
||||||
auto wrapper = createWrapper(addr, std::move(wrapper_metadata));
|
|
||||||
if (!wrapper) {
|
|
||||||
__android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-Fix",
|
|
||||||
"failed to create wrapper: %s\n", wrapper.unwrapErr().c_str());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
*reinterpret_cast<void**>(orig) = *wrapper;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
DobbyHook(
|
|
||||||
addr,
|
|
||||||
reinterpret_cast<dobby_dummy_func_t>(hook),
|
|
||||||
reinterpret_cast<dobby_dummy_func_t*>(orig)
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
[[gnu::constructor]] [[gnu::used]] void setup_hooks() {
|
|
||||||
#ifndef DISABLE_LAUNCHER_FIX
|
|
||||||
auto fopen_addr = dlsym(RTLD_NEXT, "fopen");
|
|
||||||
hook_function(fopen_addr, &fopen_hook, &fopen_original);
|
|
||||||
|
|
||||||
auto rename_addr = dlsym(RTLD_NEXT, "rename");
|
|
||||||
hook_function(rename_addr, &rename_hook, &rename_original);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
@@ -172,19 +172,13 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
|||||||
tryLoadLibrary(gdPackageInfo, Constants.COCOS_LIB_NAME)
|
tryLoadLibrary(gdPackageInfo, Constants.COCOS_LIB_NAME)
|
||||||
|
|
||||||
if (GamePackageUtils.getGameVersionCode(packageManager) >= 39L) {
|
if (GamePackageUtils.getGameVersionCode(packageManager) >= 39L) {
|
||||||
/*
|
|
||||||
val customSymbols = PreferenceUtils.get(this).getBoolean(PreferenceUtils.Key.CUSTOM_SYMBOL_LIST)
|
|
||||||
if (customSymbols) {
|
|
||||||
val symbolFile = File(LaunchUtils.getBaseDirectory(this), "exception_symbols.txt")
|
|
||||||
LauncherFix.enableCustomSymbolList(symbolFile.path)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// this fix requires geode v3, which is 2.206+
|
// this fix requires geode v3, which is 2.206+
|
||||||
// there is a short period in which 2.206 users will still have geode v2, but whatever. ig
|
// there is a short period in which 2.206 users will still have geode v2, but whatever. ig
|
||||||
LauncherFix.performExceptionsRenaming()
|
LauncherFix.enableExceptionsRenaming()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LauncherFix.performPatches()
|
||||||
|
|
||||||
loadInternalMods()
|
loadInternalMods()
|
||||||
|
|
||||||
val baseView = createView()
|
val baseView = createView()
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ object LauncherFix {
|
|||||||
|
|
||||||
external fun setOriginalDataPath(dataPath: String)
|
external fun setOriginalDataPath(dataPath: String)
|
||||||
|
|
||||||
external fun performExceptionsRenaming()
|
external fun enableExceptionsRenaming()
|
||||||
|
|
||||||
|
external fun performPatches()
|
||||||
|
|
||||||
external fun enableCustomSymbolList(symbolsPath: String)
|
external fun enableCustomSymbolList(symbolsPath: String)
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user