mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 10:00:16 +02:00
Thunks: Implement support for CUDA
This is enough to get less complex cuda applications running, and is a good starting spot to slowly finish off the remaining implementation. Some information: - 429 functions in total - 153 only compiled for 64-bit (35.6%) - 7 functions disabled entirely (1.6%) The main thing /not/ working with this initial implementation is .cu files compiled in to an ELF using the static cuda runtime. This is due to the `cuGetExportTable` function being stubbed out and the static cuda RT requires at least two interfaces from that function before it continues. This function isn't publicly documented by NVIDIA but has been publicly reverse engineered to be fairly trivial. It's just a jump table with the first element being the size of the table in bytes. That will be the next step of the implementation.
This commit is contained in:
8 files changed
+2781
No files matched your search
@@ -46,6 +46,13 @@
|
||||
"@PREFIX_LIB@/libwayland-client.so.0",
|
||||
"@PREFIX_LIB@/libwayland-client.so.0.20.0"
|
||||
]
|
||||
},
|
||||
"cuda": {
|
||||
"Library" : "libcuda-guest.so",
|
||||
"Overlay": [
|
||||
"@PREFIX_LIB@/libcuda.so",
|
||||
"@PREFIX_LIB@/libcuda.so.1"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -266,3 +266,6 @@ add_library(PlaceholderX11 SHARED ../libX11/libX11_NativeGuest.cpp)
|
||||
target_link_options(PlaceholderX11 PRIVATE "LINKER:-soname,libX11.so.6")
|
||||
set_target_properties(PlaceholderX11 PROPERTIES NO_SONAME ON)
|
||||
target_link_libraries(GL-guest PRIVATE PlaceholderX11)
|
||||
|
||||
generate(libcuda ${CMAKE_CURRENT_SOURCE_DIR}/../libcuda/libcuda_interface.cpp)
|
||||
add_guest_lib(cuda "libcuda.so.1")
|
||||
@@ -167,6 +167,9 @@ foreach(GUEST_BITNESS IN LISTS BITNESS_LIST)
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
target_link_libraries(GL-host-${GUEST_BITNESS} PRIVATE OpenGL::GL)
|
||||
|
||||
generate(libcuda ${CMAKE_CURRENT_SOURCE_DIR}/../libcuda/libcuda_interface.cpp ${GUEST_BITNESS})
|
||||
add_host_lib(cuda ${GUEST_BITNESS})
|
||||
endforeach()
|
||||
|
||||
if (BUILD_FEX_LINUX_TESTS)
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#pragma once
|
||||
#include <cstddef>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*CUhostFn)(void*);
|
||||
enum cudaError_enum {};
|
||||
enum CUgreenCtxCreate_flags {};
|
||||
enum CUdevResourceType {};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,84 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "common/Guest.h"
|
||||
#include "cuda_defines.h"
|
||||
|
||||
#include "thunkgen_guest_libcuda.inl"
|
||||
#include <cstdio>
|
||||
#include <dlfcn.h>
|
||||
#include <functional>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
// Maps cuda API function names to the address of a guest function which is
|
||||
// linked to the corresponding host function pointer
|
||||
const std::unordered_map<std::string_view, uintptr_t /* guest function address */> HostPtrInvokers = std::invoke([]() {
|
||||
#define PAIR(name, unused) Ret[#name] = reinterpret_cast<uintptr_t>(GetCallerForHostFunction(name));
|
||||
std::unordered_map<std::string_view, uintptr_t> Ret;
|
||||
FOREACH_internal_SYMBOL(PAIR);
|
||||
return Ret;
|
||||
#undef PAIR
|
||||
});
|
||||
|
||||
extern "C" {
|
||||
|
||||
// This variable controls the behavior of cuGetProcAddress for functions we don't know the signature of:
|
||||
// - if false (default), we return a nullptr (since the application might have a fallback code path)
|
||||
// - if true, we return a stub function that fatally errors upon being called
|
||||
constexpr bool stub_unknown_functions = false;
|
||||
|
||||
// Fatally erroring function with a thunk-like interface. This is used as a placeholder for unknown CUDA functions
|
||||
[[noreturn]]
|
||||
static void FatalError(void* raw_args) {
|
||||
auto called_function = reinterpret_cast<PackedArguments<void, uintptr_t>*>(raw_args)->a0;
|
||||
fprintf(stderr, "FATAL: Called unknown CUDA function at address %p\n", reinterpret_cast<void*>(called_function));
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
static void* MakeGuestCallable(const char* origin, void* func, const char* name) {
|
||||
auto It = HostPtrInvokers.find(name);
|
||||
if (It == HostPtrInvokers.end()) {
|
||||
fprintf(stderr, "%s: Unknown cuda function at address %p: %s\n", origin, func, name);
|
||||
if (stub_unknown_functions) {
|
||||
const auto StubHostPtrInvoker = CallHostFunction<FatalError, void>;
|
||||
LinkAddressToFunction((uintptr_t)func, reinterpret_cast<uintptr_t>(StubHostPtrInvoker));
|
||||
return func;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
LinkAddressToFunction((uintptr_t)func, It->second);
|
||||
return func;
|
||||
}
|
||||
|
||||
struct override_entry {
|
||||
std::string_view name;
|
||||
void* ptr;
|
||||
};
|
||||
|
||||
constexpr static std::array<override_entry, 2> proc_override = {
|
||||
{{"cuGetProcAddress", (void*)cuGetProcAddress_v2}, {"cuGetProcAddress_v2", (void*)cuGetProcAddress_v2}}};
|
||||
|
||||
CUresult cuGetProcAddress_v2(const char* symbol, void** pfn, int cudaVersion, cuuint64_t flags, CUdriverProcAddressQueryResult* symbolStatus) {
|
||||
|
||||
for (auto& over : proc_override) {
|
||||
if (symbol == over.name) {
|
||||
*pfn = over.ptr;
|
||||
if (symbolStatus) {
|
||||
*symbolStatus = (CUdriverProcAddressQueryResult)0;
|
||||
}
|
||||
return (CUresult)0; // CUDA_SUCCESS
|
||||
}
|
||||
}
|
||||
|
||||
void* ptr {};
|
||||
auto Ret = fexfn_pack_cuGetProcAddress_v2(symbol, &ptr, cudaVersion, flags, symbolStatus);
|
||||
|
||||
if (!Ret) {
|
||||
*pfn = MakeGuestCallable(__FUNCTION__, ptr, symbol);
|
||||
}
|
||||
return Ret;
|
||||
}
|
||||
}
|
||||
|
||||
LOAD_LIB(libcuda)
|
||||
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include "common/Host.h"
|
||||
#include "cuda_defines.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <dlfcn.h>
|
||||
#include <string>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
#include "thunkgen_host_libcuda.inl"
|
||||
|
||||
#define FEXFN_IMPL(fn) fexfn_impl_libcuda_##fn
|
||||
#define LDR_PTR(fn) fexldr_ptr_libcuda_##fn
|
||||
|
||||
struct override_entry {
|
||||
std::string_view name;
|
||||
void* ptr;
|
||||
};
|
||||
|
||||
constexpr static std::array<override_entry, 2> proc_override = {
|
||||
{{"cuCtxCreate_v2", (void*)FEXFN_IMPL(cuCtxCreate_v2)}, {"cuGetExportTable", (void*)FEXFN_IMPL(cuGetExportTable)}}};
|
||||
|
||||
static CUresult FEXFN_IMPL(cuGetProcAddress_v2)(const char* symbol, guest_layout<void**> pfn, int cudaVersion, cuuint64_t flags,
|
||||
CUdriverProcAddressQueryResult* symbolStatus) {
|
||||
host_layout<void*> host_data {};
|
||||
void* ptr {};
|
||||
CUresult ret {};
|
||||
for (auto& over : proc_override) {
|
||||
if (symbol == over.name) {
|
||||
ptr = over.ptr;
|
||||
if (symbolStatus) {
|
||||
*symbolStatus = (CUdriverProcAddressQueryResult)0; // CU_GET_PROC_ADDRESS_SUCCESS
|
||||
}
|
||||
ret = (CUresult)0; // CUDA_SUCCESS
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ptr) {
|
||||
ret = LDR_PTR(cuGetProcAddress_v2)(symbol, &ptr, cudaVersion, flags, symbolStatus);
|
||||
}
|
||||
|
||||
host_data.data = ptr;
|
||||
*pfn.get_pointer() = to_guest(host_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CUresult FEXFN_IMPL(cuCtxCreate_v2)(guest_layout<CUcontext*> pctx, unsigned int flags, CUdevice dev) {
|
||||
host_layout<CUcontext> host_data {};
|
||||
CUcontext ctx;
|
||||
auto ret = LDR_PTR(cuCtxCreate_v2)(&ctx, flags, dev);
|
||||
host_data.data = ctx;
|
||||
*pctx.get_pointer() = to_guest(host_data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
CUresult FEXFN_IMPL(cuGetExportTable)(guest_layout<const void**> ppExportTable, const CUuuid* pExportTableId) {
|
||||
// This function returns a pointer to an driver internal export table that is undocumented publicly.
|
||||
//
|
||||
// Some documentation about the UUIDs that has been reversed.
|
||||
// https://github.com/vosen/ZLUDA/blob/1b9ba2b2333746c5e2b05a2bf24fa6ec3828dcdf/zluda_dark_api/src/lib.rs#L197
|
||||
//
|
||||
// These two UUIDs are required by the static cuda runtime at startup. Might require more but it currently halts without these
|
||||
// implemented. UUID: [0x6b, 0xd5, 0xfb, 0x6c, 0x5b, 0xf4, 0xe7, 0x4a, 0x89, 0x87, 0xd9, 0x39, 0x12, 0xfd, 0x9d, 0xf9]
|
||||
// - cudart interface (0x68 bytes?)
|
||||
// UUID: [0xa0, 0x94, 0x79, 0x8c, 0x2e, 0x74, 0x2e, 0x74, 0x93, 0xf2, 0x08, 0x00, 0x20, 0x0c, 0x0a, 0x66]
|
||||
// - Tools runtime callback hooks (0x38 bytes?)
|
||||
|
||||
std::string uuid {};
|
||||
for (size_t i = 0; i < sizeof(CUuuid); ++i) {
|
||||
const bool last = (i + 1) == sizeof(CUuuid);
|
||||
char tmp[8];
|
||||
auto size = snprintf(tmp, 8, "0x%02x%s", pExportTableId->_0[i], last ? "" : ", ");
|
||||
uuid += std::string_view(tmp, size);
|
||||
}
|
||||
fprintf(stderr, "cuGetExportTable not implemented\n");
|
||||
fprintf(stderr, "UUID: %s\n", uuid.c_str());
|
||||
return (CUresult)4; // CUDA_ERROR_DEINITIALIZED
|
||||
}
|
||||
|
||||
EXPORTS(libcuda)
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user