mirror of
https://git.eden-emu.dev/eden-emu/libadrenotools
synced 2026-02-04 02:41:22 +01:00
Initial commit
This commit is contained in:
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[submodule "lib/linkernsbypass"]
|
||||
path = lib/linkernsbypass
|
||||
url = https://github.com/bylaws/liblinkernsbypass/
|
||||
branch = master
|
||||
27
CMakeLists.txt
Normal file
27
CMakeLists.txt
Normal file
@@ -0,0 +1,27 @@
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
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)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
|
||||
include_directories(lib/linkernsbypass)
|
||||
add_subdirectory(lib/linkernsbypass)
|
||||
|
||||
set(LIB_SOURCES src/bcenabler.cpp
|
||||
src/driver.cpp
|
||||
include/adrenotools/bcenabler.h
|
||||
include/adrenotools/driver.h
|
||||
include/adrenotools/priv.h)
|
||||
|
||||
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)
|
||||
|
||||
add_subdirectory(src/hook)
|
||||
24
LICENSE
Normal file
24
LICENSE
Normal file
@@ -0,0 +1,24 @@
|
||||
BSD 2-Clause License
|
||||
|
||||
Copyright (c) 2021, Billy Laws
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
12
README.md
Normal file
12
README.md
Normal file
@@ -0,0 +1,12 @@
|
||||
### Adreno Tools
|
||||
A library for applying rootless Adreno GPU driver modifications/replacements. Currently supports loading custom GPU drivers such as [turnip](https://docs.mesa3d.org/android.html#building-using-the-android-ndk), enabling BCn textures and redirecting file operations to allow accessing shader dumps and modification of the [driver config file](https://gist.github.com/bylaws/04130932e2634d1c6a2a9729e3940d60) without root.
|
||||
|
||||
#### Documentation
|
||||
API is documented in the `include/adrenotools` headers.
|
||||
|
||||
#### Support
|
||||
Android 9+
|
||||
Arm64
|
||||
|
||||
Please create an issue if support for anything else is desired.
|
||||
|
||||
10
build_asm.sh
Executable file
10
build_asm.sh
Executable file
@@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
|
||||
aarch64-linux-gnu-gcc -c src/bcenabler_patch.s -o /tmp/patch.o
|
||||
aarch64-linux-gnu-objcopy -O binary -j .text /tmp/patch.o /tmp/patch.bin
|
||||
|
||||
# Must be smaller than a single page
|
||||
bin2c -o gen/bcenabler_patch.h -n PatchRawData /tmp/patch.bin
|
||||
|
||||
rm /tmp/patch.o
|
||||
rm /tmp/patch.bin
|
||||
23
gen/bcenabler_patch.h
Normal file
23
gen/bcenabler_patch.h
Normal file
@@ -0,0 +1,23 @@
|
||||
/* Generated by bin2c, do not edit manually */
|
||||
|
||||
/* Contents of file /tmp/patch.bin */
|
||||
const long int PatchRawData_size = 264;
|
||||
const unsigned char PatchRawData[264] = {
|
||||
0x1F, 0x0C, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0xE0, 0x08, 0x80, 0x52, 0x3E, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x14, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0xE0, 0x08, 0x80, 0x52, 0x3A, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x10, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x00, 0x09, 0x80, 0x52, 0x36, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x18, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x00, 0x09, 0x80, 0x52, 0x32, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x1C, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x40, 0x09, 0x80, 0x52, 0x2E, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x20, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x60, 0x09, 0x80, 0x52, 0x2A, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x24, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0xA0, 0x09, 0x80, 0x52, 0x26, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x28, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0xC0, 0x09, 0x80, 0x52, 0x22, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x2C, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x00, 0x0A, 0x80, 0x52, 0x1E, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x30, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x20, 0x0A, 0x80, 0x52, 0x1A, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x34, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x60, 0x0A, 0x80, 0x52, 0x16, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x38, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x80, 0x0A, 0x80, 0x52, 0x12, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x3C, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0xE0, 0x0B, 0x80, 0x52, 0x0E, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x40, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x00, 0x0C, 0x80, 0x52, 0x0A, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x44, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x40, 0x0C, 0x80, 0x52, 0x06, 0x00, 0x00, 0x14,
|
||||
0x1F, 0x48, 0x02, 0x71, 0x61, 0x00, 0x00, 0x54, 0x60, 0x0C, 0x80, 0x52, 0x02, 0x00, 0x00, 0x14,
|
||||
0x00, 0x00, 0x80, 0x52, 0xFF, 0xFF, 0xFF, 0xFF
|
||||
};
|
||||
40
include/adrenotools/bcenabler.h
Normal file
40
include/adrenotools/bcenabler.h
Normal file
@@ -0,0 +1,40 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// Copyright © 2021 Billy Laws
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#else
|
||||
#include <stdbool.h>
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief Describes the level of BCeNabler support for a driver
|
||||
*/
|
||||
enum adrenotools_bcn_type {
|
||||
ADRENOTOOLS_BCN_INCOMPATIBLE, //!< Driver isn't supported by BCeNabler
|
||||
ADRENOTOOLS_BCN_BLOB, //!< Driver already supports BCn textures so BCeNabler isn't necessary
|
||||
ADRENOTOOLS_BCN_PATCH //!< Driver can be patched with BCeNabler to support BCn textures
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Checks the status of BCn support in the supplied driver
|
||||
* @param major The major part of VkPhysicalDeviceProperties::driverVersion
|
||||
* @param minor The minor part of VkPhysicalDeviceProperties::driverVersion
|
||||
*/
|
||||
enum adrenotools_bcn_type adrenotools_get_bcn_type(uint32_t major, uint32_t minor);
|
||||
|
||||
/**
|
||||
* @brief Patches the Adreno graphics driver to enable support for BCn compressed formats
|
||||
* @note adrenotools_get_bcn_type MUST be checked to equal ADRENOTOOLS_BCN_PATCH before calling this
|
||||
* @param vkGetPhysicalDeviceFormatPropertiesFn A pointer to vkGetPhysicalDeviceFormatProperties obtained through vkGetInstanceProcAddr. This is used to find the correct function to patch
|
||||
* @return If the patching succeeded, if false the driver will be in an undefined state
|
||||
*/
|
||||
bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
26
include/adrenotools/driver.h
Normal file
26
include/adrenotools/driver.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// Copyright © 2021 Billy Laws
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "priv.h"
|
||||
|
||||
/**
|
||||
* @brief Opens a new libvulkan.so instance according to `flags`
|
||||
* @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 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);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
12
include/adrenotools/priv.h
Normal file
12
include/adrenotools/priv.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// Copyright © 2021 Billy Laws
|
||||
|
||||
#pragma once
|
||||
|
||||
/**
|
||||
* @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
|
||||
};
|
||||
1
lib/linkernsbypass
Submodule
1
lib/linkernsbypass
Submodule
Submodule lib/linkernsbypass added at 8a819a35e1
125
src/bcenabler.cpp
Normal file
125
src/bcenabler.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// Copyright © 2021 Billy Laws
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cstring>
|
||||
#include <sys/mman.h>
|
||||
#include <adrenotools/bcenabler.h>
|
||||
#include "gen/bcenabler_patch.h"
|
||||
|
||||
enum adrenotools_bcn_type adrenotools_get_bcn_type(uint32_t major, uint32_t minor) {
|
||||
if (major != 512)
|
||||
return ADRENOTOOLS_BCN_INCOMPATIBLE;
|
||||
|
||||
if (minor >= 514)
|
||||
return ADRENOTOOLS_BCN_BLOB;
|
||||
|
||||
return ADRENOTOOLS_BCN_PATCH;
|
||||
}
|
||||
|
||||
// Searches /proc/self/maps for the first free page after the given address
|
||||
static void *find_free_page(uintptr_t address) {
|
||||
std::ifstream procMaps("/proc/self/maps");
|
||||
|
||||
uintptr_t end{};
|
||||
|
||||
for (std::string line; std::getline(procMaps, line); ) {
|
||||
std::size_t addressSeparator{line.find('-')};
|
||||
uintptr_t start{std::strtoull(line.substr(0, addressSeparator).c_str(), nullptr, 16)};
|
||||
|
||||
if (end > address && start != end)
|
||||
return reinterpret_cast<void *>(end);
|
||||
|
||||
end = std::strtoull(line.substr(addressSeparator + 1, line.find( ' ')).c_str(), nullptr, 16);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool adrenotools_patch_bcn(void *vkGetPhysicalDeviceFormatPropertiesFn) {
|
||||
union Branch {
|
||||
struct {
|
||||
int32_t offset : 26; //!< 26-bit branch offset
|
||||
uint8_t sig : 6; //!< 6-bit signature (0x25 for linked, 0x5 for jump)
|
||||
};
|
||||
|
||||
uint32_t raw{};
|
||||
};
|
||||
static_assert(sizeof(Branch) == 4, "Branch size is invalid");
|
||||
|
||||
// First branch in this function is targeted at the function we want to patch
|
||||
Branch *blInst{reinterpret_cast<Branch *>(vkGetPhysicalDeviceFormatPropertiesFn)};
|
||||
|
||||
constexpr uint8_t BranchLinkSignature{0x25};
|
||||
|
||||
// Search for first instruction with the BL signature
|
||||
while (blInst->sig != BranchLinkSignature)
|
||||
blInst++;
|
||||
|
||||
// Internal QGL format conversion function that we need to patch
|
||||
uint32_t *convFormatFn{reinterpret_cast<uint32_t *>(blInst) + blInst->offset};
|
||||
|
||||
// This would normally set the default result to 0 (error) in the format not found case
|
||||
constexpr uint32_t ClearResultSignature{0x2a1f03e0};
|
||||
|
||||
// We replace it with a branch to our own extended if statement which adds in the extra things for BCn
|
||||
uint32_t *clearResultPtr{convFormatFn};
|
||||
while (*clearResultPtr != ClearResultSignature)
|
||||
clearResultPtr++;
|
||||
|
||||
// Find the nearest unmapped page where we can place patch code
|
||||
void *patchPage{find_free_page(reinterpret_cast<uintptr_t>(clearResultPtr))};
|
||||
if (!patchPage)
|
||||
return false;
|
||||
|
||||
void *ptr{mmap(patchPage, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, 0, 0)};
|
||||
if (ptr != patchPage)
|
||||
return false;
|
||||
|
||||
// Ensure we don't write out of bounds
|
||||
if (PatchRawData_size > PAGE_SIZE)
|
||||
return false;
|
||||
|
||||
// Copy the patch function to our mapped page
|
||||
memcpy(patchPage, PatchRawData, PatchRawData_size);
|
||||
|
||||
// Fixup the patch code so it correctly returns back to the driver after running
|
||||
constexpr uint32_t PatchReturnFixupMagic{0xffffffff};
|
||||
constexpr uint8_t BranchSignature{0x5};
|
||||
|
||||
uint32_t *fixupTargetPtr = clearResultPtr + 1;
|
||||
uint32_t *fixupPtr = reinterpret_cast<uint32_t *>(patchPage);
|
||||
for (long unsigned int i{}; i < (PatchRawData_size / sizeof(uint32_t)); i++, fixupPtr++) {
|
||||
if (*fixupPtr == PatchReturnFixupMagic) {
|
||||
Branch branchToDriver{
|
||||
{
|
||||
.offset = static_cast<int32_t>((reinterpret_cast<intptr_t>(fixupTargetPtr) - reinterpret_cast<intptr_t>(fixupPtr)) / sizeof(int32_t)),
|
||||
.sig = BranchSignature,
|
||||
}
|
||||
};
|
||||
|
||||
*fixupPtr = branchToDriver.raw;
|
||||
}
|
||||
}
|
||||
|
||||
Branch branchToPatch{
|
||||
{
|
||||
.offset = static_cast<int32_t>((reinterpret_cast<intptr_t>(patchPage) - reinterpret_cast<intptr_t>(clearResultPtr)) / sizeof(int32_t)),
|
||||
.sig = BranchSignature,
|
||||
}
|
||||
};
|
||||
|
||||
void *driverPatchPage{reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(clearResultPtr) & ~(PAGE_SIZE - 1))};
|
||||
|
||||
// For some reason mprotect just breaks entirely after we patch the QGL instruction so just set perms to RWX
|
||||
if (mprotect(driverPatchPage, PAGE_SIZE, PROT_WRITE | PROT_READ | PROT_EXEC))
|
||||
return false;
|
||||
|
||||
*clearResultPtr = branchToPatch.raw;
|
||||
|
||||
asm volatile("ISB");
|
||||
|
||||
// Done!
|
||||
return true;
|
||||
}
|
||||
83
src/bcenabler_patch.s
Normal file
83
src/bcenabler_patch.s
Normal file
@@ -0,0 +1,83 @@
|
||||
cmp w0, 131
|
||||
bne .L2
|
||||
mov w0, 71
|
||||
b .L3
|
||||
.L2:
|
||||
cmp w0, 133
|
||||
bne .L4
|
||||
mov w0, 71
|
||||
b .L3
|
||||
.L4:
|
||||
cmp w0, 132
|
||||
bne .L5
|
||||
mov w0, 72
|
||||
b .L3
|
||||
.L5:
|
||||
cmp w0, 134
|
||||
bne .L6
|
||||
mov w0, 72
|
||||
b .L3
|
||||
.L6:
|
||||
cmp w0, 135
|
||||
bne .L7
|
||||
mov w0, 74
|
||||
b .L3
|
||||
.L7:
|
||||
cmp w0, 136
|
||||
bne .L8
|
||||
mov w0, 75
|
||||
b .L3
|
||||
.L8:
|
||||
cmp w0, 137
|
||||
bne .L9
|
||||
mov w0, 77
|
||||
b .L3
|
||||
.L9:
|
||||
cmp w0, 138
|
||||
bne .L10
|
||||
mov w0, 78
|
||||
b .L3
|
||||
.L10:
|
||||
cmp w0, 139
|
||||
bne .L11
|
||||
mov w0, 80
|
||||
b .L3
|
||||
.L11:
|
||||
cmp w0, 140
|
||||
bne .L12
|
||||
mov w0, 81
|
||||
b .L3
|
||||
.L12:
|
||||
cmp w0, 141
|
||||
bne .L13
|
||||
mov w0, 83
|
||||
b .L3
|
||||
.L13:
|
||||
cmp w0, 142
|
||||
bne .L14
|
||||
mov w0, 84
|
||||
b .L3
|
||||
.L14:
|
||||
cmp w0, 143
|
||||
bne .L15
|
||||
mov w0, 95
|
||||
b .L3
|
||||
.L15:
|
||||
cmp w0, 144
|
||||
bne .L16
|
||||
mov w0, 96
|
||||
b .L3
|
||||
.L16:
|
||||
cmp w0, 145
|
||||
bne .L17
|
||||
mov w0, 98
|
||||
b .L3
|
||||
.L17:
|
||||
cmp w0, 146
|
||||
bne .L18
|
||||
mov w0, 99
|
||||
b .L3
|
||||
.L18:
|
||||
mov w0, 0
|
||||
.L3:
|
||||
.word 0xffffffff // Branch fixup
|
||||
54
src/driver.cpp
Normal file
54
src/driver.cpp
Normal file
@@ -0,0 +1,54 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// Copyright © 2021 Billy Laws
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <sys/stat.h>
|
||||
#include <dlfcn.h>
|
||||
#include <android/api-level.h>
|
||||
#include <android/log.h>
|
||||
#include <android_linker_ns.h>
|
||||
#include "hook/main_hook.h"
|
||||
#include <adrenotools/driver.h>
|
||||
|
||||
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
|
||||
if (!linkernsbypass_load_status())
|
||||
return nullptr;
|
||||
|
||||
// Always use memfd on Q+ since it's guaranteed to work
|
||||
if (android_get_device_api_level() >= 29)
|
||||
tmpLibDir = nullptr;
|
||||
|
||||
// Verify that params for specific features are only passed if they are enabled
|
||||
if (!(featureFlags & ADRENOTOOLS_DRIVER_FILE_REDIRECT) && fileRedirectDir)
|
||||
return nullptr;
|
||||
|
||||
if (!(featureFlags & ADRENOTOOLS_DRIVER_CUSTOM) && (customDriverDir || customDriverName))
|
||||
return nullptr;
|
||||
|
||||
// Verify that params for enabled features are correct
|
||||
struct stat buf{};
|
||||
|
||||
if (featureFlags & ADRENOTOOLS_DRIVER_CUSTOM) {
|
||||
if (!customDriverName || !customDriverDir)
|
||||
return nullptr;
|
||||
|
||||
if (stat((std::string(customDriverDir) + customDriverName).c_str(), &buf) != 0)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Verify that params for enabled features are correct
|
||||
if (featureFlags & ADRENOTOOLS_DRIVER_FILE_REDIRECT) {
|
||||
if (!fileRedirectDir)
|
||||
return nullptr;
|
||||
|
||||
if (stat(fileRedirectDir, &buf) != 0)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Will be destroyed by the libmain_hook.so destructor on unload
|
||||
auto *hookParam{new MainHookParam(featureFlags, tmpLibDir, hookLibDir, customDriverDir, customDriverName, fileRedirectDir)};
|
||||
|
||||
return linkernsbypass_dlopen_unique_hooked("/system/lib64/libvulkan.so", tmpLibDir, dlopenMode, hookLibDir, "libmain_hook.so", nullptr, true, reinterpret_cast<void *>(hookParam));
|
||||
}
|
||||
15
src/hook/CMakeLists.txt
Normal file
15
src/hook/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
add_library(main_hook SHARED main_hook.cpp)
|
||||
|
||||
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)
|
||||
set_target_properties(main_hook PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||
|
||||
add_library(file_redirect_hook SHARED file_redirect_hook.cpp)
|
||||
|
||||
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)
|
||||
set_target_properties(file_redirect_hook PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||
35
src/hook/file_redirect_hook.cpp
Normal file
35
src/hook/file_redirect_hook.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <dlfcn.h>
|
||||
#include <android/log.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<decltype(&fopen)>(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);
|
||||
}
|
||||
}
|
||||
88
src/hook/main_hook.cpp
Normal file
88
src/hook/main_hook.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
#include <cstring>
|
||||
#include <android/dlext.h>
|
||||
#include <android_linker_ns.h>
|
||||
#include <android/log.h>
|
||||
#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<const void *>(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;
|
||||
}
|
||||
}
|
||||
|
||||
28
src/hook/main_hook.h
Normal file
28
src/hook/main_hook.h
Normal file
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// Copyright © 2021 Billy Laws
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <adrenotools/priv.h>
|
||||
|
||||
/**
|
||||
* @brief Holds the parameters for the main libvulkan.so hook
|
||||
* @note See comments for adrenotools_open_libvulkan as a reference for member variables
|
||||
*/
|
||||
struct MainHookParam {
|
||||
int featureFlags;
|
||||
std::string tmpLibDir;
|
||||
std::string hookLibDir;
|
||||
std::string customDriverDir;
|
||||
std::string customDriverName;
|
||||
std::string fileRedirectDir;
|
||||
|
||||
MainHookParam(int featureFlags, const char *tmpLibDir, const char *hookLibDir, const char *customDriverDir, const char *customDriverName, const char *fileRedirectDir)
|
||||
: featureFlags(featureFlags),
|
||||
tmpLibDir(tmpLibDir ? tmpLibDir : ""),
|
||||
hookLibDir(hookLibDir),
|
||||
customDriverDir(customDriverDir ? customDriverDir : ""),
|
||||
customDriverName(customDriverName ? customDriverName : ""),
|
||||
fileRedirectDir(fileRedirectDir ? fileRedirectDir : "") {}
|
||||
};
|
||||
Reference in New Issue
Block a user