mirror of
https://github.com/bylaws/libadrenotools
synced 2024-11-21 06:26:12 -07:00
Add a GSL hook that allows for importing host-mapped memory onto the GPU
This commit is contained in:
parent
0e4dccf696
commit
4ec3ae3dab
9 changed files with 2343 additions and 12 deletions
|
@ -19,7 +19,22 @@ extern "C" {
|
|||
* @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`
|
||||
*/
|
||||
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, adrenotools_gpu_mapping *nextGpuMapping);
|
||||
|
||||
/**
|
||||
* @brief Imports the given CPU mapped memory range into the GSL allocator. The out_mapping should have been passed to adrenotools_open_libvulkan with ADRENOTOOLS_DRIVER_GPU_MAPPING_IMPORT set in `featureFlags`. 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 outMapping Output mapping pointer, the same as was passed to 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(adrenotools_gpu_mapping *outMapping, 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(adrenotools_gpu_mapping *mapping);
|
||||
|
||||
#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;
|
||||
};
|
|
@ -4,14 +4,18 @@
|
|||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/ioctl.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>
|
||||
|
||||
void *adrenotools_open_libvulkan(int dlopenFlags, int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir) {
|
||||
void *adrenotools_open_libvulkan(int dlopenFlags, int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir, adrenotools_gpu_mapping *nextGpuMapping) {
|
||||
// Bail out if linkernsbypass failed to load, this probably means we're on api < 28
|
||||
if (!linkernsbypass_load_status())
|
||||
return nullptr;
|
||||
|
@ -27,6 +31,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) && nextGpuMapping)
|
||||
return nullptr;
|
||||
|
||||
// Verify that params for enabled features are correct
|
||||
struct stat buf{};
|
||||
|
||||
|
@ -64,7 +71,7 @@ void *adrenotools_open_libvulkan(int dlopenFlags, int featureFlags, const char *
|
|||
if (!initHookParam)
|
||||
return nullptr;
|
||||
|
||||
initHookParam(new HookImplParams(featureFlags, tmpLibDir, hookLibDir, customDriverDir, customDriverName, fileRedirectDir));
|
||||
initHookParam(new HookImplParams(featureFlags, tmpLibDir, hookLibDir, customDriverDir, customDriverName, fileRedirectDir, nextGpuMapping));
|
||||
|
||||
// Load the libvulkan hook into the isolated namespace
|
||||
if (!linkernsbypass_namespace_dlopen("libmain_hook.so", RTLD_GLOBAL, hookNs))
|
||||
|
@ -72,3 +79,48 @@ 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(adrenotools_gpu_mapping *outMapping, void *hostPtr, uint64_t size) {
|
||||
kgsl_gpuobj_import_useraddr addr{
|
||||
.virtaddr = reinterpret_cast<uint64_t>(hostPtr),
|
||||
};
|
||||
|
||||
kgsl_gpuobj_import user_mem_import{
|
||||
.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, &user_mem_import)};
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
info.id = user_mem_import.id;
|
||||
ret = ioctl(kgslFd, IOCTL_KGSL_GPUOBJ_INFO, &info);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
outMapping->host_ptr = hostPtr;
|
||||
outMapping->gpu_addr = info.gpuaddr;
|
||||
outMapping->size = size;
|
||||
outMapping->flags = 0xc2600; //!< Unknown flags, but they are required for the mapping to work
|
||||
|
||||
close(kgslFd);
|
||||
return true;
|
||||
|
||||
err:
|
||||
close(kgslFd);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool adrenotools_validate_gpu_mapping(adrenotools_gpu_mapping *mapping) {
|
||||
return mapping->gpu_addr == ADRENOTOOLS_GPU_MAPPING_SUCCEEDED_MAGIC;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
9
src/hook/gsl_alloc_hook.c
Normal file
9
src/hook/gsl_alloc_hook.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
#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(size, flags, memDesc);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) int gsl_memory_free_pure(void *memDesc) {
|
||||
return hook_gsl_memory_free_pure(memDesc);
|
||||
}
|
|
@ -3,9 +3,12 @@
|
|||
#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"
|
||||
|
||||
|
@ -13,11 +16,22 @@
|
|||
#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
|
||||
int (*gsl_memory_alloc_pure_sym)(uint32_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_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);
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) void init_gsl(void *alloc, void *free) {
|
||||
gsl_memory_alloc_pure_sym = reinterpret_cast<gsl_memory_alloc_pure_t>(alloc);
|
||||
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) {
|
||||
auto fallback{[&]() {
|
||||
LOGI("hook_android_dlopen_ext: falling back!");
|
||||
|
@ -46,7 +60,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 +71,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 +87,38 @@ __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("libgsl.so", RTLD_NOW, &newExtinfo)};
|
||||
if (!libgslHandle) {
|
||||
libgslHandle = android_dlopen_ext("notgsl.so", RTLD_NOW, &newExtinfo);
|
||||
if (!libgslHandle)
|
||||
libgslHandle = android_dlopen_ext("vkbgsl.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_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_free_pure_sym) {
|
||||
auto initGsl{reinterpret_cast<void (*)(gsl_memory_alloc_pure_t, gsl_memory_free_pure_t)>(dlsym(hookImpl, "init_gsl"))};
|
||||
if (!initGsl)
|
||||
return fallback();
|
||||
|
||||
initGsl(gsl_memory_alloc_pure_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_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 +137,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 +152,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 +164,60 @@ __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(uint32_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) == 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 {
|
||||
return gsl_memory_alloc_pure_sym(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 *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(uint32_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
Loading…
Reference in a new issue