diff --git a/Data/ThunksDB.json b/Data/ThunksDB.json index 5e1a206def..d9786ed691 100644 --- a/Data/ThunksDB.json +++ b/Data/ThunksDB.json @@ -31,75 +31,19 @@ "@PREFIX_LIB@/x86_64-linux-gnu/libX11.so.6.4.0" ] }, - "Vulkan-radeon": { - "Library": "libvulkan_radeon-guest.so", + "Vulkan": { + "Library": "libvulkan-guest.so", "Depends": [ "xcb" ], "Overlay": [ - "@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_radeon.so" + "@PREFIX_LIB@/x86_64-linux-gnu/libvulkan.so", + "@PREFIX_LIB@/x86_64-linux-gnu/libvulkan.so.1", ], "Comment": [ "Vulkan library relies on xcb, otherwise it crashes with jemalloc" ] }, - "Vulkan-lavapipe": { - "Library": "libvulkan_lvp-guest.so", - "Depends": [ - "xcb" - ], - "Overlay": [ - "@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_lvp.so" - ] - }, - "Vulkan-freedreno": { - "Library": "libvulkan_freedreno-guest.so", - "Depends": [ - "xcb" - ], - "Overlay": [ - "@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_freedreno.so" - ] - }, - "Vulkan-intel": { - "Library": "libvulkan_intel-guest.so", - "Depends": [ - "xcb" - ], - "Overlay": [ - "@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_intel.so" - ] - }, - "Vulkan-panfrost": { - "Library": "libvulkan_panfrost-guest.so", - "Depends": [ - "xcb" - ], - "Overlay": [ - "@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_panfrost.so" - ] - }, - "Vulkan-nvidia": { - "Library": "libvulkan_nvidia-guest.so", - "Depends": [ - "xcb" - ], - "Overlay": [ - "@PREFIX_LIB@/x86_64-linux-gnu/libGLX_nvidia.so.0" - ], - "Comment": [ - "Not currently wired up" - ] - }, - "Vulkan-virtio": { - "Library": "libvulkan_virtio-guest.so", - "Depends": [ - "xcb" - ], - "Overlay": [ - "@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_virtio.so" - ] - }, "xcb": { "Library": "libxcb-guest.so", "Overlay": [ diff --git a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp index 62712fb99c..9f30806966 100644 --- a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp +++ b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp @@ -10,6 +10,7 @@ tags: glue|thunks #include #include #include +#include #include "Thunks.h" #include @@ -41,8 +42,13 @@ namespace FEXCore { std::map Thunks = { { // sha256(fex:loadlib) - { 0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80}, + { 0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80 }, &LoadLib + }, + { + // sha256(fex:link_address_to_function) + { 0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, 0xd6, 0x1f, 0x2b, 0x87, 0x8f, 0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77 }, + &LinkAddressToGuestFunction } }; @@ -56,6 +62,54 @@ namespace FEXCore { Thread->CTX->HandleCallback(Thread, (uintptr_t)callback); } + /** + * Instructs the Core to redirect calls to functions at the given + * address to another function. The original callee address is passed + * to the target function through an implicit argument stored in r11. + * + * The primary use case of this is ensuring that host function pointers + * returned from thunked APIs can safely be called by the guest. + */ + static void LinkAddressToGuestFunction(void* argsv) { + struct args_t { + uintptr_t original_callee; + uintptr_t target_addr; // Guest function to call when branching to original_callee + }; + + auto args = reinterpret_cast(argsv); + auto CTX = Thread->CTX; + + LOGMAN_THROW_A_FMT(args->original_callee, "Tried to link null pointer address to guest function"); + LOGMAN_THROW_A_FMT(args->target_addr, "Tried to link address to null pointer guest function"); + if (!CTX->Config.Is64BitMode) { + LOGMAN_THROW_A_FMT((args->original_callee >> 32) == 0, "Tried to link 64-bit address in 32-bit mode"); + LOGMAN_THROW_A_FMT((args->target_addr >> 32) == 0, "Tried to link 64-bit address in 32-bit mode"); + } + + LogMan::Msg::DFmt("Thunks: Adding trampoline from address {:#x} to guest function {:#x}", + args->original_callee, args->target_addr); + + auto Result = Thread->CTX->AddCustomIREntrypoint( + args->original_callee, + [CTX, GuestThunkEntrypoint = args->target_addr](uintptr_t Entrypoint, FEXCore::IR::IREmitter *emit) { + auto IRHeader = emit->_IRHeader(emit->Invalid(), 0); + auto Block = emit->CreateCodeNode(); + IRHeader.first->Blocks = emit->WrapNode(Block); + emit->SetCurrentCodeBlock(Block); + + const uint8_t GPRSize = CTX->GetGPRSize(); + + emit->_StoreContext(GPRSize, IR::GPRClass, emit->_Constant(Entrypoint), offsetof(Core::CPUState, gregs[X86State::REG_R11])); + emit->_ExitFunction(emit->_Constant(GuestThunkEntrypoint)); + }, CTX->ThunkHandler.get(), (void*)args->target_addr); + + if (!Result) { + if (Result.Creator != CTX->ThunkHandler.get() || Result.Data != (void*)args->target_addr) { + ERROR_AND_DIE_FMT("Input address for LinkAddressToGuestFunction is already linked elsewhere"); + } + } + } + static void LoadLib(void *ArgsV) { auto CTX = Thread->CTX; diff --git a/ThunkLibs/Generator/gen.cpp b/ThunkLibs/Generator/gen.cpp index 91d946f16b..018ea05781 100644 --- a/ThunkLibs/Generator/gen.cpp +++ b/ThunkLibs/Generator/gen.cpp @@ -1,9 +1,11 @@ #include "clang/AST/RecursiveASTVisitor.h" #include +#include #include #include #include +#include #include @@ -45,6 +47,11 @@ struct ThunkedFunction : FunctionParams { // This is implied e.g. for thunks generated for variadic functions bool custom_host_impl = false; + // If true, the unpacking function will use an extra argument for a + // host function pointer that is called instead of calling the host library + // function directly. + bool is_hostcall = false; + std::string GetOriginalFunctionName() const { const std::string suffix = "_internal"; assert(function_name.length() > suffix.size()); @@ -96,6 +103,8 @@ struct NamespaceInfo { std::string host_loader; bool generate_guest_symtable; + + bool indirect_guest_calls; }; // List of namespaces with a non-specialized fex_gen_config definition (including the global namespace, represented with an empty name) @@ -114,6 +123,7 @@ class ASTVisitor : public clang::RecursiveASTVisitor { std::optional version; std::optional load_host_endpoint_via; bool generate_guest_symtable = false; + bool indirect_guest_calls = false; }; struct Annotations { @@ -138,6 +148,8 @@ class ASTVisitor : public clang::RecursiveASTVisitor { auto annotation = base.getType().getAsString(); if (annotation == "fexgen::generate_guest_symtable") { ret.generate_guest_symtable = true; + } else if (annotation == "fexgen::indirect_guest_calls") { + ret.indirect_guest_calls = true; } else { throw Error(base.getSourceRange().getBegin(), "Unknown namespace annotation"); } @@ -231,7 +243,8 @@ class ASTVisitor : public clang::RecursiveASTVisitor { auto namespace_decl = llvm::dyn_cast(decl->getDeclContext()); namespaces.push_back({ namespace_decl ? namespace_decl->getNameAsString() : "", annotations.load_host_endpoint_via.value_or(""), - annotations.generate_guest_symtable }); + annotations.generate_guest_symtable, + annotations.indirect_guest_calls }); if (annotations.version) { if (namespace_decl) { @@ -276,7 +289,6 @@ class ASTVisitor : public clang::RecursiveASTVisitor { const auto annotations = GetAnnotations(decl); if (return_type->isFunctionPointerType() && !annotations.returns_guest_pointer) { - // TODO: Should regular pointers require annotation, too? throw Error(decl->getBeginLoc(), "Function pointer return types require explicit annotation\n"); } @@ -345,10 +357,21 @@ class ASTVisitor : public clang::RecursiveASTVisitor { // This function is thunked through an "_internal" symbol since its signature // is different from the one in the native host/guest libraries. data.function_name = data.function_name + "_internal"; - assert(!data.custom_host_impl && "Custom host impl requested but this is implied by the function signature already"); + if (data.custom_host_impl) { + throw Error(decl->getBeginLoc(), "Custom host impl requested but this is implied by the function signature already"); + } data.custom_host_impl = true; } + // For indirect calls, register a second thunk with the callee pointer as an extra argument + if (namespace_info.indirect_guest_calls) { + auto hostcall_data = data; + hostcall_data.function_name = "hostcall_" + data.function_name; + hostcall_data.param_types.push_back(context.getUIntPtrType()); + hostcall_data.is_hostcall = true; + thunks.push_back(std::move(hostcall_data)); + } + thunks.push_back(std::move(data)); return true; @@ -574,8 +597,18 @@ void GenerateThunkLibsAction::EndSourceFileAction() { } } + FunctionParams args = thunk; + auto function_to_call = "fexldr_ptr_" + libname + "_" + function_name; + if (thunk.is_hostcall) { + // Get the host function pointer by casting the last parameter to the correct signature + args.param_types.pop_back(); + function_to_call = "reinterpret_cast<" + thunk.return_type.getAsString() + "(*)(" + format_function_params(args) + ")>(args->a_" + std::to_string(args.param_types.size()) + ")"; + } else if (thunk.custom_host_impl) { + function_to_call = "fexfn_impl_" + libname + "_" + function_name; + } + file << "static void fexfn_unpack_" << libname << "_" << function_name << "(fexfn_packed_args_" << libname << "_" << function_name << "* args) {\n"; - file << (is_void ? " " : " args->rv = ") << (thunk.custom_host_impl ? "fexfn_impl_" : "fexldr_ptr_") << libname << "_" << function_name << "("; + file << (is_void ? " " : " args->rv = ") << function_to_call << "("; { auto format_param = [&](std::size_t idx) { auto cb = thunk.callbacks.find(idx); @@ -589,7 +622,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() { } }; - file << format_function_args(thunk, format_param); + file << format_function_args(args, format_param); } file << ");\n"; file << "}\n"; diff --git a/ThunkLibs/GuestLibs/CMakeLists.txt b/ThunkLibs/GuestLibs/CMakeLists.txt index 988901c529..9209535c7e 100644 --- a/ThunkLibs/GuestLibs/CMakeLists.txt +++ b/ThunkLibs/GuestLibs/CMakeLists.txt @@ -22,31 +22,21 @@ else() set(GENERATE_GUEST_INSTALL_TARGETS FALSE) endif() -# Syntax: generate(libxyz [LIBNAME libxyz-custom] libxyz-interface.cpp generator-targets...) +# Syntax: generate(libxyz libxyz-interface.cpp generator-targets...) # This defines a target and a custom command: # - custom command: Main build step that runs the thunk generator on the given interface definition # - libxyz-guest-deps: Interface target to read include directories from which are passed to libclang when parsing the interface definition -function(generate NAME) - cmake_parse_arguments(PARSE_ARGV 1 ARGS "" "LIBNAME" "") - set(ARGN ${ARGS_UNPARSED_ARGUMENTS}) - list(POP_FRONT ARGN SOURCE_FILE) - - if (ARGS_LIBNAME) - set(LIBNAME ${ARGS_LIBNAME}) - else() - set(LIBNAME ${NAME}) - endif() - +function(generate NAME SOURCE_FILE) # Interface target for the user to add include directories - add_library(${LIBNAME}-guest-deps INTERFACE) - target_include_directories(${LIBNAME}-guest-deps INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../include") + add_library(${NAME}-guest-deps INTERFACE) + target_include_directories(${NAME}-guest-deps INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../include") # Shorthand for the include directories added after calling this function. # This is not evaluated directly, hence directories added after return are still picked up - set(prop "$") + set(prop "$") # Run thunk generator for each of the given output files foreach(WHAT IN LISTS ARGN) - set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen/${LIBNAME}") + set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen/${NAME}") set(OUTFILE "${OUTFOLDER}/${WHAT}.inl") file(MAKE_DIRECTORY "${OUTFOLDER}") @@ -55,7 +45,7 @@ function(generate NAME) OUTPUT "${OUTFILE}" DEPENDS "${GENERATOR_EXE}" DEPENDS "${SOURCE_FILE}" - COMMAND "${GENERATOR_EXE}" "${SOURCE_FILE}" "${LIBNAME}" "-${WHAT}" "${OUTFILE}" -- -std=c++17 + COMMAND "${GENERATOR_EXE}" "${SOURCE_FILE}" "${NAME}" "-${WHAT}" "${OUTFILE}" -- -std=c++17 # Expand include directories to space-separated list of -isystem parameters "$<$:;-isystem$>" VERBATIM @@ -64,12 +54,10 @@ function(generate NAME) list(APPEND OUTPUTS "${OUTFILE}") endforeach() - set(GEN_${LIBNAME} ${OUTPUTS} PARENT_SCOPE) + set(GEN_${NAME} ${OUTPUTS} PARENT_SCOPE) endfunction() -add_custom_target(ThunkGuestsInstall) - -function(add_guest_lib_with_name NAME LIBNAME) +function(add_guest_lib NAME) set (SOURCE_FILE ../lib${NAME}/lib${NAME}_Guest.cpp) get_filename_component(SOURCE_FILE_ABS "${SOURCE_FILE}" ABSOLUTE) if (NOT EXISTS "${SOURCE_FILE_ABS}") @@ -80,26 +68,19 @@ function(add_guest_lib_with_name NAME LIBNAME) endif() endif() - add_library(${LIBNAME}-guest ${TARGET_TYPE} ${SOURCE_FILE} ${GEN_lib${LIBNAME}}) - target_include_directories(${LIBNAME}-guest PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/lib${LIBNAME}") - target_compile_definitions(${LIBNAME}-guest PRIVATE GUEST_THUNK_LIBRARY) - target_link_libraries(${LIBNAME}-guest PRIVATE lib${LIBNAME}-guest-deps) + add_library(${NAME}-guest ${TARGET_TYPE} ${SOURCE_FILE} ${GEN_lib${NAME}}) + target_include_directories(${NAME}-guest PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/lib${NAME}") + target_compile_definitions(${NAME}-guest PRIVATE GUEST_THUNK_LIBRARY) + target_link_libraries(${NAME}-guest PRIVATE lib${NAME}-guest-deps) - ## Tell GCC to not complain about ignored attributes - target_compile_options(${LIBNAME}-guest PRIVATE -Wno-attributes) ## Make signed overflow well defined 2's complement overflow - target_compile_options(${LIBNAME}-guest PRIVATE -fwrapv) - target_compile_options(${LIBNAME}-guest PRIVATE -DLIB_NAME=${LIBNAME} -DLIBLIB_NAME=lib${LIBNAME}) + target_compile_options(${NAME}-guest PRIVATE -fwrapv) if (GENERATE_GUEST_INSTALL_TARGETS) - install(TARGETS ${LIBNAME}-guest DESTINATION ${DATA_DIRECTORY}/GuestThunks/) + install(TARGETS ${NAME}-guest DESTINATION ${DATA_DIRECTORY}/GuestThunks/) endif() endfunction() -function(add_guest_lib NAME) - add_guest_lib_with_name(${NAME} ${NAME} ${ARGV}) -endfunction() - #add_guest_lib(fex_malloc_loader) #target_link_libraries(fex_malloc_loader-guest PRIVATE dl) @@ -112,7 +93,7 @@ add_guest_lib(asound) generate(libEGL ${CMAKE_CURRENT_SOURCE_DIR}/../libEGL/libEGL_interface.cpp thunks function_packs function_packs_public) add_guest_lib(EGL) -generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp thunks function_packs function_packs_public) +generate(libGL ${CMAKE_CURRENT_SOURCE_DIR}/../libGL/libGL_interface.cpp thunks function_packs function_packs_public symbol_list) add_guest_lib(GL) # disabled for now, headers are platform specific @@ -135,19 +116,8 @@ add_guest_lib(Xrender) generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp thunks function_packs function_packs_public) add_guest_lib(Xfixes) -set (VULKAN_LIBS - vulkan_radeon - vulkan_lvp - vulkan_freedreno - vulkan_intel - vulkan_panfrost - vulkan_virtio -) - -foreach (LIB IN LISTS VULKAN_LIBS) - generate(libvulkan_device LIBNAME lib${LIB} ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan_device/libvulkan_device_interface.cpp thunks function_packs function_packs_public symbol_list) - add_guest_lib_with_name(vulkan_device ${LIB}) -endforeach() +generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp thunks function_packs function_packs_public symbol_list) +add_guest_lib(vulkan) generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp thunks function_packs function_packs_public callback_structs callback_unpacks_header callback_unpacks_header_init callback_unpacks callback_typedefs) add_guest_lib(xcb) diff --git a/ThunkLibs/HostLibs/CMakeLists.txt b/ThunkLibs/HostLibs/CMakeLists.txt index 59c01c8135..97190c30a9 100644 --- a/ThunkLibs/HostLibs/CMakeLists.txt +++ b/ThunkLibs/HostLibs/CMakeLists.txt @@ -4,36 +4,26 @@ project(host-thunks) set(CMAKE_CXX_STANDARD 17) set (HOSTLIBS_DATA_DIRECTORY "${CMAKE_INSTALL_PREFIX}/lib/fex-emu" CACHE PATH "global data directory") -# Syntax: generate(libxyz [LIBNAME libxyz-custom] libxyz-interface.cpp generator-targets...) +# Syntax: generate(libxyz libxyz-interface.cpp generator-targets...) # This defines two targets and a custom command: # - custom command: Main build step that runs the thunk generator on the given interface definition # - libxyz-interface: Target for IDE integration (making sure libxyz-interface.cpp shows up as a source file in the project tree) # - libxyz-deps: Interface target to read include directories from which are passed to libclang when parsing the interface definition -function(generate NAME) - cmake_parse_arguments(PARSE_ARGV 1 ARGS "" "LIBNAME" "") - set(ARGN ${ARGS_UNPARSED_ARGUMENTS}) - list(POP_FRONT ARGN SOURCE_FILE) - - if (ARGS_LIBNAME) - set(LIBNAME ${ARGS_LIBNAME}) - else() - set(LIBNAME ${NAME}) - endif() - +function(generate NAME SOURCE_FILE) # Interface target for the user to add include directories - add_library(${LIBNAME}-deps INTERFACE) - target_include_directories(${LIBNAME}-deps INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../include") + add_library(${NAME}-deps INTERFACE) + target_include_directories(${NAME}-deps INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../include") # Shorthand for the include directories added after calling this function. # This is not evaluated directly, hence directories added after return are still picked up - set(prop "$") + set(prop "$") # Target for IDE integration - add_library(${LIBNAME}-interface EXCLUDE_FROM_ALL ${SOURCE_FILE}) - target_link_libraries(${LIBNAME}-interface PRIVATE ${LIBNAME}-deps) + add_library(${NAME}-interface EXCLUDE_FROM_ALL ${SOURCE_FILE}) + target_link_libraries(${NAME}-interface PRIVATE ${NAME}-deps) # Run thunk generator for each of the given output files foreach(WHAT IN LISTS ARGN) - set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen/${LIBNAME}") + set(OUTFOLDER "${CMAKE_CURRENT_BINARY_DIR}/gen/${NAME}") set(OUTFILE "${OUTFOLDER}/${WHAT}.inl") file(MAKE_DIRECTORY "${OUTFOLDER}") @@ -42,7 +32,7 @@ function(generate NAME) OUTPUT "${OUTFILE}" DEPENDS "${SOURCE_FILE}" DEPENDS thunkgen - COMMAND thunkgen "${SOURCE_FILE}" "${LIBNAME}" "-${WHAT}" "${OUTFILE}" -- -std=c++17 + COMMAND thunkgen "${SOURCE_FILE}" "${NAME}" "-${WHAT}" "${OUTFILE}" -- -std=c++17 # Expand include directories to space-separated list of -isystem parameters "$<$:;-isystem$>" VERBATIM @@ -51,10 +41,10 @@ function(generate NAME) list(APPEND OUTPUTS "${OUTFILE}") endforeach() - set(GEN_${LIBNAME} ${OUTPUTS} PARENT_SCOPE) + set(GEN_${NAME} ${OUTPUTS} PARENT_SCOPE) endfunction() -function(add_host_lib_with_name NAME LIBNAME) +function(add_host_lib NAME) set (SOURCE_FILE ../lib${NAME}/lib${NAME}_Host.cpp) get_filename_component(SOURCE_FILE_ABS "${SOURCE_FILE}" ABSOLUTE) if (NOT EXISTS "${SOURCE_FILE_ABS}") @@ -65,22 +55,17 @@ function(add_host_lib_with_name NAME LIBNAME) endif() endif() - add_library(${LIBNAME}-host SHARED ${SOURCE_FILE} ${GEN_lib${LIBNAME}}) - target_include_directories(${LIBNAME}-host PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/lib${LIBNAME}") - target_link_libraries(${LIBNAME}-host PRIVATE dl) - target_link_libraries(${LIBNAME}-host PRIVATE lib${LIBNAME}-deps) + add_library(${NAME}-host SHARED ${SOURCE_FILE} ${GEN_lib${NAME}}) + target_include_directories(${NAME}-host PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/gen/lib${NAME}") + target_link_libraries(${NAME}-host PRIVATE dl) + target_link_libraries(${NAME}-host PRIVATE lib${NAME}-deps) ## Make signed overflow well defined 2's complement overflow - target_compile_options(${LIBNAME}-host PRIVATE -fwrapv) - target_compile_options(${LIBNAME}-host PRIVATE -DLIB_NAME=${LIBNAME} -DLIBLIB_NAME=lib${LIBNAME}) + target_compile_options(${NAME}-host PRIVATE -fwrapv) # generated files forward-declare functions that need to be implemented manually, so pass --no-undefined to make sure errors are detected at compile-time rather than runtime - target_link_options(${LIBNAME}-host PRIVATE "LINKER:--no-undefined") + target_link_options(${NAME}-host PRIVATE "LINKER:--no-undefined") - install(TARGETS ${LIBNAME}-host DESTINATION ${HOSTLIBS_DATA_DIRECTORY}/HostThunks/) -endfunction() - -function(add_host_lib NAME) - add_host_lib_with_name(${NAME} ${NAME} ${ARGV}) + install(TARGETS ${NAME}-host DESTINATION ${HOSTLIBS_DATA_DIRECTORY}/HostThunks/) endfunction() #add_host_lib(fex_malloc_symbols) @@ -118,19 +103,8 @@ add_host_lib(Xrender) generate(libXfixes ${CMAKE_CURRENT_SOURCE_DIR}/../libXfixes/libXfixes_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs) add_host_lib(Xfixes) -set (VULKAN_LIBS - vulkan_radeon - vulkan_lvp - vulkan_freedreno - vulkan_intel - vulkan_panfrost - vulkan_virtio -) - -foreach (LIB IN LISTS VULKAN_LIBS) - generate(libvulkan_device LIBNAME lib${LIB} ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan_device/libvulkan_device_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs symbol_list) - add_host_lib_with_name(vulkan_device ${LIB}) -endforeach() +generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs symbol_list) +add_host_lib(vulkan) generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp function_unpacks tab_function_unpacks ldr ldr_ptrs callback_structs callback_unpacks_header callback_typedefs) add_host_lib(xcb) diff --git a/ThunkLibs/include/common/GeneratorInterface.h b/ThunkLibs/include/common/GeneratorInterface.h index 3aa22737c5..f3cd082db9 100644 --- a/ThunkLibs/include/common/GeneratorInterface.h +++ b/ThunkLibs/include/common/GeneratorInterface.h @@ -4,6 +4,7 @@ struct custom_host_impl {}; struct custom_guest_entrypoint {}; struct generate_guest_symtable {}; +struct indirect_guest_calls {}; struct callback_annotation_base { // Prevent annotating multiple callback strategies diff --git a/ThunkLibs/include/common/Guest.h b/ThunkLibs/include/common/Guest.h index 29775f6cfd..2e3deac788 100644 --- a/ThunkLibs/include/common/Guest.h +++ b/ThunkLibs/include/common/Guest.h @@ -1,5 +1,6 @@ #pragma once #include +#include #ifndef _M_ARM_64 #define MAKE_THUNK(lib, name, hash) \ @@ -30,35 +31,144 @@ struct LoadlibArgs { uintptr_t CallbackThunks; }; -#define LOAD_LIB(name) \ +#define LOAD_LIB_BASE(name, callback_unpacks, init_fn) \ MAKE_THUNK(fex, loadlib, "0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80") \ + MAKE_THUNK(fex, link_address_to_function, "0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, 0xd6, 0x1f, 0x2b, 0x87, 0x8f, 0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77") \ __attribute__((constructor)) static void loadlib() \ { \ - LoadlibArgs args = { #name, 0 }; \ + LoadlibArgs args = { #name, (uintptr_t)(callback_unpacks) }; \ fexthunks_fex_loadlib(&args); \ - } -#define LOAD_LIB_INIT(name, init_fn) \ - MAKE_THUNK(fex, loadlib, "0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80") \ - __attribute__((constructor)) static void loadlib() \ - { \ - LoadlibArgs args = { #name, 0 }; \ - fexthunks_fex_loadlib(&args); \ - init_fn (); \ + if ((init_fn)) ((void(*)())init_fn)(); \ } -#define LOAD_LIB_WITH_CALLBACKS(name) \ - MAKE_THUNK(fex, loadlib, "0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80") \ - __attribute__((constructor)) static void loadlib() \ - { \ - LoadlibArgs args = { #name, (uintptr_t)&callback_unpacks }; \ - fexthunks_fex_loadlib(&args); \ - } +#define LOAD_LIB(name) LOAD_LIB_BASE(name, nullptr, nullptr) +#define LOAD_LIB_INIT(name, init_fn) LOAD_LIB_BASE(name, nullptr, init_fn) +#define LOAD_LIB_WITH_CALLBACKS(name) LOAD_LIB_BASE(name, &callback_unpacks, nullptr) +#define LOAD_LIB_WITH_CALLBACKS_INIT(name, init_fn) LOAD_LIB_BASE(name, (uintptr_t)&callback_unpacks, init_fn) -#define LOAD_LIB_WITH_CALLBACKS_INIT(name, init_fn) \ - MAKE_THUNK(fex, loadlib, "0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80") \ - __attribute__((constructor)) static void loadlib() \ - { \ - LoadlibArgs args = { #name, (uintptr_t)&callback_unpacks }; \ - fexthunks_fex_loadlib(&args); \ - init_fn (); \ - } +extern "C" int fexthunks_fex_link_address_to_function(void*); +inline void LinkAddressToFunction(uintptr_t addr, uintptr_t target) { + struct args_t { + uintptr_t original_callee; + uintptr_t target_addr; // Function to call when branching to replaced_addr + }; + args_t args = { addr, target }; + fexthunks_fex_link_address_to_function(&args); +} + +template +struct PackedArguments; + +template +struct PackedArguments { A0 a0; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; R rv; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; R rv; }; + +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; R rv; }; + +template +struct PackedArguments { + A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; + A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; A18 a18; A19 a19; + A20 a20; A21 a21; A22 a22; A23 a23; R rv; +}; + +template +struct PackedArguments { A0 a0; }; +template +struct PackedArguments { A0 a0; A1 a1; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; }; +template +struct PackedArguments { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; }; + +// Helper template that packs the given arguments and invokes a thunk at the +// address stored in the `r11` guest register. The signature of the thunk must +// be specified at compile-time via the Thunk template parameter. +// Other than reading the thunk address from `r11`, this is equivalent to the +// fexfn_pack_* functions generated for global API functions. +template +inline Result CallHostThunkFromRuntimePointer(Args... args) { +#ifndef _M_ARM_64 + uintptr_t host_addr; + asm("mov %%r11, %0" : "=r" (host_addr)); +#else + uintptr_t host_addr = 0; +#endif + + PackedArguments packed_args = { + args..., + host_addr + // Return value not explicitly initialized since an initializer would fail to compile for the void case + }; + + Thunk(reinterpret_cast(&packed_args)); + + if constexpr (!std::is_void_v) { + return packed_args.rv; + } +} + +// Convenience wrapper that returns the function pointer to a +// CallHostThunkFromRuntimePointer instantiation matching the function +// signature of `host_func` +template +static auto GetCallerForHostThunkFromRuntimePointer(Result (*host_func)(Args...)) + -> Result(*)(Args...) { + return CallHostThunkFromRuntimePointer; +} diff --git a/ThunkLibs/libGL/libGL_Guest.cpp b/ThunkLibs/libGL/libGL_Guest.cpp index c9d8609c75..05fe827f37 100644 --- a/ThunkLibs/libGL/libGL_Guest.cpp +++ b/ThunkLibs/libGL/libGL_Guest.cpp @@ -8,36 +8,56 @@ desc: Handles glXGetProcAddress #define GL_GLEXT_PROTOTYPES 1 #define GLX_GLXEXT_PROTOTYPES 1 -#include "glcorearb.h" - #include #include #include #include + +#undef GL_ARB_viewport_array +#include "glcorearb.h" + #include -#include +#include +#include +#include +#include #include "common/Guest.h" #include "thunks.inl" #include "function_packs.inl" #include "function_packs_public.inl" +#include "symbol_list.inl" typedef void voidFunc(); +// Maps OpenGL API function names to the address of a guest function which is +// linked to the corresponding host function pointer +const std::unordered_map HostPtrInvokers = + std::invoke([]() { +#define PAIR(name, unused) Ret[#name] = reinterpret_cast(GetCallerForHostThunkFromRuntimePointer(name)); + std::unordered_map Ret; + FOREACH_internal_SYMBOL(PAIR) + return Ret; +#undef PAIR + }); + extern "C" { voidFunc *glXGetProcAddress(const GLubyte *procname) { + auto Ret = fexfn_pack_glXGetProcAddress(procname); + if (!Ret) { + return nullptr; + } - for (int i = 0; internal_symtable[i].name; i++) { - if (strcmp(internal_symtable[i].name, (const char*)procname) == 0) { - // for debugging - //printf("glXGetProcAddress: looked up %s %s %p %p\n", procname, internal_symtable[i].name, internal_symtable[i].fn, &glXGetProcAddress); - return internal_symtable[i].fn; - } - } + auto TargetFuncIt = HostPtrInvokers.find(reinterpret_cast(procname)); + if (TargetFuncIt == HostPtrInvokers.end()) { + // Extension found in host but not in our interface definition => treat as fatal error + fprintf(stderr, "glXGetProcAddress: not found %s\n", procname); + __builtin_trap(); + } - printf("glXGetProcAddress: not found %s\n", procname); - return nullptr; + LinkAddressToFunction((uintptr_t)Ret, TargetFuncIt->second); + return Ret; } voidFunc *glXGetProcAddressARB(const GLubyte *procname) { @@ -45,4 +65,4 @@ extern "C" { } } -LOAD_LIB(libGL) \ No newline at end of file +LOAD_LIB(libGL) diff --git a/ThunkLibs/libGL/libGL_Host.cpp b/ThunkLibs/libGL/libGL_Host.cpp index 18c9d6e44c..85fbc81698 100644 --- a/ThunkLibs/libGL/libGL_Host.cpp +++ b/ThunkLibs/libGL/libGL_Host.cpp @@ -18,6 +18,8 @@ desc: Uses glXGetProcAddress instead of dlsym #include #include +#include + #include "common/Host.h" void fexfn_impl_libGL_glDebugMessageCallbackAMD_internal(GLDEBUGPROCAMD, const void*) { diff --git a/ThunkLibs/libGL/libGL_interface.cpp b/ThunkLibs/libGL/libGL_interface.cpp index 459bf4d44f..1822cbc0d1 100644 --- a/ThunkLibs/libGL/libGL_interface.cpp +++ b/ThunkLibs/libGL/libGL_interface.cpp @@ -15,6 +15,14 @@ template struct fex_gen_config { }; +template<> struct fex_gen_config : fexgen::custom_guest_entrypoint, fexgen::returns_guest_pointer {}; + +// Symbols queryable through glXGetProcAddr +namespace internal { +template +struct fex_gen_config : fexgen::generate_guest_symtable, fexgen::indirect_guest_calls { +}; + template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -93,13 +101,6 @@ template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; -// Symbols exposed through glXGetProcAddr -namespace internal { -template -struct fex_gen_config : fexgen::generate_guest_symtable { - const char* load_host_endpoint_via = "symbolFromGlXGetProcAddr"; -}; - template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -696,9 +697,9 @@ template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; -template<> struct fex_gen_config {}; -template<> struct fex_gen_config {}; -template<> struct fex_gen_config {}; +template<> struct fex_gen_config : fexgen::callback_stub {}; +template<> struct fex_gen_config : fexgen::callback_stub {}; +template<> struct fex_gen_config : fexgen::callback_stub {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; diff --git a/ThunkLibs/libvulkan/Guest.cpp b/ThunkLibs/libvulkan/Guest.cpp new file mode 100644 index 0000000000..1d3d428463 --- /dev/null +++ b/ThunkLibs/libvulkan/Guest.cpp @@ -0,0 +1,73 @@ +/* +$info$ +tags: thunklibs|Vulkan +$end_info$ +*/ + +#define VK_USE_PLATFORM_XLIB_XRANDR_EXT +#define VK_USE_PLATFORM_XLIB_KHR +#define VK_USE_PLATFORM_XCB_KHR +#define VK_USE_PLATFORM_WAYLAND_KHR +#include + +#include "common/Guest.h" + +#include +#include +#include +#include +#include + +#include "thunks.inl" +#include "function_packs.inl" +#include "function_packs_public.inl" +#include "symbol_list.inl" + +extern "C" { + +// Maps Vulkan API function names to the address of a guest function which is +// linked to the corresponding host function pointer +const std::unordered_map HostPtrInvokers = + std::invoke([]() { +#define PAIR(name, unused) Ret[#name] = reinterpret_cast(GetCallerForHostThunkFromRuntimePointer(name)); + std::unordered_map Ret; + FOREACH_internal_SYMBOL(PAIR) + return Ret; +#undef PAIR + }); + +PFN_vkVoidFunction vkGetDeviceProcAddr(VkDevice a_0,const char* a_1){ + auto Ret = fexfn_pack_vkGetDeviceProcAddr(a_0, a_1); + if (!Ret) { + return nullptr; + } + auto It = HostPtrInvokers.find(a_1); + if (It == HostPtrInvokers.end() || !It->second) { + fprintf(stderr, "\tvkGetDeviceProcAddr: Couldn't find Guest symbol: '%s'\n", a_1); + __builtin_trap(); + } + LinkAddressToFunction((uintptr_t)Ret, It->second); + return Ret; +} + +PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance a_0,const char* a_1){ + if (a_1 == std::string_view { "vkGetDeviceProcAddr" }) { + return (PFN_vkVoidFunction)vkGetDeviceProcAddr; + } else { + auto Ret = fexfn_pack_vkGetInstanceProcAddr(a_0, a_1); + if (!Ret) { + return nullptr; + } + auto It = HostPtrInvokers.find(a_1); + if (It == HostPtrInvokers.end() || !It->second) { + fprintf(stderr, "\tvkGetInstanceProcAddr: Couldn't find Guest symbol: '%s'\n", a_1); + __builtin_trap(); + } + LinkAddressToFunction((uintptr_t)Ret, It->second); + return Ret; + } +} + +} + +LOAD_LIB(libvulkan) diff --git a/ThunkLibs/libvulkan/Host.cpp b/ThunkLibs/libvulkan/Host.cpp new file mode 100644 index 0000000000..52776ce8ae --- /dev/null +++ b/ThunkLibs/libvulkan/Host.cpp @@ -0,0 +1,131 @@ +/* +$info$ +tags: thunklibs|Vulkan +$end_info$ +*/ + +#define VK_USE_PLATFORM_XLIB_XRANDR_EXT +#define VK_USE_PLATFORM_XLIB_KHR +#define VK_USE_PLATFORM_XCB_KHR +#define VK_USE_PLATFORM_WAYLAND_KHR +#include + +#include "common/Host.h" + +#include +#include +#include +#include + +#include + +#include "ldr_ptrs.inl" + +static bool SetupInstance{}; +static std::mutex SetupMutex{}; + +#define LDR_PTR(fn) fexldr_ptr_libvulkan_##fn + +static void DoSetupWithInstance(VkInstance instance) { + std::unique_lock lk {SetupMutex}; + + // Needed since the Guest-endpoint calls without a function pointer + // TODO: Support use of multiple instances + (void*&)LDR_PTR(vkGetDeviceProcAddr) = (void*)LDR_PTR(vkGetInstanceProcAddr)(instance, "vkGetDeviceProcAddr"); + if (LDR_PTR(vkGetDeviceProcAddr) == nullptr) { + std::abort(); + } + + // Query pointers for functions customized below + (void*&)LDR_PTR(vkCreateInstance) = (void*)LDR_PTR(vkGetInstanceProcAddr)(instance, "vkCreateInstance"); + (void*&)LDR_PTR(vkCreateDevice) = (void*)LDR_PTR(vkGetInstanceProcAddr)(instance, "vkCreateDevice"); + + // Only do this lookup once. + // NOTE: If vkGetInstanceProcAddr was called with a null instance, only a few function pointers will be filled with non-null values, so we do repeat the lookup in that case + if (instance) { + SetupInstance = true; + } +} + +#define FEXFN_IMPL(fn) fexfn_impl_libvulkan_##fn + +// Functions with callbacks are overridden to ignore the guest-side callbacks + +static VkResult FEXFN_IMPL(vkCreateShaderModule)(VkDevice a_0, const VkShaderModuleCreateInfo* a_1, const VkAllocationCallbacks* a_2, VkShaderModule* a_3) { + (void*&)LDR_PTR(vkCreateShaderModule) = (void*)LDR_PTR(vkGetDeviceProcAddr)(a_0, "vkCreateShaderModule"); + return LDR_PTR(vkCreateShaderModule)(a_0, a_1, nullptr, a_3); +} + +static VkResult FEXFN_IMPL(vkCreateInstance)(const VkInstanceCreateInfo* a_0, const VkAllocationCallbacks* a_1, VkInstance* a_2) { + return LDR_PTR(vkCreateInstance)(a_0, nullptr, a_2); +} + +static VkResult FEXFN_IMPL(vkCreateDevice)(VkPhysicalDevice a_0, const VkDeviceCreateInfo* a_1, const VkAllocationCallbacks* a_2, VkDevice* a_3){ + return LDR_PTR(vkCreateDevice)(a_0, a_1, nullptr, a_3); +} + +static VkResult FEXFN_IMPL(vkAllocateMemory)(VkDevice a_0, const VkMemoryAllocateInfo* a_1, const VkAllocationCallbacks* a_2, VkDeviceMemory* a_3){ + (void*&)LDR_PTR(vkAllocateMemory) = (void*)LDR_PTR(vkGetDeviceProcAddr)(a_0, "vkAllocateMemory"); + return LDR_PTR(vkAllocateMemory)(a_0, a_1, nullptr, a_3); +} + +static void FEXFN_IMPL(vkFreeMemory)(VkDevice a_0, VkDeviceMemory a_1, const VkAllocationCallbacks* a_2) { + (void*&)LDR_PTR(vkFreeMemory) = (void*)LDR_PTR(vkGetDeviceProcAddr)(a_0, "vkFreeMemory"); + LDR_PTR(vkFreeMemory)(a_0, a_1, nullptr); +} + +static VkResult FEXFN_IMPL(vkEnumerateInstanceExtensionProperties)(const char* a_0, uint32_t* a_1, VkExtensionProperties* a_2) { + auto ret = LDR_PTR(vkEnumerateInstanceExtensionProperties)(a_0, a_1, a_2); + if (a_2) { + const auto end = a_2 + *a_1; + auto it = std::remove_if(a_2, end, [](const VkExtensionProperties& prop) { return strcmp(prop.extensionName, "VK_EXT_debug_report") == 0 || strcmp(prop.extensionName, "VK_EXT_debug_utils") == 0; }); + // Replace by dummy entry and reduce extension count + std::fill(it, end, VkExtensionProperties {}); + *a_1 -= (end - it); + } + return ret; +} + +static PFN_vkVoidFunction FEXFN_IMPL(vkGetDeviceProcAddr)(VkDevice a_0, const char* a_1) { + // Just return the host facing function pointer + // The guest will handle mapping if this exists + + // Check for functions with stubbed callbacks first + if (std::strcmp(a_1, "vkCreateShaderModule") == 0) { + return (PFN_vkVoidFunction)fexfn_impl_libvulkan_vkCreateShaderModule; + } else if (std::strcmp(a_1, "vkCreateInstance") == 0) { + return (PFN_vkVoidFunction)fexfn_impl_libvulkan_vkCreateInstance; + } else if (std::strcmp(a_1, "vkCreateDevice") == 0) { + return (PFN_vkVoidFunction)fexfn_impl_libvulkan_vkCreateDevice; + } else if (std::strcmp(a_1, "vkAllocateMemory") == 0) { + return (PFN_vkVoidFunction)fexfn_impl_libvulkan_vkAllocateMemory; + } else if (std::strcmp(a_1, "vkFreeMemory") == 0) { + return (PFN_vkVoidFunction)fexfn_impl_libvulkan_vkFreeMemory; + } + + auto ret = LDR_PTR(vkGetDeviceProcAddr)(a_0, a_1); + return ret; +} + +static PFN_vkVoidFunction FEXFN_IMPL(vkGetInstanceProcAddr)(VkInstance a_0, const char* a_1) { + if (!SetupInstance && a_0) { + DoSetupWithInstance(a_0); + } + + // Just return the host facing function pointer + // The guest will handle mapping if it exists + auto ret = LDR_PTR(vkGetInstanceProcAddr)(a_0, a_1); + return ret; +} + + +#include "function_unpacks.inl" + +static ExportEntry exports[] = { + #include "tab_function_unpacks.inl" + { nullptr, nullptr } +}; + +#include "ldr.inl" + +EXPORTS(libvulkan) diff --git a/ThunkLibs/libvulkan_device/libvulkan_device_interface.cpp b/ThunkLibs/libvulkan/libvulkan_interface.cpp similarity index 99% rename from ThunkLibs/libvulkan_device/libvulkan_device_interface.cpp rename to ThunkLibs/libvulkan/libvulkan_interface.cpp index 149cfb1bb8..4b25841e49 100644 --- a/ThunkLibs/libvulkan_device/libvulkan_device_interface.cpp +++ b/ThunkLibs/libvulkan/libvulkan_interface.cpp @@ -9,6 +9,16 @@ struct fex_gen_config; #define VK_USE_PLATFORM_WAYLAND_KHR #include +template<> struct fex_gen_config : fexgen::custom_host_impl, fexgen::custom_guest_entrypoint, fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::custom_host_impl, fexgen::custom_guest_entrypoint, fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::custom_host_impl {}; + +namespace internal { + +template +struct fex_gen_config : fexgen::generate_guest_symtable, fexgen::indirect_guest_calls { +}; + template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -246,7 +256,6 @@ template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; -template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -282,7 +291,6 @@ template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; -template<> struct fex_gen_config : fexgen::custom_host_impl, fexgen::custom_guest_entrypoint, fexgen::returns_guest_pointer {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -304,7 +312,6 @@ template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; -template<> struct fex_gen_config : fexgen::custom_host_impl, fexgen::custom_guest_entrypoint, fexgen::returns_guest_pointer {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -429,3 +436,5 @@ template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; + +} // namespace internal diff --git a/ThunkLibs/libvulkan_device/Guest.cpp b/ThunkLibs/libvulkan_device/Guest.cpp deleted file mode 100644 index a585421659..0000000000 --- a/ThunkLibs/libvulkan_device/Guest.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/* -$info$ -tags: thunklibs|Vulkan -$end_info$ -*/ - -#define VK_USE_PLATFORM_XLIB_XRANDR_EXT -#define VK_USE_PLATFORM_XLIB_KHR -#define VK_USE_PLATFORM_XCB_KHR -#define VK_USE_PLATFORM_WAYLAND_KHR -#include - -#include "common/Guest.h" - -#include -#include -#include -#include - -#include "thunks.inl" -#include "function_packs.inl" -#include "function_packs_public.inl" -#include "symbol_list.inl" - -extern "C" { -static bool Setup{}; -static std::unordered_map PtrsToLookUp{}; - -// Setup can't be done on shared library constructor -// Needs to be deferred until post-constructor phase to remove the chance of crashing -static void DoSetup() { - // Initialize unordered_map from generated initializer-list - PtrsToLookUp = { -#define PAIR(name, unused) { #name, (PFN_vkVoidFunction*)name }, -FOREACH_SYMBOL(PAIR) -#undef PAIR - }; - - Setup = true; -} - -PFN_vkVoidFunction vkGetDeviceProcAddr(VkDevice a_0,const char* a_1){ - if (!Setup) { - DoSetup(); - } - - auto ret = fexfn_pack_vkGetDeviceProcAddr(a_0, a_1); - - if (ret == nullptr) { - // Early out if our instance doesn't have the pointer - // Definitely means we don't support it - return nullptr; - } - - // Okay, we found a host side function for this - // Now return our local instance of this function - auto It = PtrsToLookUp.find(a_1); - if (It == PtrsToLookUp.end() || !It->second) { - fprintf(stderr, "\tvkGetDeviceProcAddr: Couldn't find Guest symbol: '%s'\n", a_1); - __builtin_trap(); - } - return (PFN_vkVoidFunction)It->second; -} - -PFN_vkVoidFunction vkGetInstanceProcAddr(VkInstance a_0,const char* a_1){ - if (!Setup) { - DoSetup(); - } - - // Search our host install first to see if the pointer exists - // This also populates a map on the host facing side - auto ret = fexfn_pack_vkGetInstanceProcAddr(a_0, a_1); - if (ret == nullptr) { - // Early out if our instance doesn't have the pointer - // Definitely means we don't support it - return nullptr; - } - - auto It = PtrsToLookUp.find(a_1); - if (It == PtrsToLookUp.end() || !It->second) { - fprintf(stderr, "\tvkGetInstanceProcAddr: Couldn't find Guest symbol: '%s'\n", a_1); - __builtin_trap(); - } - return (PFN_vkVoidFunction)It->second; -} - -} - -#define DOLOAD(name) LOAD_LIB(name) -DOLOAD(LIBLIB_NAME) diff --git a/ThunkLibs/libvulkan_device/Host.cpp b/ThunkLibs/libvulkan_device/Host.cpp deleted file mode 100644 index 9469cd882e..0000000000 --- a/ThunkLibs/libvulkan_device/Host.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* -$info$ -tags: thunklibs|Vulkan -$end_info$ -*/ - -#define VK_USE_PLATFORM_XLIB_XRANDR_EXT -#define VK_USE_PLATFORM_XLIB_KHR -#define VK_USE_PLATFORM_XCB_KHR -#define VK_USE_PLATFORM_WAYLAND_KHR -#include - -#include "common/Host.h" - -#include -#include - -#include - -#include "ldr_ptrs.inl" - -#include "symbol_list.inl" - -static bool SetupDev{}; -static bool SetupInstance{}; -std::mutex SetupMutex{}; - -static std::unordered_map PtrsToLookUp{}; - - -#define CONCAT(a, b, c) a##_##b##_##c -#define EVAL(a, b) CONCAT(fexldr_ptr, a, b) -#define LDR_PTR(fn) EVAL(LIBLIB_NAME, fn) - -static void DoSetupWithDevice(VkDevice dev) { - std::unique_lock lk {SetupMutex}; - - auto add_ptr = [&](const char* name) { - auto Lookup = PtrsToLookUp.find(name); - if (Lookup != PtrsToLookUp.end() && *Lookup->second == nullptr) { - auto Res = LDR_PTR(vkGetDeviceProcAddr)(dev, name); - if (Res) { - *Lookup->second = Res; - } - } - }; - -#define PAIR(name, unused) add_ptr(#name); -FOREACH_SYMBOL(PAIR) -#undef PAIR - - SetupDev = true; -} - -static void DoSetupWithInstance(VkInstance instance) { - std::unique_lock lk {SetupMutex}; - - auto add_ptr = [&](const char* name) { - auto Lookup = PtrsToLookUp.find(name); - auto Res = LDR_PTR(vkGetInstanceProcAddr)(instance, name); - if (Res) { - *Lookup->second = Res; - } - }; - -#define PAIR(name, unused) add_ptr(#name); -FOREACH_SYMBOL(PAIR); -#undef PAIR - - // Only do this lookup once. - // NOTE: If vkGetInstanceProcAddr was called with a null instance, only a few function pointers will be filled with non-null values, so we do repeat the lookup in that case - if (instance) { - SetupInstance = true; - } -} - -#define FEXFN_IMPL3(a, b, c) a##_##b##_##c -#define FEXFN_IMPL2(a, b) FEXFN_IMPL3(fexfn_impl, a, b) -#define FEXFN_IMPL(fn) FEXFN_IMPL2(LIBLIB_NAME, fn) - -static PFN_vkVoidFunction FEXFN_IMPL(vkGetDeviceProcAddr)(VkDevice a_0, const char* a_1) { - if (!SetupDev) { - DoSetupWithDevice(a_0); - } - - // Just return the host facing function pointer - // The guest will handle mapping if this exists - auto ret = LDR_PTR(vkGetDeviceProcAddr)(a_0, a_1); - return ret; -} - -static PFN_vkVoidFunction FEXFN_IMPL(vkGetInstanceProcAddr)(VkInstance a_0, const char* a_1) { - if (!SetupInstance) { - DoSetupWithInstance(a_0); - } - - // Just return the host facing function pointer - // The guest will handle mapping if it exists - auto ret = LDR_PTR(vkGetInstanceProcAddr)(a_0, a_1); - return ret; -} - -static VkResult FEXFN_IMPL(vkCreateShaderModule)(VkDevice a_0, const VkShaderModuleCreateInfo* a_1, const VkAllocationCallbacks* a_2, VkShaderModule* a_3) { - return LDR_PTR(vkCreateShaderModule)(a_0, a_1, nullptr, a_3); -} - -static VkResult FEXFN_IMPL(vkCreateInstance)(const VkInstanceCreateInfo* a_0, const VkAllocationCallbacks* a_1, VkInstance* a_2) { - return LDR_PTR(vkCreateInstance)(a_0, nullptr, a_2); -} - -static VkResult FEXFN_IMPL(vkCreateDevice)(VkPhysicalDevice a_0, const VkDeviceCreateInfo* a_1, const VkAllocationCallbacks* a_2, VkDevice* a_3){ - return LDR_PTR(vkCreateDevice)(a_0, a_1, nullptr, a_3); -} - -static VkResult FEXFN_IMPL(vkAllocateMemory)(VkDevice a_0, const VkMemoryAllocateInfo* a_1, const VkAllocationCallbacks* a_2, VkDeviceMemory* a_3){ - return LDR_PTR(vkAllocateMemory)(a_0, a_1, nullptr, a_3); -} - -static void FEXFN_IMPL(vkFreeMemory)(VkDevice a_0, VkDeviceMemory a_1, const VkAllocationCallbacks* a_2) { - LDR_PTR(vkFreeMemory)(a_0, a_1, nullptr); -} - - -#include "function_unpacks.inl" - -static ExportEntry exports[] = { - #include "tab_function_unpacks.inl" - { nullptr, nullptr } -}; - -static void DoSetup() { - // Initialize unordered_map from generated initializer-list -#define PAIR(name, unused) { #name, (PFN_vkVoidFunction*)&LDR_PTR(name) }, - PtrsToLookUp = { - FOREACH_SYMBOL(PAIR) - }; -#undef PAIR -} - -#include "ldr.inl" - -#define LDR_HANDLE3(a) fexldr_ptr##_##a##_##so -#define LDR_HANDLE2(a) LDR_HANDLE3(a) -#define LDR_HANDLE LDR_HANDLE2(LIBLIB_NAME) - -static void init_func() { - // Initialize some initial pointers that we can get while loading - (void*&)LDR_PTR(vkGetInstanceProcAddr) = dlsym(LDR_HANDLE, "vkGetInstanceProcAddr"); - if (LDR_PTR(vkGetInstanceProcAddr) == nullptr) { - (void*&)LDR_PTR(vkGetInstanceProcAddr) = dlsym(LDR_HANDLE, "vk_icdGetInstanceProcAddr"); - } - - (void*&)LDR_PTR(vkEnumerateInstanceVersion) = (void *) LDR_PTR(vkGetInstanceProcAddr)(nullptr, "vkEnumerateInstanceVersion"); - (void*&)LDR_PTR(vkEnumerateInstanceExtensionProperties) = (void *) LDR_PTR(vkGetInstanceProcAddr)(nullptr, "vkEnumerateInstanceExtensionProperties"); - (void*&)LDR_PTR(vkEnumerateInstanceLayerProperties) = (void*) LDR_PTR(vkGetInstanceProcAddr)(nullptr, "vkEnumerateInstanceLayerProperties"); - (void*&)LDR_PTR(vkCreateInstance) = (void*) LDR_PTR(vkGetInstanceProcAddr)(nullptr, "vkCreateInstance"); - - DoSetup(); -} - -#define DOEXPORT_INIT(name, init) EXPORTS_INIT(name, init) -DOEXPORT_INIT(LIBLIB_NAME, init_func)