Merge pull request #5536 from Sonicadvance1/105

LibraryForwarding: Implement support for CUDA
This commit is contained in:
Ryan Houdek authored and GitHub committed 2026-06-12 15:14:38 -07:00
commit e12bd27106
8 files changed
+2781

No files matched your search

+7
View File
@@ -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"
]
}
}
}
+3
View File
@@ -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")
+3
View File
@@ -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
+15
View File
@@ -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
+84
View File
@@ -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)
+81
View File
@@ -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