Compare commits

20 Commits
v1.0 ... master

Author SHA1 Message Date
MrPurple666
e2ab0b6f9d Merge pull request 'initial adrenotools env loader' (#1) from MrPurple666/libadrenotools:master into master
Reviewed-on: https://git.eden-emu.dev/eden-emu/libadrenotools/pulls/1
2025-12-26 01:28:48 +01:00
MrPurple666
8ba23b42d7 initial adrenotools env loader 2025-12-25 21:24:57 -03:00
Reg Tiangha
8fae8ce254 Update linkernsbypass 2024-09-10 16:17:36 +01:00
Reg Tiangha
c9d613fe3d bcenabler.cpp: Replace PAGE_SIZE with getpagesize()
Android 15 will allow OEMs to ship arm64-v8a devices with 16KiB page sizes. Devices that use this configuration will not be able to run existing apps that use native code. To be compatible with these devices, applications will need to rebuild all their native code to be 16KiB aligned, and rewrite any code which assumes a specific page size.

This change should allow for page size to be calculated dynamically to support these new devices.
2024-09-10 09:16:22 +01:00
Matias N. Goldberg
5deac9f1ab Add FAQ and link to example project 2024-04-06 01:13:48 +01:00
Matias N. Goldberg
02428431af Improve documentation with important information
This avoids common mistakes
2024-04-06 01:13:48 +01:00
Billy Laws
deec5f75ee Inverse priority when loading gsl library
Most custom drivers ship with notgsl or vkbgsl, which should be loaded instead of the system gsl to prevent conflicts.
2023-06-30 18:04:51 +01:00
Billy Laws
c4bc94c03e Introduce new API for placed memory allocations
Useful for WOW64, where allocations need to be done in the lower 32-bits
of the AS.
2023-06-30 18:04:16 +01:00
Billy Laws
a5dfbe8b4f Add shared library install target supporting pkgconfig 2023-06-30 18:04:00 +01:00
Robin Kertels
f4ce3c9618 Update submodule 2023-06-10 17:59:35 +01:00
Robin Kertels
48d76e5828 Update submodule
To fix issues on older Android versions.
It won't work on those versions but it also shouldn't break them.
2023-06-09 20:41:47 +01:00
Robin Kertels
79087e7f08 Don't link Vulkan 2023-05-31 20:56:30 +01:00
Billy Laws
5cd3f5c5ce Support new 64-bit GSL memory allocation functions 2023-05-15 20:48:01 +01:00
Billy Laws
089572cf1d Rework memory import API 2023-05-06 14:42:58 +01:00
Billy Laws
b721d83895 Add compiler argument shim 2023-04-01 22:08:49 +01:00
Billy Laws
19d1998c5a Ensure that memory import arguments are a superset of those requested
Fixes 8G2, where some extra top bits are set but we don't request.
2023-03-05 22:55:36 +00:00
Billy Laws
a6c0947df6 Add function to enable KGSL turbo mode 2022-12-27 20:10:25 +00:00
Billy Laws
4ec3ae3dab Add a GSL hook that allows for importing host-mapped memory onto the GPU 2022-12-27 20:10:25 +00:00
Billy Laws
0e4dccf696 Remove file redir hook remenants 2022-12-27 20:10:25 +00:00
Billy Laws
d3b42cc5c0 Correct default mapper version for ADPKG instructions 2022-07-14 12:58:09 +01:00
19 changed files with 2690 additions and 32 deletions

View File

@@ -6,6 +6,8 @@ endif()
project(adrenotools LANGUAGES CXX C)
set(GEN_INSTALL_TARGET OFF CACHE BOOL "")
add_subdirectory(lib/linkernsbypass)
set(LIB_SOURCES src/bcenabler.cpp
@@ -19,6 +21,17 @@ add_library(adrenotools ${LIB_SOURCES})
target_include_directories(adrenotools PUBLIC include)
target_include_directories(adrenotools PRIVATE .)
target_compile_options(adrenotools PRIVATE -Wall -Wextra)
target_link_libraries(adrenotools vulkan android linkernsbypass)
target_link_libraries(adrenotools android linkernsbypass)
add_subdirectory(src/hook)
if (${BUILD_SHARED_LIBS})
target_link_options(adrenotools PRIVATE "-Wl,-s,--exclude-libs,liblinkernsbypass.a")
endif()
if (${GEN_INSTALL_TARGET})
configure_file(adrenotools.pc.in adrenotools.pc @ONLY)
install(FILES ${CMAKE_BINARY_DIR}/adrenotools.pc DESTINATION lib/pkgconfig)
install(TARGETS adrenotools main_hook file_redirect_hook gsl_alloc_hook hook_impl)
install(DIRECTORY include/
DESTINATION include
FILES_MATCHING PATTERN "*.h*")
endif()

View File

@@ -10,3 +10,21 @@ Arm64
Please create an issue if support for anything else is desired.
### FAQ
#### Is there an example project?
There is a simple bare-bones project [AdrenoToolsTest](https://github.com/darksylinc/AdrenoToolsTest) demonstrating how to get libadrenotools working.
#### How do I use this to update the drivers on my phone? Where's the apk?
You don't. This library is **not** for installing into Android and is **not** for end users.
This library is aimed at other developers.
Each individual app must explicitly make use of libadrenotools in order to load custom drivers into an app / game.
#### How do I use this library to make \<favourite game\> use newer drivers?
See previous question. It's up to the game developer to add support & use this library.
You could contact them to so they add support for it; but that's out of our power.

13
adrenotools.pc.in Normal file
View File

@@ -0,0 +1,13 @@
prefix="@CMAKE_INSTALL_PREFIX@"
exec_prefix=${prefix}
libdir=${exec_prefix}/lib
includedir=${prefix}/include
Name: adrenotools
Description:
URL:
Version: 1.0.0
Libs: -L${libdir} -ladrenotools
Libs.private:
Requires.private:
Cflags: -I${includedir}

View File

@@ -7,19 +7,76 @@
extern "C" {
#endif
#include <stdbool.h>
#include "priv.h"
/**
* @brief Opens a new libvulkan.so instance according to `flags`
* @remark IMPORTANT: The app MUST be packaged w/ useLegacyPackaging = true. The reason for this is
* that we install the hook in hookLibDir; but this folder won't be set correctly when useLegacyPackaging == false
* because Android expects to read the *.so directly from the apk (instead of decompressing them into nativeLibraryDir).
* @param dlopenMode The dlopen mode to use when opening libvulkan
* @param featureFlags Which adrenotools driver features to enable
* @param tmpLibDir A writable directory to hold patched libraries, only used on api < 29 due to the lack of memfd support. If nullptr is passed and the API version is < 29 memfd usage will be attempted and if unsupported nullptr will be returned
* @param hookLibDir The directory holding the built hooks
* @param customDriverDir The directory to load a custom GPU driver named according to `customDriverName` from. Only used if ADRENOTOOLS_DRIVER_CUSTOM is set in `featureFlags`
* @param hookLibDir The directory holding the built hooks.
* IMPORTANT: This path MUST point to whatever `getApplicationInfo().nativeLibraryDir` (Kotlin code) returns.
* Failing to do so will result in this function returning a valid ptr but then the hook will fail and either the original driver is loaded as fallback, or vkEnumeratePhysicalDevices returns 0 devices.
* @param customDriverDir The directory to load a custom GPU driver named according to `customDriverName` from. Only used if ADRENOTOOLS_DRIVER_CUSTOM is set in `featureFlags`.
* IMPORTANT: This path MUST NOT be on sdcard/storage. i.e. use ANativeActivity::internalDataPath and often looks like "/data/user/0/com.example.app/files/".
* Otherwise you will get permission denied because dlopen() can't be done on *.so libraries that any random user or application can manipulate (which would be a security risk).
* @param customDriverName The soname of the custom driver to load. Only used if ADRENOTOOLS_DRIVER_CUSTOM is set in `featureFlags`
* @param fileRedirectDir The directory which to redirect all file accesses performed by the driver to. Only used if ADRENOTOOLS_DRIVER_FILE_REDIRECT is set in `featureFlags`
* @param userMappingHandle A pointer to a void* which will be set to the mapping handle if ADRENOTOOLS_DRIVER_GPU_MAPPING_IMPORT is set in `featureFlags`
*/
void *adrenotools_open_libvulkan(int dlopenMode, int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir);
void *adrenotools_open_libvulkan(int dlopenMode, int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir, void **userMappingHandle);
/**
* @brief Imports the given CPU mapped memory range into the GSL allocator. This should then be followed by a call to vkAllocateMemory with a matching size which will return a VkDeviceMemory view over the input region
* @param handle Mapping handle that was returned by adrenotools_open_libvulkan
* @param hostPtr The host pointer to import
* @param size The size of the region to import
* @return True on success
*/
bool adrenotools_import_user_mem(void *handle, void *hostPtr, uint64_t size);
/**
* @brief Maps GPU memory and imports it into the GSL allocator. This should then be followed by a call to adrenotools_mem_cpu_map to map the memory on the CPU, then finally vkAllocateMemory with a matching size
* @param handle Mapping handle that was returned by adrenotools_open_libvulkan
* @param size Pointer to a variable containing the size of the region to import, will be updated to contain the required size of the region to allocate CPU side
* @return true on success
*/
bool adrenotools_mem_gpu_allocate(void *handle, uint64_t *size);
/**
* @brief Maps the last mapping allocated using adrenotools_mem_gpu_allocate into the given host memory region, such that vkAllocateMemory can then be called
* @param handle Mapping handle that was returned by adrenotools_open_libvulkan
* @param hostPtr A pointer to where the mapping should be mapped
* @param size The size of the mapping. MUST be equal to the size returned by adrenotools_mem_gpu_allocate
*/
bool adrenotools_mem_cpu_map(void *handle, void *hostPtr, uint64_t size);
/**
* @note This function should be called after adrenotools_open_libvulkan and Vulkan driver init to check if the mapping import hook loaded successfully
* @return True if the mapping was successfully imported (or initialization succeeded)
*/
bool adrenotools_validate_gpu_mapping(void *handle);
/**
* @brief Provides a way to force the GPU to run at the maximum possible clocks (thermal constraints will still be applied)
*/
void adrenotools_set_turbo(bool turbo);
/**
* @brief Sets a Freedreno/Turnip environment variable for driver configuration
* @param varName The environment variable name (e.g., "TU_DEBUG", "FD_RD_DUMP", "FD_MESA_DEBUG")
* @param value The value to set
* @note IMPORTANT: Must be called BEFORE adrenotools_open_libvulkan() to take effect
* @note Valid options documented at https://docs.mesa3d.org/drivers/freedreno.html
* @note Example TU_DEBUG values: "sysmem", "gmem", "nobin", "forcebin", "noubwc", "nolrz"
* @note Example FD_RD_DUMP values: "enable", "combine", "full", "trigger"
* @return true on success, false if varName or value is NULL
*/
bool adrenotools_set_freedreno_env(const char *varName, const char *value);
#ifdef __cplusplus
}

View File

@@ -3,10 +3,25 @@
#pragma once
#include <stdint.h>
/**
* @brief Bitfield enum of additional driver features that can be used with adrenotools_open_libvulkan
*/
enum {
ADRENOTOOLS_DRIVER_CUSTOM = 1 << 0,
ADRENOTOOLS_DRIVER_FILE_REDIRECT = 1 << 1
ADRENOTOOLS_DRIVER_FILE_REDIRECT = 1 << 1,
ADRENOTOOLS_DRIVER_GPU_MAPPING_IMPORT = 1 << 2,
};
#define ADRENOTOOLS_GPU_MAPPING_SUCCEEDED_MAGIC 0xDEADBEEF
/**
* @brief A replacement GPU memory mapping for use with ADRENOTOOLS_DRIVER_GPU_MAPPING_IMPORT
*/
struct adrenotools_gpu_mapping {
void *host_ptr;
uint64_t gpu_addr; //!< The GPU address of the mapping to import, if mapping import/init succeeds this will be set to ADRENOTOOLS_GPU_MAPPING_SUCCEEDED_MAGIC
uint64_t size;
uint64_t flags;
};

View File

@@ -5,6 +5,7 @@
#include <string>
#include <cstring>
#include <sys/mman.h>
#include <unistd.h>
#include <adrenotools/bcenabler.h>
#include "gen/bcenabler_patch.h"
@@ -38,7 +39,7 @@ static void *find_free_page(uintptr_t address) {
}
static void *align_ptr(void *ptr) {
return reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(ptr) & ~(PAGE_SIZE - 1));
return reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(ptr) & ~(getpagesize() - 1));
}
bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) {
@@ -58,13 +59,13 @@ bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) {
return false;
// Map patch region
void *ptr{mmap(patchPage, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0)};
void *ptr{mmap(patchPage, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0)};
if (ptr != patchPage)
return false;
// Allow reading from the blob's .text section since some devices enable ---X
// Protect two pages just in case we happen to land on a page boundary
if (mprotect(align_ptr(vkGetPhysicalDeviceFormatPropertiesFn), PAGE_SIZE * 2, PROT_WRITE | PROT_READ | PROT_EXEC))
if (mprotect(align_ptr(vkGetPhysicalDeviceFormatPropertiesFn), getpagesize() * 2, PROT_WRITE | PROT_READ | PROT_EXEC))
return false;
// First branch in this function is targeted at the function we want to patch
@@ -81,7 +82,7 @@ bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) {
// See mprotect call above
// This time we also set PROT_WRITE so we can write our patch to the page
if (mprotect(align_ptr(convFormatFn), PAGE_SIZE * 2, PROT_WRITE | PROT_READ | PROT_EXEC))
if (mprotect(align_ptr(convFormatFn), getpagesize() * 2, PROT_WRITE | PROT_READ | PROT_EXEC))
return false;
// This would normally set the default result to 0 (error) in the format not found case
@@ -93,7 +94,7 @@ bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) {
clearResultPtr++;
// Ensure we don't write out of bounds
if (PatchRawData_size > PAGE_SIZE)
if (PatchRawData_size > getpagesize())
return false;
// Copy the patch function to our mapped page

View File

@@ -4,17 +4,30 @@
#include <string>
#include <string_view>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <dlfcn.h>
#include <fcntl.h>
#include <android/api-level.h>
#include <android/log.h>
#include <android_linker_ns.h>
#include "hook/kgsl.h"
#include "hook/hook_impl_params.h"
#include <adrenotools/driver.h>
#include <unistd.h>
#include <cstring>
void *adrenotools_open_libvulkan(int dlopenFlags, int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir) {
#define TAG "adrenotools"
#define LOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##__VA_ARGS__)
#define LOGW(fmt, ...) __android_log_print(ANDROID_LOG_WARN, TAG, fmt, ##__VA_ARGS__)
#define LOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##__VA_ARGS__)
void *adrenotools_open_libvulkan(int dlopenFlags, int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir, void **userMappingHandle) {
// Bail out if linkernsbypass failed to load, this probably means we're on api < 28
if (!linkernsbypass_load_status())
if (!linkernsbypass_load_status()) {
LOGE("adrenotools_open_libvulkan: linkernsbypass_load_status() failed");
return nullptr;
}
// Always use memfd on Q+ since it's guaranteed to work
if (android_get_device_api_level() >= 29)
@@ -27,6 +40,9 @@ void *adrenotools_open_libvulkan(int dlopenFlags, int featureFlags, const char *
if (!(featureFlags & ADRENOTOOLS_DRIVER_CUSTOM) && (customDriverDir || customDriverName))
return nullptr;
if (!(featureFlags & ADRENOTOOLS_DRIVER_GPU_MAPPING_IMPORT) && userMappingHandle)
return nullptr;
// Verify that params for enabled features are correct
struct stat buf{};
@@ -64,7 +80,19 @@ void *adrenotools_open_libvulkan(int dlopenFlags, int featureFlags, const char *
if (!initHookParam)
return nullptr;
initHookParam(new HookImplParams(featureFlags, tmpLibDir, hookLibDir, customDriverDir, customDriverName, fileRedirectDir));
auto importMapping{[&]() -> adrenotools_gpu_mapping * {
if (featureFlags & ADRENOTOOLS_DRIVER_GPU_MAPPING_IMPORT) {
// This will be leaked, but it's not a big deal since it's only a few bytes
adrenotools_gpu_mapping *mapping{new adrenotools_gpu_mapping{}};
*userMappingHandle = mapping;
return mapping;
} else {
return nullptr;
}
}()};
initHookParam(new HookImplParams(featureFlags, tmpLibDir, hookLibDir, customDriverDir, customDriverName, fileRedirectDir, importMapping));
// Load the libvulkan hook into the isolated namespace
if (!linkernsbypass_namespace_dlopen("libmain_hook.so", RTLD_GLOBAL, hookNs))
@@ -72,3 +100,138 @@ void *adrenotools_open_libvulkan(int dlopenFlags, int featureFlags, const char *
return linkernsbypass_namespace_dlopen_unique("/system/lib64/libvulkan.so", tmpLibDir, dlopenFlags, hookNs);
}
bool adrenotools_import_user_mem(void *handle, void *hostPtr, uint64_t size) {
auto importMapping{reinterpret_cast<adrenotools_gpu_mapping *>(handle)};
kgsl_gpuobj_import_useraddr addr{
.virtaddr = reinterpret_cast<uint64_t>(hostPtr),
};
kgsl_gpuobj_import userMemImport{
.priv = reinterpret_cast<uint64_t>(&addr),
.priv_len = size,
.flags = KGSL_CACHEMODE_WRITEBACK << KGSL_CACHEMODE_SHIFT | KGSL_MEMFLAGS_IOCOHERENT,
.type = KGSL_USER_MEM_TYPE_ADDR,
};
kgsl_gpuobj_info info{};
int kgslFd{open("/dev/kgsl-3d0", O_RDWR)};
if (kgslFd < 0)
return false;
int ret{ioctl(kgslFd, IOCTL_KGSL_GPUOBJ_IMPORT, &userMemImport)};
if (ret)
goto err;
info.id = userMemImport.id;
ret = ioctl(kgslFd, IOCTL_KGSL_GPUOBJ_INFO, &info);
if (ret)
goto err;
importMapping->host_ptr = hostPtr;
importMapping->gpu_addr = info.gpuaddr;
importMapping->size = size;
importMapping->flags = 0xc2600; //!< Unknown flags, but they are required for the mapping to work
close(kgslFd);
return true;
err:
close(kgslFd);
return false;
}
bool adrenotools_mem_gpu_allocate(void *handle, uint64_t *size) {
auto mapping{reinterpret_cast<adrenotools_gpu_mapping *>(handle)};
kgsl_gpuobj_alloc gpuobjAlloc{
.size = *size,
.flags = KGSL_CACHEMODE_WRITEBACK << KGSL_CACHEMODE_SHIFT | KGSL_MEMFLAGS_IOCOHERENT,
};
kgsl_gpuobj_info info{};
int kgslFd{open("/dev/kgsl-3d0", O_RDWR)};
if (kgslFd < 0)
return false;
int ret{ioctl(kgslFd, IOCTL_KGSL_GPUOBJ_ALLOC, &gpuobjAlloc)};
if (ret)
goto err;
*size = gpuobjAlloc.mmapsize;
info.id = gpuobjAlloc.id;
ret = ioctl(kgslFd, IOCTL_KGSL_GPUOBJ_INFO, &info);
if (ret)
goto err;
mapping->host_ptr = nullptr;
mapping->gpu_addr = info.gpuaddr;
mapping->size = *size;
mapping->flags = 0xc2600; //!< Unknown flags, but they are required for the mapping to work
close(kgslFd);
return true;
err:
close(kgslFd);
return false;
}
bool adrenotools_mem_cpu_map(void *handle, void *hostPtr, uint64_t size) {
auto mapping{reinterpret_cast<adrenotools_gpu_mapping *>(handle)};
int kgslFd{open("/dev/kgsl-3d0", O_RDWR)};
if (kgslFd < 0)
return false;
mapping->host_ptr = mmap(hostPtr, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, kgslFd, mapping->gpu_addr);
close(kgslFd);
return mapping->host_ptr != nullptr;
}
bool adrenotools_validate_gpu_mapping(void *handle) {
auto importMapping{reinterpret_cast<adrenotools_gpu_mapping *>(handle)};
return importMapping->gpu_addr == ADRENOTOOLS_GPU_MAPPING_SUCCEEDED_MAGIC;
}
void adrenotools_set_turbo(bool turbo) {
uint32_t enable{turbo ? 0U : 1U};
kgsl_device_getproperty prop{
.type = KGSL_PROP_PWRCTRL,
.value = reinterpret_cast<void *>(&enable),
.sizebytes = sizeof(enable),
};
int kgslFd{open("/dev/kgsl-3d0", O_RDWR)};
if (kgslFd < 0)
return;
ioctl(kgslFd, IOCTL_KGSL_SETPROPERTY, &prop);
close (kgslFd);
}
bool adrenotools_set_freedreno_env(const char *varName, const char *value) {
if (!varName || !value || std::strlen(varName) == 0)
return false;
int result = setenv(varName, value, 1);
if (result != 0) {
LOGE("adrenotools_set_freedreno_env: Failed to set '%s' (errno: %d)", varName, errno);
return false;
}
const char *verifyValue = std::getenv(varName);
if (verifyValue && std::strcmp(verifyValue, value) == 0) {
return true;
} else {
LOGE("adrenotools_set_freedreno_env: Verification failed for '%s'", varName);
return false;
}
}

View File

@@ -18,3 +18,10 @@ target_compile_options(file_redirect_hook PRIVATE -Wall -Wextra)
target_link_options(file_redirect_hook PRIVATE -z global)
target_link_libraries(file_redirect_hook hook_impl)
set_target_properties(file_redirect_hook PROPERTIES CXX_VISIBILITY_PRESET hidden)
add_library(gsl_alloc_hook SHARED gsl_alloc_hook.c)
target_compile_options(gsl_alloc_hook PRIVATE -Wall -Wextra)
target_link_options(gsl_alloc_hook PRIVATE -z global)
target_link_libraries(gsl_alloc_hook hook_impl)
set_target_properties(gsl_alloc_hook PROPERTIES CXX_VISIBILITY_PRESET hidden)

View File

@@ -1,9 +0,0 @@
#include "hook_impl.h"
__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);
}

13
src/hook/gsl_alloc_hook.c Normal file
View File

@@ -0,0 +1,13 @@
#include "hook_impl.h"
__attribute__((visibility("default"))) int gsl_memory_alloc_pure(uint32_t size, uint32_t flags, void *memDesc) {
return hook_gsl_memory_alloc_pure_64((uint64_t)size, flags, memDesc);
}
__attribute__((visibility("default"))) int gsl_memory_alloc_pure_64(uint64_t size, uint32_t flags, void *memDesc) {
return hook_gsl_memory_alloc_pure_64(size, flags, memDesc);
}
__attribute__((visibility("default"))) int gsl_memory_free_pure(void *memDesc) {
return hook_gsl_memory_free_pure(memDesc);
}

View File

@@ -3,19 +3,43 @@
#include <cstdio>
#include <cstring>
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <android_linker_ns.h>
#include <android/dlext.h>
#include <android/log.h>
#include "kgsl.h"
#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__)
#define LOGW(fmt, ...) __android_log_print(ANDROID_LOG_WARN, TAG, fmt, ##__VA_ARGS__)
#define LOGE(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##__VA_ARGS__)
const HookImplParams *hook_params; //!< Bunch of info needed to load/patch the driver
int (*gsl_memory_alloc_pure_sym)(uint32_t, uint32_t, void *);
int (*gsl_memory_alloc_pure_64_sym)(uint64_t, uint32_t, void *);
int (*gsl_memory_free_pure_sym)(void *);
int kgsl_fd;
using gsl_memory_alloc_pure_t = decltype(gsl_memory_alloc_pure_sym);
using gsl_memory_alloc_pure_64_t = decltype(gsl_memory_alloc_pure_64_sym);
using gsl_memory_free_pure_t = decltype(gsl_memory_free_pure_sym);
__attribute__((visibility("default"))) void init_hook_param(const void *param) {
hook_params = reinterpret_cast<const HookImplParams *>(param);
if (!hook_params) {
LOGE("init_hook_param: Received NULL hook_params!");
return;
}
}
__attribute__((visibility("default"))) void init_gsl(void *alloc, void *alloc64, void *free) {
gsl_memory_alloc_pure_sym = reinterpret_cast<gsl_memory_alloc_pure_t>(alloc);
gsl_memory_alloc_pure_64_sym = reinterpret_cast<gsl_memory_alloc_pure_64_t>(alloc64);
gsl_memory_free_pure_sym = reinterpret_cast<gsl_memory_free_pure_t>(free);
}
__attribute__((visibility("default"))) void *hook_android_dlopen_ext(const char *filename, int flags, const android_dlextinfo *extinfo) {
@@ -46,7 +70,7 @@ __attribute__((visibility("default"))) void *hook_android_dlopen_ext(const char
// 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)};
@@ -57,7 +81,7 @@ __attribute__((visibility("default"))) void *hook_android_dlopen_ext(const char
auto initHookParam{reinterpret_cast<void (*)(const void *)>(dlsym(hookImpl, "init_hook_param"))};
if (!initHookParam)
return nullptr;
initHookParam(hook_params);
if (hook_params->featureFlags & ADRENOTOOLS_DRIVER_FILE_REDIRECT) {
@@ -73,7 +97,39 @@ __attribute__((visibility("default"))) void *hook_android_dlopen_ext(const char
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
if (hook_params->featureFlags & ADRENOTOOLS_DRIVER_GPU_MAPPING_IMPORT) {
if (!linkernsbypass_namespace_dlopen("libgsl_alloc_hook.so", RTLD_GLOBAL, driverNs)) {
LOGI("hook_android_dlopen_ext: hook failed: failed to apply libgsl_alloc_hook!");
return fallback();
}
auto libgslHandle{android_dlopen_ext("vkbgsl.so", RTLD_NOW, &newExtinfo)};
if (!libgslHandle) {
libgslHandle = android_dlopen_ext("notgsl.so", RTLD_NOW, &newExtinfo);
if (!libgslHandle)
libgslHandle = android_dlopen_ext("libgsl.so", RTLD_NOW, &newExtinfo);
}
if (libgslHandle) {
gsl_memory_alloc_pure_sym = reinterpret_cast<decltype(gsl_memory_alloc_pure_sym)>(dlsym(libgslHandle, "gsl_memory_alloc_pure"));
gsl_memory_alloc_pure_64_sym = reinterpret_cast<decltype(gsl_memory_alloc_pure_64_sym)>(dlsym(libgslHandle, "gsl_memory_alloc_pure_64"));
gsl_memory_free_pure_sym = reinterpret_cast<decltype(gsl_memory_free_pure_sym)>(dlsym(libgslHandle, "gsl_memory_free_pure"));
if ((gsl_memory_alloc_pure_sym || gsl_memory_alloc_pure_64_sym) && gsl_memory_free_pure_sym) {
auto initGsl{reinterpret_cast<void (*)(gsl_memory_alloc_pure_t, gsl_memory_alloc_pure_64_t, gsl_memory_free_pure_t)>(dlsym(hookImpl, "init_gsl"))};
if (!initGsl)
return fallback();
initGsl(gsl_memory_alloc_pure_sym, gsl_memory_alloc_pure_64_sym, gsl_memory_free_pure_sym);
LOGI("hook_android_dlopen_ext: applied libgsl_alloc_hook");
hook_params->nextGpuMapping->gpu_addr = ADRENOTOOLS_GPU_MAPPING_SUCCEEDED_MAGIC;
}
}
if (!((gsl_memory_alloc_pure_sym || gsl_memory_alloc_pure_64_sym) && gsl_memory_free_pure_sym))
LOGI("hook_android_dlopen_ext: hook failed: failed to apply libgsl_alloc_hook!");
}
// 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());
@@ -92,7 +148,7 @@ __attribute__((visibility("default"))) void *hook_android_dlopen_ext(const char
__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)}) {
@@ -107,7 +163,7 @@ __attribute__((visibility("default"))) void *hook_android_load_sphal_library(con
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);
@@ -119,3 +175,62 @@ __attribute__((visibility("default"))) FILE *hook_fopen(const char *filename, co
return fopen(replacement.c_str(), mode);
}
static constexpr uintptr_t GslMemDescImportedPrivMagic{0xdeadb33f};
struct GslMemDesc {
void *hostptr;
uint64_t gpuaddr;
uint64_t size;
uint64_t flags;
uintptr_t priv;
};
__attribute__((visibility("default"))) int hook_gsl_memory_alloc_pure_64(uint64_t size, uint32_t flags, void *memDesc) {
auto gslMemDesc{reinterpret_cast<GslMemDesc *>(memDesc)};
if (hook_params->nextGpuMapping && hook_params->nextGpuMapping->size == size && (hook_params->nextGpuMapping->flags & flags) == hook_params->nextGpuMapping->flags) {
auto &nextMapping{*hook_params->nextGpuMapping};
gslMemDesc->hostptr = nextMapping.host_ptr;
gslMemDesc->gpuaddr = nextMapping.gpu_addr;
gslMemDesc->size = nextMapping.size;
gslMemDesc->flags = nextMapping.flags;
gslMemDesc->priv = GslMemDescImportedPrivMagic;
hook_params->nextGpuMapping->size = 0;
hook_params->nextGpuMapping->gpu_addr = ADRENOTOOLS_GPU_MAPPING_SUCCEEDED_MAGIC;
return 0;
} else {
if (gsl_memory_alloc_pure_64_sym)
return gsl_memory_alloc_pure_64_sym(size, flags, gslMemDesc);
else
return gsl_memory_alloc_pure_sym((uint32_t)size, flags, gslMemDesc);
}
}
__attribute__((visibility("default"))) int hook_gsl_memory_free_pure(void *memDesc) {
auto gslMemDesc{reinterpret_cast<GslMemDesc *>(memDesc)};
if (gslMemDesc->priv == GslMemDescImportedPrivMagic) {
if (!kgsl_fd)
kgsl_fd = open("/dev/kgsl-3d0", O_RDWR);
kgsl_gpumem_get_info info{
.gpuaddr = gslMemDesc->gpuaddr
};
if (ioctl(kgsl_fd, IOCTL_KGSL_GPUMEM_GET_INFO, &info) < 0) {
LOGI("IOCTL_KGSL_GPUMEM_GET_INFO failed");
return 0;
}
kgsl_gpuobj_free args{
.id = info.id,
};
if (ioctl(kgsl_fd, IOCTL_KGSL_GPUOBJ_FREE, &args) < 0)
LOGI("IOCTL_KGSL_GPUOBJ_FREE failed");
return 0;
} else {
return gsl_memory_free_pure_sym(memDesc);
}
}

View File

@@ -11,13 +11,18 @@ extern "C" {
#include <android/dlext.h>
void init_hook_param(const void *param);
void init_gsl(void *alloc, void *alloc64, void *free);
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);
int hook_gsl_memory_alloc_pure_64(uint64_t size, uint32_t flags, void *memDesc);
int hook_gsl_memory_free_pure(void *memDesc);
#ifdef __cplusplus
}

View File

@@ -17,13 +17,15 @@ struct HookImplParams {
std::string customDriverDir;
std::string customDriverName;
std::string fileRedirectDir;
adrenotools_gpu_mapping *nextGpuMapping;
HookImplParams(int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir,
const char *customDriverName, const char *fileRedirectDir)
const char *customDriverName, const char *fileRedirectDir, adrenotools_gpu_mapping *nextGpuMapping)
: featureFlags(featureFlags),
tmpLibDir(tmpLibDir ? tmpLibDir : ""),
hookLibDir(hookLibDir),
customDriverDir(customDriverDir ? customDriverDir : ""),
customDriverName(customDriverName ? customDriverName : ""),
fileRedirectDir(fileRedirectDir ? fileRedirectDir : "") {}
fileRedirectDir(fileRedirectDir ? fileRedirectDir : ""),
nextGpuMapping(nextGpuMapping) {}
};

2124
src/hook/kgsl.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,7 @@
I recommend running blob-patcher.py to patch a set of blobs from a device dump with mapper ver as 5, then copying in the qtimapper-shim files from the adrenotools releases page.
```bash
$ mkdir outpkg
$ patch.py <device dump> outpkg vulkan.adreno.so vulkan.ad0615.so 5
$ patch.py <device dump> outpkg vulkan.adreno.so vulkan.ad0615.so 1
$ vim outpkg/meta.json
$ cp qtimapper-shim-rel/* outpkg
```

View File

@@ -2,5 +2,6 @@
blob-patcher.py is a script for generating adrenotools loadable drivers from an extracted ROM zip
qtimapper-shim allows newer drivers to work on devices that lack support for the new mapper HAL
acc-shim allows for suppling arguments to the underlying LLVM-based shader compiler library

12
tools/acc-shim/README.md Normal file
View File

@@ -0,0 +1,12 @@
## Adreno Compiler Collection parameter shim
### TODO
Make some sort of API for this so parameters can be changed through adrenotools in realtime.
### Compilation
```
$ sed -i 's/libllvm-glnext/notreal-glnext/g' libllvm-glnext.so
$ mv libllvm-glnext.so notreal-glnext.so
$ aarch64-linux-android28-clang vk_acc_shim.cpp -o notllvm-glnext.so --shared -fpic
$ sed -i 's/libllvm-glnext/notllvm-glnext/g' vulkan.adreno.so
```

View File

@@ -0,0 +1,108 @@
#include <stdint.h>
#include <dlfcn.h>
#include <string.h>
#include <android/log.h>
thread_local int satisfy_driver_emutls;
// Check libllvm-glnext.so in older drivers for a full list of these, newer ones are slightly more awkward due to the split
enum class OptionType : uint32_t {
ArgsStringList = 1,
QcArgsString = 4,
OptLevelInt = 0x101,
NoOptsInt = 0x10D,
};
struct StringListVal {
uint32_t len;
const char **entry;
};
struct Option
{
OptionType type;
// PAD
union {
bool boolVal;
int intVal;
float floatVal;
StringListVal *strVals;
const char *strVal;
};
};
struct OptionsSet
{
Option *options;
uint32_t num;
};
int CompileHook(void *state, OptionsSet *opts);
decltype(CompileHook) *OrigCompile{};
int CompileHook(void *state, OptionsSet *opts) {
constexpr size_t ReplacementOptsNum = 201;
Option replacementOpts[ReplacementOptsNum];
uint32_t patchedOptNum{};
// Insert your desired args here - results will be written to stderr
const char *argv[] = {"-help", "-help-hidden", "", NULL };
StringListVal listVal{
.len = 3,
.entry = argv,
};
replacementOpts[patchedOptNum++] = {
.type = OptionType::ArgsStringList,
.strVals = &listVal
};
const char *opt ="Verbose=true Quiet=false";
replacementOpts[patchedOptNum++] = {
.type = OptionType::QcArgsString,
.strVal = opt
};
if (opts->num + patchedOptNum > ReplacementOptsNum)
__builtin_trap();
memcpy(&replacementOpts[patchedOptNum], opts->options, sizeof(Option) * opts->num);
OptionsSet optList{
.options = replacementOpts,
.num = opts->num + patchedOptNum
};
return OrigCompile(state, &optList);
}
struct LlvmInterface {
uint32_t unk0[3];
// PAD
void *makeContext;
void *delContext;
decltype(CompileHook) *compile;
void *unk;
};
extern "C" int LoadACC(LlvmInterface *impl);
extern "C" __attribute__((visibility("default"))) int LoadACC(LlvmInterface *impl) {
void *hnd = dlopen("notreal-glnext.so", RTLD_NOW);
auto origLoadACC = reinterpret_cast<decltype(LoadACC) *>(dlsym(hnd, "LoadACC"));
if (!origLoadACC)
__builtin_trap();
int ret = origLoadACC(impl);
OrigCompile = impl->compile;
impl->compile = &CompileHook;
return ret;
}