mirror of
https://git.eden-emu.dev/eden-emu/libadrenotools
synced 2026-02-04 10:51:21 +01:00
Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e2ab0b6f9d | ||
|
|
8ba23b42d7 | ||
|
|
8fae8ce254 | ||
|
|
c9d613fe3d | ||
|
|
5deac9f1ab | ||
|
|
02428431af | ||
|
|
deec5f75ee | ||
|
|
c4bc94c03e | ||
|
|
a5dfbe8b4f | ||
|
|
f4ce3c9618 | ||
|
|
48d76e5828 | ||
|
|
79087e7f08 | ||
|
|
5cd3f5c5ce | ||
|
|
089572cf1d | ||
|
|
b721d83895 | ||
|
|
19d1998c5a | ||
|
|
a6c0947df6 | ||
|
|
4ec3ae3dab | ||
|
|
0e4dccf696 | ||
|
|
d3b42cc5c0 |
@@ -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()
|
||||
|
||||
18
README.md
18
README.md
@@ -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
13
adrenotools.pc.in
Normal 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}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
Submodule lib/linkernsbypass updated: 700fd357b9...aa3975893d
@@ -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
|
||||
|
||||
169
src/driver.cpp
169
src/driver.cpp
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
13
src/hook/gsl_alloc_hook.c
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
2124
src/hook/kgsl.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
12
tools/acc-shim/README.md
Normal 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
|
||||
```
|
||||
108
tools/acc-shim/vk_acc_shim.cpp
Normal file
108
tools/acc-shim/vk_acc_shim.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user