diff --git a/CMakeLists.txt b/CMakeLists.txt index 89d581d..5955247 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,11 +4,8 @@ if(NOT ${CMAKE_ANDROID_ARCH_ABI} STREQUAL arm64-v8a) message(FATAL_ERROR "Unsupported target architecture: ${CMAKE_ANDROID_ARCH_ABI}. Please make an issue on the repo!") endif() -project(adrenotools LANGUAGES CXX) +project(adrenotools LANGUAGES CXX C) -set(CMAKE_CXX_STANDARD 17) - -include_directories(lib/linkernsbypass) add_subdirectory(lib/linkernsbypass) set(LIB_SOURCES src/bcenabler.cpp diff --git a/lib/linkernsbypass b/lib/linkernsbypass index 8a819a3..341f6e2 160000 --- a/lib/linkernsbypass +++ b/lib/linkernsbypass @@ -1 +1 @@ -Subproject commit 8a819a35e1585b8adfd95174299901362b57b154 +Subproject commit 341f6e2d585c9501ad044338da34797c060aacf3 diff --git a/src/driver.cpp b/src/driver.cpp index 00f2163..a59b515 100644 --- a/src/driver.cpp +++ b/src/driver.cpp @@ -8,11 +8,11 @@ #include #include #include -#include "hook/main_hook.h" +#include "hook/hook_impl_params.h" #include -void *adrenotools_open_libvulkan(int dlopenMode, int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir) { - // Bail out if linkernsbyapss failed to load, this probably means we're on api < 28 +void *adrenotools_open_libvulkan(int dlopenFlags, int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir) { + // Bail out if linkernsbypass failed to load, this probably means we're on api < 28 if (!linkernsbypass_load_status()) return nullptr; @@ -47,8 +47,28 @@ void *adrenotools_open_libvulkan(int dlopenMode, int featureFlags, const char *t return nullptr; } - // Will be destroyed by the libmain_hook.so destructor on unload - auto *hookParam{new MainHookParam(featureFlags, tmpLibDir, hookLibDir, customDriverDir, customDriverName, fileRedirectDir)}; + // Create a namespace that can isolate our hook from the classloader namespace + auto hookNs{android_create_namespace("adrenotools-libvulkan", hookLibDir, nullptr, ANDROID_NAMESPACE_TYPE_SHARED, nullptr, nullptr)}; - return linkernsbypass_dlopen_unique_hooked("/system/lib64/libvulkan.so", tmpLibDir, dlopenMode, hookLibDir, "libmain_hook.so", nullptr, true, reinterpret_cast(hookParam)); + // Link it to the default namespace so the hook can use libandroid etc + if (!linkernsbypass_link_namespace_to_default_all_libs(hookNs)) + return nullptr; + + // Preload the hook implementation, otherwise we get a weird issue where despite being in NEEDED of the hook lib the hook's symbols will overwrite ours and cause an infinite loop + auto hookImpl{linkernsbypass_namespace_dlopen("libhook_impl.so", RTLD_NOW, hookNs)}; + if (!hookImpl) + return nullptr; + + // Pass parameters to the hook implementation + auto initHookParam{reinterpret_cast(dlsym(hookImpl, "init_hook_param"))}; + if (!initHookParam) + return nullptr; + + initHookParam(new HookImplParams(featureFlags, tmpLibDir, hookLibDir, customDriverDir, customDriverName, fileRedirectDir)); + + // Load the libvulkan hook into the isolated namespace + if (!linkernsbypass_namespace_dlopen("libmain_hook.so", RTLD_GLOBAL, hookNs)) + return nullptr; + + return linkernsbypass_namespace_dlopen_unique("/system/lib64/libvulkan.so", tmpLibDir, dlopenFlags, hookNs); } diff --git a/src/hook/CMakeLists.txt b/src/hook/CMakeLists.txt index c21b6ed..c0f95db 100644 --- a/src/hook/CMakeLists.txt +++ b/src/hook/CMakeLists.txt @@ -1,15 +1,20 @@ -add_library(main_hook SHARED main_hook.cpp) +add_library(hook_impl SHARED hook_impl.cpp hook_impl.h hook_impl_params.h) + +target_compile_options(hook_impl PRIVATE -Wall -Wextra) +target_link_libraries(hook_impl linkernsbypass log) +target_include_directories(hook_impl PRIVATE ../../include) +set_target_properties(hook_impl PROPERTIES CXX_VISIBILITY_PRESET hidden) + +add_library(main_hook SHARED main_hook.c) target_compile_options(main_hook PRIVATE -Wall -Wextra) target_link_options(main_hook PRIVATE -z global) -target_link_libraries(main_hook linkernsbypass log) -target_include_directories(main_hook PRIVATE ../../include) +target_link_libraries(main_hook hook_impl) set_target_properties(main_hook PROPERTIES CXX_VISIBILITY_PRESET hidden) -add_library(file_redirect_hook SHARED file_redirect_hook.cpp) +add_library(file_redirect_hook SHARED file_redirect_hook.c) target_compile_options(file_redirect_hook PRIVATE -Wall -Wextra) target_link_options(file_redirect_hook PRIVATE -z global) -target_link_libraries(file_redirect_hook linkernsbypass dl log) -target_include_directories(file_redirect_hook PRIVATE ../../include) +target_link_libraries(file_redirect_hook hook_impl) set_target_properties(file_redirect_hook PROPERTIES CXX_VISIBILITY_PRESET hidden) diff --git a/src/hook/file_redirect_hook.c b/src/hook/file_redirect_hook.c new file mode 100644 index 0000000..aa3b6d2 --- /dev/null +++ b/src/hook/file_redirect_hook.c @@ -0,0 +1,5 @@ +#include "hook_impl.h" + +__attribute__((visibility("default"))) FILE *fopen(const char *filename, const char *mode) { + return hook_fopen(filename, mode); +} \ No newline at end of file diff --git a/src/hook/file_redirect_hook.cpp b/src/hook/file_redirect_hook.cpp index 8861a3a..8e84cf3 100644 --- a/src/hook/file_redirect_hook.cpp +++ b/src/hook/file_redirect_hook.cpp @@ -1,35 +1,9 @@ -#include -#include -#include -#include +#include "hook_impl.h" -#define TAG "file_redirect_hook" -#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##__VA_ARGS__) - -static decltype(&fopen) libc_fopen{[]() { - auto libcHandle{dlopen("libc.so", RTLD_LAZY)}; - if (!libcHandle) - __builtin_trap(); - - auto sym{dlsym(libcHandle, "fopen")}; - if (!sym) - __builtin_trap(); - - return reinterpret_cast(sym); -}()}; - -extern "C" { - __attribute__((visibility("default"))) const char *hook_param; //!< The prefix to add to filesystem calls - - __attribute__((visibility("default"))) FILE *fopen(const char *filename, const char *mode) { - if ((strncmp("/proc", filename, 5) == 0) || (strncmp("/sys", filename, 4) == 0)) { - LOGI("fopen: passthrough: %s", filename); - return libc_fopen(filename, mode); - } - - auto replacement{std::string{hook_param} + filename}; - LOGI("fopen: %s -> %s", filename, replacement.c_str()); - - return libc_fopen(replacement.c_str(), mode); - } +__attribute__((visibility("default"))) void init_hook_param(const void *param) { + init_file_redirect_hook_param(param); } + +__attribute__((visibility("default"))) FILE *fopen(const char *filename, const char *mode) { + return hook_fopen(filename, mode); +} \ No newline at end of file diff --git a/src/hook/hook_impl.cpp b/src/hook/hook_impl.cpp new file mode 100644 index 0000000..906803b --- /dev/null +++ b/src/hook/hook_impl.cpp @@ -0,0 +1,116 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "hook_impl_params.h" +#include "hook_impl.h" + +#define TAG "hook_impl" +#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##__VA_ARGS__) + +const HookImplParams *hook_params; //!< Bunch of info needed to load/patch the driver + +__attribute__((visibility("default"))) void init_hook_param(const void *param) { + hook_params = reinterpret_cast(param); + LOGI("SET %p", param); +} + +__attribute__((visibility("default"))) void *hook_android_dlopen_ext(const char *filename, int flags, const android_dlextinfo *extinfo) { + auto fallback{[&]() { + LOGI("hook_android_dlopen_ext: falling back!"); + return android_dlopen_ext(filename, flags, extinfo); + }}; + + LOGI("hook_android_dlopen_ext: filename: %s", filename); + + // Ignore non-vulkan libraries + if (!strstr(filename, "vulkan.")) + return android_dlopen_ext(filename, flags, extinfo); + + if (extinfo->library_namespace == nullptr || !(extinfo->flags & ANDROID_DLEXT_USE_NAMESPACE)) { + LOGI("hook_android_dlopen_ext: hook failed: namespace not supplied!"); + return fallback(); + } + + // customDriverDir will be empty if ADRENOTOOLS_DRIVER_CUSTOM isn't set therefore it's fine to have either way + auto driverNs{android_create_namespace(filename, hook_params->customDriverDir.c_str(), + hook_params->hookLibDir.c_str(), ANDROID_NAMESPACE_TYPE_SHARED, + nullptr, extinfo->library_namespace)}; + if (!driverNs) { + LOGI("hook_android_dlopen_ext: hook failed: namespace not supplied!"); + return fallback(); + } + + // We depend on libandroid which is unlikely to be in the supplied driver namespace so we have to link it over + android_link_namespaces(driverNs, nullptr, "libandroid.so"); + + // Preload ourself, a new instance will be created since we have different linker ancestory + // If we don't preload we get a weird issue where despite being in NEEDED of the hook lib the hook's symbols will overwrite ours and cause an infinite loop + auto hookImpl{linkernsbypass_namespace_dlopen("libhook_impl.so", RTLD_NOW, driverNs)}; + if (!hookImpl) + return nullptr; + + // Pass parameters to ourself + auto initHookParam{reinterpret_cast(dlsym(hookImpl, "init_hook_param"))}; + if (!initHookParam) + return nullptr; + + initHookParam(hook_params); + + if (hook_params->featureFlags & ADRENOTOOLS_DRIVER_FILE_REDIRECT) { + if (!linkernsbypass_namespace_dlopen("libfile_redirect_hook.so", RTLD_GLOBAL, driverNs)) { + LOGI("hook_android_dlopen_ext: hook failed: failed to apply libfopen_redirect_hook!"); + return fallback(); + } + + LOGI("hook_android_dlopen_ext: applied libfile_redirect_hook"); + } + + // Use our new namespace to load the vulkan driver + auto newExtinfo{*extinfo}; + newExtinfo.library_namespace = driverNs; + + // TODO: If there is already an instance of a vulkan driver loaded hooks won't be applied, this will only be the case for skiavk generally + // To fix this we would need to search /proc/self/maps for the file to a loaded instance of the library in order to read it to patch the soname and load it uniquely + if (hook_params->featureFlags & ADRENOTOOLS_DRIVER_CUSTOM) { + LOGI("hook_android_dlopen_ext: loading custom driver: %s%s", hook_params->customDriverDir.c_str(), hook_params->customDriverName.c_str()); + return android_dlopen_ext(hook_params->customDriverName.c_str(), flags, &newExtinfo); + } else { + LOGI("hook_android_dlopen_ext: loading default driver: %s", filename); + return android_dlopen_ext(filename, flags, &newExtinfo); + } +} + +__attribute__((visibility("default"))) void *hook_android_load_sphal_library(const char *filename, int flags) { + LOGI("hook_android_load_sphal_library: filename: %s", filename); + + // https://android.googlesource.com/platform/system/core/+/master/libvndksupport/linker.cpp + for (const char *name : {"sphal", "vendor", "default"}) { + if (auto vendorNs{android_get_exported_namespace(name)}) { + android_dlextinfo dlextinfo{ + .flags = ANDROID_DLEXT_USE_NAMESPACE, + .library_namespace = vendorNs, + }; + + return hook_android_dlopen_ext(filename, flags, &dlextinfo); + } + } + + return nullptr; +} + +__attribute__((visibility("default"))) FILE *hook_fopen(const char *filename, const char *mode) { + if (!strncmp("/proc", filename, 5) || !strncmp("/sys", filename, 4)) { + LOGI("hook_fopen: passthrough: %s", filename); + return fopen(filename, mode); + } + LOGI("LET %p", hook_params); + auto replacement{hook_params->fileRedirectDir + filename}; + LOGI("hook_fopen: %s -> %s", filename, replacement.c_str()); + + return fopen(replacement.c_str(), mode); +} diff --git a/src/hook/hook_impl.h b/src/hook/hook_impl.h new file mode 100644 index 0000000..fb1da03 --- /dev/null +++ b/src/hook/hook_impl.h @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: BSD-2-Clause +// Copyright © 2021 Billy Laws + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +void init_hook_param(const void *param); + +void *hook_android_dlopen_ext(const char *filename, int flags, const android_dlextinfo *extinfo); + +void *hook_android_load_sphal_library(const char *filename, int flags); + +FILE *hook_fopen(const char *filename, const char *mode); + + +#ifdef __cplusplus +} +#endif + diff --git a/src/hook/main_hook.h b/src/hook/hook_impl_params.h similarity index 74% rename from src/hook/main_hook.h rename to src/hook/hook_impl_params.h index e0d54f7..45cbba1 100644 --- a/src/hook/main_hook.h +++ b/src/hook/hook_impl_params.h @@ -7,10 +7,10 @@ #include /** - * @brief Holds the parameters for the main libvulkan.so hook + * @brief Holds the parameters needed for all hooks * @note See comments for adrenotools_open_libvulkan as a reference for member variables */ -struct MainHookParam { +struct HookImplParams { int featureFlags; std::string tmpLibDir; std::string hookLibDir; @@ -18,7 +18,8 @@ struct MainHookParam { std::string customDriverName; std::string fileRedirectDir; - MainHookParam(int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir) + HookImplParams(int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, + const char *customDriverName, const char *fileRedirectDir) : featureFlags(featureFlags), tmpLibDir(tmpLibDir ? tmpLibDir : ""), hookLibDir(hookLibDir), diff --git a/src/hook/main_hook.c b/src/hook/main_hook.c new file mode 100644 index 0000000..067537f --- /dev/null +++ b/src/hook/main_hook.c @@ -0,0 +1,9 @@ +#include "hook_impl.h" + +__attribute__((visibility("default"))) void *android_dlopen_ext(const char *filename, int flags, const android_dlextinfo *extinfo) { + return hook_android_dlopen_ext(filename, flags, extinfo); +} + +__attribute__((visibility("default"))) void *android_load_sphal_library(const char *filename, int flags) { + return hook_android_load_sphal_library(filename, flags); +} \ No newline at end of file diff --git a/src/hook/main_hook.cpp b/src/hook/main_hook.cpp deleted file mode 100644 index de58aaf..0000000 --- a/src/hook/main_hook.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include -#include -#include -#include -#include "main_hook.h" - -#define TAG "driver_load_hook" -#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##__VA_ARGS__) - -extern "C" { - // The namespace that should be used to load vulkan drivers, set dlopen_hooked from the hookParam argument - __attribute__((visibility("default"))) const MainHookParam *hook_param; - - __attribute__((destructor)) static void free_hook_param() { - delete hook_param; - } - - __attribute__((visibility("default"))) void *android_dlopen_ext(const char *filename, int flags, const android_dlextinfo *extinfo) { - auto fallback{[&]() { - LOGI("android_dlopen_ext: falling back!"); - return libdl_android_dlopen_ext(filename, flags, extinfo); - }}; - - LOGI("android_dlopen_ext: filename: %s", filename); - - // Ignore non-vulkan libraries - if (!strstr(filename, "vulkan.")) - return libdl_android_dlopen_ext(filename, flags, extinfo); - - if (extinfo->library_namespace == nullptr || !(extinfo->flags & ANDROID_DLEXT_USE_NAMESPACE)) { - LOGI("android_dlopen_ext: hook failed: namespace not supplied!"); - return fallback(); - } - - // customDriverDir will be empty if ADRENOTOOLS_DRIVER_CUSTOM isn't set therefore it's fine to have either way - auto driverNs{android_create_namespace(filename, hook_param->customDriverDir.c_str(), - hook_param->hookLibDir.c_str(), ANDROID_NAMESPACE_TYPE_SHARED, - nullptr, extinfo->library_namespace)}; - if (!driverNs) { - LOGI("android_dlopen_ext: hook failed: namespace not supplied!"); - return fallback(); - } - - // Hook libs depend on libandroid which is unlikely to be in the supplied driver namespace so we have to link it over - android_link_namespaces(driverNs, nullptr, "libandroid.so"); - - if (hook_param->featureFlags & ADRENOTOOLS_DRIVER_FILE_REDIRECT) { - if (!linkernsbypass_namespace_apply_hook("libfile_redirect_hook.so", driverNs, - reinterpret_cast(hook_param->fileRedirectDir.c_str()))) { - LOGI("android_dlopen_ext: hook failed: failed to apply libfopen_redirect_hook!"); - return fallback(); - } - - LOGI("android_dlopen_ext: applied libfopen_redirect_hook"); - } - - // Use our new namespace to load the vulkan driver - auto newExtinfo{*extinfo}; - newExtinfo.library_namespace = driverNs; - - // TODO: If there is already an instance of a vulkan driver loaded hooks won't be applied, this will only be the case for skiavk generally - // To fix this we would need to search /proc/self/maps for the file to a loaded instance of the library in order to read it to patch the soname and load it uniquely - if (hook_param->featureFlags & ADRENOTOOLS_DRIVER_CUSTOM) { - LOGI("android_dlopen_ext: loading custom driver: %s%s", hook_param->customDriverDir.c_str(), hook_param->customDriverName.c_str()); - return libdl_android_dlopen_ext(hook_param->customDriverName.c_str(), flags, &newExtinfo); - } else { - LOGI("android_dlopen_ext: loading default driver: %s", filename); - return libdl_android_dlopen_ext(filename, flags, &newExtinfo); - } - } - - __attribute__((visibility("default"))) void *android_load_sphal_library(const char *filename, int flags) { - // https://android.googlesource.com/platform/system/core/+/master/libvndksupport/linker.cpp - for (const char *name : {"sphal", "vendor", "default"}) { - if (auto vendorNs{android_get_exported_namespace(name)}) { - android_dlextinfo dlextinfo{ - .flags = ANDROID_DLEXT_USE_NAMESPACE, - .library_namespace = vendorNs, - }; - - return android_dlopen_ext(filename, flags, &dlextinfo); - } - } - - return nullptr; - } -} -