From 4d404ea94d28c82c3108ec04867b5efa060e2d91 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 6 Aug 2022 12:25:36 +0200 Subject: [PATCH 01/18] Thunks: Fix tests --- unittests/ThunkLibs/generator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/ThunkLibs/generator.cpp b/unittests/ThunkLibs/generator.cpp index f85e78e34..1a421c9a4 100644 --- a/unittests/ThunkLibs/generator.cpp +++ b/unittests/ThunkLibs/generator.cpp @@ -321,7 +321,7 @@ SourceWithAST Fixture::run_thunkgen_host(std::string_view prelude, std::string_v " using args_t = typename function_traits::arg_t;\n" " return Fn(reinterpret_cast(argsv));\n" "}\n" - "#define LOAD_INTERNAL_GUESTPTR_ARG(arg)\n" + "#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(arg)\n" "struct GuestcallInfo {\n" " uintptr_t HostPacker;\n" " void (*CallCallback)(uintptr_t, uintptr_t, void*);\n" From 7c6d49155c614f2b24373d8a6402f4a44d440cd3 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 5 Aug 2022 10:47:46 +0200 Subject: [PATCH 02/18] Thunks: Define GUEST_THUNK_LIBRARY for guest-side thunkgen invocations --- ThunkLibs/GuestLibs/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ThunkLibs/GuestLibs/CMakeLists.txt b/ThunkLibs/GuestLibs/CMakeLists.txt index 77d65100d..03d52b0b2 100644 --- a/ThunkLibs/GuestLibs/CMakeLists.txt +++ b/ThunkLibs/GuestLibs/CMakeLists.txt @@ -40,7 +40,7 @@ function(generate NAME SOURCE_FILE) OUTPUT "${OUTFILE}" DEPENDS "${GENERATOR_EXE}" DEPENDS "${SOURCE_FILE}" - COMMAND "${GENERATOR_EXE}" "${SOURCE_FILE}" "${NAME}" "-${WHAT}" "${OUTFILE}" -- -std=c++17 + COMMAND "${GENERATOR_EXE}" "${SOURCE_FILE}" "${NAME}" "-${WHAT}" "${OUTFILE}" -- -std=c++17 -DGUEST_THUNK_LIBRARY # Expand include directories to space-separated list of -isystem parameters "$<$:;-isystem$>" VERBATIM From 737e76968a55f37bc671fd6f70e28ea761218dff Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 29 Jul 2022 18:21:02 +0200 Subject: [PATCH 03/18] Thunks/X11: Add more symbols --- ThunkLibs/libX11/libX11_Guest.cpp | 21 +++++++++ ThunkLibs/libX11/libX11_Host.cpp | 27 ++++++++++- ThunkLibs/libX11/libX11_interface.cpp | 65 +++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 1 deletion(-) diff --git a/ThunkLibs/libX11/libX11_Guest.cpp b/ThunkLibs/libX11/libX11_Guest.cpp index 0df8e060e..8c66d91fc 100644 --- a/ThunkLibs/libX11/libX11_Guest.cpp +++ b/ThunkLibs/libX11/libX11_Guest.cpp @@ -9,6 +9,8 @@ $end_info$ #include #include +#include + // Include Xlibint.h and undefine some of its macros that clash with the standard library #include #undef min @@ -54,6 +56,25 @@ extern "C" { return rv; } + char* XGetIMValues(XIM ic, ...) { + fprintf(stderr, "XGetIMValues\n"); + va_list ap; + std::vector args; + va_start(ap, ic); + for (;;) { + auto arg = va_arg(ap, void*); + if (arg == 0) + break; + args.push_back(arg); + fprintf(stderr, "%p\n", arg); + } + + va_end(ap); + auto rv = fexfn_pack_XGetIMValues_internal(ic, args.size(), &args[0]); + fprintf(stderr, "RV: %p\n", rv); + return rv; + } + _XIC* XCreateIC(XIM im, ...) { fprintf(stderr, "XCreateIC\n"); va_list ap; diff --git a/ThunkLibs/libX11/libX11_Host.cpp b/ThunkLibs/libX11/libX11_Host.cpp index a5a635d30..f873424a0 100644 --- a/ThunkLibs/libX11/libX11_Host.cpp +++ b/ThunkLibs/libX11/libX11_Host.cpp @@ -9,8 +9,17 @@ $end_info$ #include #include + +// Include Xlibint.h and undefine some of its macros that clash with the standard library +#include +#undef min +#undef max + #include #include + +#include + #include "common/Host.h" #include @@ -45,7 +54,23 @@ char* fexfn_impl_libX11_XGetICValues_internal(XIC a_0, size_t count, unsigned lo case 6: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break; case 7: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break; default: - fprintf(stderr, "XCreateIC_internal FAILURE\n"); + fprintf(stderr, "XCreateICValues_internal FAILURE\n"); + return ErrorReply; + } +} + +char* fexfn_impl_libX11_XGetIMValues_internal(XIM a_0, size_t count, void **list) { + switch(count) { + case 0: return fexldr_ptr_libX11_XGetIMValues(a_0, nullptr); break; + case 1: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], nullptr); break; + case 2: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], nullptr); break; + case 3: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], nullptr); break; + case 4: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], nullptr); break; + case 5: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break; + case 6: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break; + case 7: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break; + default: + fprintf(stderr, "XCreateIMValues_internal FAILURE\n"); return ErrorReply; } } diff --git a/ThunkLibs/libX11/libX11_interface.cpp b/ThunkLibs/libX11/libX11_interface.cpp index 9f6e71fd7..702695541 100644 --- a/ThunkLibs/libX11/libX11_interface.cpp +++ b/ThunkLibs/libX11/libX11_interface.cpp @@ -4,6 +4,8 @@ #include #include +#include + #include #include @@ -442,6 +444,8 @@ template<> struct fex_gen_config { }; template<> struct fex_gen_config {}; +template<> struct fex_gen_config {}; + // TODO: Make returned function pointer guest-callable. For now, just pretend it already is. template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; template<> struct fex_gen_config {}; @@ -524,3 +528,64 @@ 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 {}; +template<> struct fex_gen_config {}; +template<> struct fex_gen_config {}; +template<> struct fex_gen_config {}; + +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config { + using uniform_va_type = void*; +}; + +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; + + +// TODO: Should probably catch these with a custom handler +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; + +template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; + +template<> struct fex_gen_config<_XRead32> {}; +template<> struct fex_gen_config<_XRead> {}; +template<> struct fex_gen_config<_XReadPad> {}; +template<> struct fex_gen_config<_XData32> {}; +template<> struct fex_gen_config<_XEatData> {}; +template<> struct fex_gen_config<_XEatDataWords> {}; +template<> struct fex_gen_config<_XGetAsyncReply> {}; +template<> struct fex_gen_config<_XSend> {}; +template<> struct fex_gen_config<_XFlush> {}; +template<> struct fex_gen_config<_XFlushGCCache> {}; +template<> struct fex_gen_config<_XAllocScratch> {}; +template<> struct fex_gen_config<_XGetRequest> {}; +template<> struct fex_gen_config<_XGetBitsPerPixel> {}; +template<> struct fex_gen_config<_XGetScanlinePad> {}; +template<> struct fex_gen_config<_XVIDtoVisual> {}; +template<> struct fex_gen_config<_XSetLastRequestRead> {}; +template<> struct fex_gen_config<_XDeqAsyncHandler> {}; +template<> struct fex_gen_config<_XAllocTemp> {}; +template<> struct fex_gen_config<_XFreeTemp> {}; +template<> struct fex_gen_config<_XUnknownNativeEvent> {}; +template<> struct fex_gen_config<_XIsEventCookie> {}; + +template<> struct fex_gen_config {}; + + + +// TODO: Vtable support... +template<> struct fex_gen_config<_XInitImageFuncPtrs> {}; From c6a034da400664e242f6ba116d04533905713362 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 29 Jul 2022 18:21:02 +0200 Subject: [PATCH 04/18] Thunks: Move PackedArguments to a dedicated header --- ThunkLibs/include/common/Guest.h | 83 +-------- ThunkLibs/include/common/PackedArguments.h | 187 +++++++++++++++++++++ 2 files changed, 189 insertions(+), 81 deletions(-) create mode 100644 ThunkLibs/include/common/PackedArguments.h diff --git a/ThunkLibs/include/common/Guest.h b/ThunkLibs/include/common/Guest.h index 69b736d84..c46e99868 100644 --- a/ThunkLibs/include/common/Guest.h +++ b/ThunkLibs/include/common/Guest.h @@ -2,6 +2,8 @@ #include #include +#include "PackedArguments.h" + #ifndef _M_ARM_64 #define MAKE_THUNK(lib, name, hash) \ extern "C" int fexthunks_##lib##_##name(void *args); \ @@ -68,87 +70,6 @@ inline bool IsLibLoaded(const char *libname) { return argsrv.rv; } -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. diff --git a/ThunkLibs/include/common/PackedArguments.h b/ThunkLibs/include/common/PackedArguments.h new file mode 100644 index 000000000..c9e47dc68 --- /dev/null +++ b/ThunkLibs/include/common/PackedArguments.h @@ -0,0 +1,187 @@ +#pragma once + +#include +#include + +template +struct PackedArguments; + +template +struct PackedArguments { R rv; }; +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; 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 { }; +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; }; + +template +void Invoke(Result(*func)(Args...), PackedArguments& args) { + constexpr auto NumArgs = sizeof...(Args); + static_assert(NumArgs <= 17 || NumArgs == 23); + if constexpr (std::is_void_v) { + if constexpr (NumArgs == 0) { + func(); + } else if constexpr (NumArgs == 1) { + func(args.a0); + } else if constexpr (NumArgs == 2) { + func(args.a0, args.a1); + } else if constexpr (NumArgs == 3) { + func(args.a0, args.a1, args.a2); + } else if constexpr (NumArgs == 4) { + func(args.a0, args.a1, args.a2, args.a3); + } else if constexpr (NumArgs == 5) { + func(args.a0, args.a1, args.a2, args.a3, args.a4); + } else if constexpr (NumArgs == 6) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5); + } else if constexpr (NumArgs == 7) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6); + } else if constexpr (NumArgs == 8) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7); + } else if constexpr (NumArgs == 9) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8); + } else if constexpr (NumArgs == 10) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9); + } else if constexpr (NumArgs == 11) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10); + } else if constexpr (NumArgs == 12) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11); + } else if constexpr (NumArgs == 13) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12); + } else if constexpr (NumArgs == 14) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13); + } else if constexpr (NumArgs == 15) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14); + } else if constexpr (NumArgs == 16) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15); + } else if constexpr (NumArgs == 17) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16); + } else if constexpr (NumArgs == 23) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17, args.a18, args.a19, args.a20, args.a21, args.a22); + } + } else { + if constexpr (NumArgs == 0) { + args.rv = func(); + } else if constexpr (NumArgs == 1) { + args.rv = func(args.a0); + } else if constexpr (NumArgs == 2) { + args.rv = func(args.a0, args.a1); + } else if constexpr (NumArgs == 3) { + args.rv = func(args.a0, args.a1, args.a2); + } else if constexpr (NumArgs == 4) { + args.rv = func(args.a0, args.a1, args.a2, args.a3); + } else if constexpr (NumArgs == 5) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4); + } else if constexpr (NumArgs == 6) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5); + } else if constexpr (NumArgs == 7) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6); + } else if constexpr (NumArgs == 8) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7); + } else if constexpr (NumArgs == 9) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8); + } else if constexpr (NumArgs == 10) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9); + } else if constexpr (NumArgs == 11) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10); + } else if constexpr (NumArgs == 12) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11); + } else if constexpr (NumArgs == 13) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12); + } else if constexpr (NumArgs == 14) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13); + } else if constexpr (NumArgs == 15) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14); + } else if constexpr (NumArgs == 16) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15); + } else if constexpr (NumArgs == 17) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16); + } else if constexpr (NumArgs == 23) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17, args.a18, args.a19, args.a20, args.a21, args.a22); + } + } +} From de92624c9ad9b1a0fde07e0de6156b8397c94e43 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 5 Aug 2022 17:40:49 +0200 Subject: [PATCH 05/18] Thunks: Use stronger types for interfaces --- .../Source/Interface/HLE/Thunks/Thunks.cpp | 50 ++++++++++--------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp index 464ced06a..fc835621e 100644 --- a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp +++ b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp @@ -68,12 +68,23 @@ namespace FEXCore { struct ExportEntry { uint8_t *sha256; ThunkedFunction* Fn; }; struct TrampolineInstanceInfo { - uintptr_t HostPacker; + void* HostPacker; uintptr_t CallCallback; uintptr_t GuestUnpacker; uintptr_t GuestTarget; }; + // Opaque type pointing to an instance of HostToGuestTrampolineTemplate and its + // embedded TrampolineInstanceInfo + struct HostToGuestTrampolinePtr; + const auto HostToGuestTrampolineSize = __stop_HostToGuestTrampolineTemplate - __start_HostToGuestTrampolineTemplate; + + static TrampolineInstanceInfo& GetInstanceInfo(HostToGuestTrampolinePtr* Trampoline) { + const auto Length = __stop_HostToGuestTrampolineTemplate - __start_HostToGuestTrampolineTemplate; + const auto InstanceInfoOffset = Length - sizeof(TrampolineInstanceInfo); + return *reinterpret_cast(reinterpret_cast(Trampoline) + InstanceInfoOffset); + } + struct GuestcallInfo { uintptr_t GuestUnpacker; uintptr_t GuestTarget; @@ -131,7 +142,7 @@ namespace FEXCore { // Ideally we track when a library has been unloaded and remove it from this set before the memory backing goes away. std::set Libs; - std::unordered_map GuestcallToHostTrampoline; + std::unordered_map GuestcallToHostTrampoline; uint8_t *HostTrampolineInstanceDataPtr; size_t HostTrampolineInstanceDataAvailable = 0; @@ -237,7 +248,7 @@ namespace FEXCore { auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci); if (found != ThunkHandler->GuestcallToHostTrampoline.end()) { - args->rv = found->second; + args->rv = reinterpret_cast(found->second); return; } } @@ -248,7 +259,7 @@ namespace FEXCore { { auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci); if (found != ThunkHandler->GuestcallToHostTrampoline.end()) { - args->rv = found->second; + args->rv = reinterpret_cast(found->second); return; } } @@ -262,10 +273,7 @@ namespace FEXCore { LogMan::Msg::DFmt("Thunks: Adding host trampoline for guest function {:#x}", args->GuestTarget); - const auto Length = __stop_HostToGuestTrampolineTemplate - __start_HostToGuestTrampolineTemplate; - const auto InstanceInfoOffset = Length - sizeof(TrampolineInstanceInfo); - - if (ThunkHandler->HostTrampolineInstanceDataAvailable < Length) { + if (ThunkHandler->HostTrampolineInstanceDataAvailable < HostToGuestTrampolineSize) { const auto allocation_step = 16 * 1024; ThunkHandler->HostTrampolineInstanceDataAvailable = allocation_step; ThunkHandler->HostTrampolineInstanceDataPtr = (uint8_t *)mmap( @@ -276,23 +284,19 @@ namespace FEXCore { LOGMAN_THROW_AA_FMT(ThunkHandler->HostTrampolineInstanceDataPtr != MAP_FAILED, "Failed to mmap HostTrampolineInstanceDataPtr"); } - const TrampolineInstanceInfo NewTrampolineInfo { - .HostPacker = reinterpret_cast(HostPackerEntry->second), - .CallCallback = (uintptr_t)&CallCallback, - .GuestUnpacker = args->GuestUnpacker, - .GuestTarget = args->GuestTarget - }; - - uint8_t* const HostTrampoline = ThunkHandler->HostTrampolineInstanceDataPtr; - ThunkHandler->HostTrampolineInstanceDataAvailable -= Length; - ThunkHandler->HostTrampolineInstanceDataPtr += Length; - - memcpy(HostTrampoline, (void*)&HostToGuestTrampolineTemplate, Length); - memcpy(HostTrampoline + InstanceInfoOffset, &NewTrampolineInfo, sizeof(NewTrampolineInfo)); + auto HostTrampoline = reinterpret_cast(ThunkHandler->HostTrampolineInstanceDataPtr); + ThunkHandler->HostTrampolineInstanceDataAvailable -= HostToGuestTrampolineSize; + ThunkHandler->HostTrampolineInstanceDataPtr += HostToGuestTrampolineSize; + memcpy(HostTrampoline, (void*)&HostToGuestTrampolineTemplate, HostToGuestTrampolineSize); + GetInstanceInfo(HostTrampoline) = TrampolineInstanceInfo { + .HostPacker = reinterpret_cast(HostPackerEntry->second), + .CallCallback = (uintptr_t)&CallCallback, + .GuestUnpacker = args->GuestUnpacker, + .GuestTarget = args->GuestTarget + }; args->rv = reinterpret_cast(HostTrampoline); - - ThunkHandler->GuestcallToHostTrampoline[gci] = args->rv; + ThunkHandler->GuestcallToHostTrampoline[gci] = HostTrampoline; } /** From 329d624a99a3e6583e933a5f5a35f5e4c893862f Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 29 Jul 2022 18:21:02 +0200 Subject: [PATCH 06/18] Thunks/gen: Use signature-based thunking of host function pointers --- ThunkLibs/Generator/gen.cpp | 56 +++++++++++++++++++- ThunkLibs/include/common/Guest.h | 25 ++++++--- ThunkLibs/include/common/Host.h | 60 ++++++++++++++++++++++ ThunkLibs/include/common/PackedArguments.h | 14 +++-- ThunkLibs/libGL/libGL_Guest.cpp | 4 +- ThunkLibs/libvulkan/Guest.cpp | 6 +-- unittests/ThunkLibs/generator.cpp | 38 ++++++++++++-- 7 files changed, 182 insertions(+), 21 deletions(-) diff --git a/ThunkLibs/Generator/gen.cpp b/ThunkLibs/Generator/gen.cpp index e49a3397f..fa2e31118 100644 --- a/ThunkLibs/Generator/gen.cpp +++ b/ThunkLibs/Generator/gen.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -93,6 +94,7 @@ struct ThunkedAPIFunction : FunctionParams { static std::vector thunks; static std::vector thunked_api; +static std::unordered_set funcptr_types; static std::optional lib_version; struct NamespaceInfo { @@ -263,6 +265,20 @@ public: * Matches "template<> struct fex_gen_config { ... }" */ bool VisitClassTemplateSpecializationDecl(clang::ClassTemplateSpecializationDecl* decl) try { + if (decl->getName() == "fex_gen_type") { + const auto& template_args = decl->getTemplateArgs(); + assert(template_args.size() == 1); + + // NOTE: Function types that are equivalent but use differently + // named types (e.g. GLuint/GLenum) are represented by + // different Type instances. The canonical type they refer + // to is unique, however. + auto type = context.getCanonicalType(template_args[0].getAsType()).getTypePtr(); + funcptr_types.insert(type); + + return true; + } + if (decl->getName() != "fex_gen_config") { return true; } @@ -323,6 +339,10 @@ public: } data.callbacks.emplace(param_idx, callback); + if (!callback.is_stub && !callback.is_guest) { + funcptr_types.insert(context.getCanonicalType(funcptr)); + } + // TODO: Support for more than one callback is untested assert(data.callbacks.size() == 1); if (funcptr->isVariadic() && !callback.is_stub) { @@ -367,6 +387,8 @@ public: hostcall_data.param_types.push_back(context.getUIntPtrType()); hostcall_data.is_hostcall = true; thunks.push_back(std::move(hostcall_data)); + + funcptr_types.insert(context.getCanonicalType(emitted_function->getFunctionType())); } thunks.push_back(std::move(data)); @@ -395,6 +417,7 @@ GenerateThunkLibsAction::GenerateThunkLibsAction(const std::string& libname_, co thunks.clear(); thunked_api.clear(); + funcptr_types.clear(); namespaces.clear(); lib_version = std::nullopt; } @@ -496,8 +519,25 @@ void GenerateThunkLibsAction::EndSourceFileAction() { } } } - file << "}\n"; + + for (auto type_it = funcptr_types.begin(); type_it != funcptr_types.end(); ++type_it) { + auto* type = *type_it; + std::string funcptr_signature = clang::QualType { type, 0 }.getAsString(); + + auto cb_sha256 = get_sha256("fexcallback_" + funcptr_signature); + std::stringstream cb_sha256_ss; + for (auto c : cb_sha256) { + cb_sha256_ss << "0x" << std::hex << std::setw(2) << std::setfill('0') << +c << ", "; + } + auto cb_sha256_str = std::move(cb_sha256_ss).str(); + cb_sha256_str.pop_back(); + cb_sha256_str.pop_back(); + + // Thunk used for guest-side calls to host function pointers + auto funcptr_idx = std::distance(funcptr_types.begin(), type_it); + file << " MAKE_CALLBACK_THUNK(callback_" << funcptr_idx << ", " << funcptr_signature << ", \"" << cb_sha256_str << "\");\n"; + } } if (!output_filenames.function_packs_public.empty()) { @@ -710,6 +750,20 @@ void GenerateThunkLibsAction::EndSourceFileAction() { file << "\", (void(*)(void *))&fexfn_pack_guestcall_" << cb_function_name << "}, // " << libname << ":" << cb_function_name << "\n"; } } + + for (auto& type : funcptr_types) { + std::string mangled_name = clang::QualType { type, 0 }.getAsString(); + { + auto cb_sha256 = get_sha256("fexcallback_" + mangled_name); + + std::stringstream cb_sha256_ss; + for (auto c : cb_sha256) { + cb_sha256_ss << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c; + } + auto cb_sha256_str = std::move(cb_sha256_ss).str(); + file << " {(uint8_t*)\"" << cb_sha256_str << "\", (void(*)(void *))&CallbackUnpack<" << mangled_name << ">::ForIndirectCall},\n"; + } + } } if (!output_filenames.ldr.empty()) { diff --git a/ThunkLibs/include/common/Guest.h b/ThunkLibs/include/common/Guest.h index c46e99868..525f583c1 100644 --- a/ThunkLibs/include/common/Guest.h +++ b/ThunkLibs/include/common/Guest.h @@ -4,10 +4,19 @@ #include "PackedArguments.h" +template +const int (*fexthunks_invoke_callback)(void*); + #ifndef _M_ARM_64 #define MAKE_THUNK(lib, name, hash) \ extern "C" int fexthunks_##lib##_##name(void *args); \ asm(".text\nfexthunks_" #lib "_" #name ":\n.byte 0xF, 0x3F\n.byte " hash ); + +#define MAKE_CALLBACK_THUNK(name, signature, hash) \ + extern "C" int fexthunks_##name(void *args); \ + asm(".text\nfexthunks_" #name ":\n.byte 0xF, 0x3F\n.byte " hash ); \ + template<> inline constexpr int (*fexthunks_invoke_callback)(void*) = fexthunks_##name; + #else // We're compiling for IDE integration, so provide a dummy-implementation that just calls an undefined function. // The name of that function serves as an error message if this library somehow gets loaded at runtime. @@ -17,6 +26,9 @@ extern "C" void BROKEN_INSTALL___TRIED_LOADING_AARCH64_BUILD_OF_GUEST_THUNK(); BROKEN_INSTALL___TRIED_LOADING_AARCH64_BUILD_OF_GUEST_THUNK(); \ return 0; \ } +#define MAKE_CALLBACK_THUNK(name, signature, hash) \ + extern "C" int fexthunks_##name(void *args); \ + template<> inline constexpr int (*fexthunks_invoke_callback)(void*) = fexthunks_##name; #endif // Generated fexfn_pack_ symbols should be hidden by default, but clang does @@ -76,7 +88,7 @@ inline bool IsLibLoaded(const char *libname) { // 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) { +inline Result CallHostFunction(Args... args) { #ifndef _M_ARM_64 uintptr_t host_addr; asm volatile("mov %%r11, %0" : "=r" (host_addr)); @@ -97,13 +109,12 @@ inline Result CallHostThunkFromRuntimePointer(Args... args) { } } -// 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...)) +// Convenience wrapper that returns the function pointer to a CallHostFunction +// instantiation matching the function signature of `host_func` +template +static auto GetCallerForHostFunction(Result (*host_func)(Args...)) -> Result(*)(Args...) { - return CallHostThunkFromRuntimePointer; + return CallHostFunction, Result, Args...>; } template diff --git a/ThunkLibs/include/common/Host.h b/ThunkLibs/include/common/Host.h index af3acd82c..568d8d96e 100644 --- a/ThunkLibs/include/common/Host.h +++ b/ThunkLibs/include/common/Host.h @@ -7,6 +7,8 @@ $end_info$ #pragma once #include +#include "PackedArguments.h" + template struct function_traits; template @@ -76,3 +78,61 @@ struct GuestcallInfo { #define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) \ asm volatile("mov %0, x11" : "=r" (target_variable)) #endif + +template +struct CallbackUnpack; + +template +struct CallbackUnpack { + static void ForIndirectCall(void* argsv) { + auto args = reinterpret_cast*>(argsv); + constexpr auto CBIndex = sizeof...(Args); + uintptr_t cb; + static_assert(CBIndex <= 17 || CBIndex == 23); + if constexpr(CBIndex == 0) { + cb = args->a0; + } else if constexpr(CBIndex == 1) { + cb = args->a1; + } else if constexpr(CBIndex == 2) { + cb = args->a2; + } else if constexpr(CBIndex == 3) { + cb = args->a3; + } else if constexpr(CBIndex == 4) { + cb = args->a4; + } else if constexpr(CBIndex == 5) { + cb = args->a5; + } else if constexpr(CBIndex == 6) { + cb = args->a6; + } else if constexpr(CBIndex == 7) { + cb = args->a7; + } else if constexpr(CBIndex == 8) { + cb = args->a8; + } else if constexpr(CBIndex == 9) { + cb = args->a9; + } else if constexpr(CBIndex == 10) { + cb = args->a10; + } else if constexpr(CBIndex == 11) { + cb = args->a11; + } else if constexpr(CBIndex == 12) { + cb = args->a12; + } else if constexpr(CBIndex == 13) { + cb = args->a13; + } else if constexpr(CBIndex == 14) { + cb = args->a14; + } else if constexpr(CBIndex == 15) { + cb = args->a15; + } else if constexpr(CBIndex == 16) { + cb = args->a16; + } else if constexpr(CBIndex == 17) { + cb = args->a17; + } else if constexpr(CBIndex == 23) { + cb = args->a23; + } + auto callback = reinterpret_cast(cb); + Invoke(callback, *args); + } +}; + +template +struct CallbackUnpack : CallbackUnpack { +}; diff --git a/ThunkLibs/include/common/PackedArguments.h b/ThunkLibs/include/common/PackedArguments.h index c9e47dc68..2578e545e 100644 --- a/ThunkLibs/include/common/PackedArguments.h +++ b/ThunkLibs/include/common/PackedArguments.h @@ -102,7 +102,7 @@ struct PackedArguments void Invoke(Result(*func)(Args...), PackedArguments& args) { constexpr auto NumArgs = sizeof...(Args); - static_assert(NumArgs <= 17 || NumArgs == 23); + static_assert(NumArgs <= 18 || NumArgs == 24); if constexpr (std::is_void_v) { if constexpr (NumArgs == 0) { func(); @@ -140,8 +140,10 @@ void Invoke(Result(*func)(Args...), PackedArguments& args) { func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15); } else if constexpr (NumArgs == 17) { func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16); - } else if constexpr (NumArgs == 23) { - func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17, args.a18, args.a19, args.a20, args.a21, args.a22); + } else if constexpr (NumArgs == 18) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17); + } else if constexpr (NumArgs == 24) { + func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17, args.a18, args.a19, args.a20, args.a21, args.a22, args.a23); } } else { if constexpr (NumArgs == 0) { @@ -180,8 +182,10 @@ void Invoke(Result(*func)(Args...), PackedArguments& args) { args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15); } else if constexpr (NumArgs == 17) { args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16); - } else if constexpr (NumArgs == 23) { - args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17, args.a18, args.a19, args.a20, args.a21, args.a22); + } else if constexpr (NumArgs == 18) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17); + } else if constexpr (NumArgs == 24) { + args.rv = func(args.a0, args.a1, args.a2, args.a3, args.a4, args.a5, args.a6, args.a7, args.a8, args.a9, args.a10, args.a11, args.a12, args.a13, args.a14, args.a15, args.a16, args.a17, args.a18, args.a19, args.a20, args.a21, args.a22, args.a23); } } } diff --git a/ThunkLibs/libGL/libGL_Guest.cpp b/ThunkLibs/libGL/libGL_Guest.cpp index 01024b5ee..65635cb8c 100644 --- a/ThunkLibs/libGL/libGL_Guest.cpp +++ b/ThunkLibs/libGL/libGL_Guest.cpp @@ -35,9 +35,9 @@ typedef void voidFunc(); // linked to the corresponding host function pointer const std::unordered_map HostPtrInvokers = std::invoke([]() { -#define PAIR(name, unused) Ret[#name] = reinterpret_cast(GetCallerForHostThunkFromRuntimePointer(name)); +#define PAIR(name, unused) Ret[#name] = reinterpret_cast(GetCallerForHostFunction(name)); std::unordered_map Ret; - FOREACH_internal_SYMBOL(PAIR) + FOREACH_internal_SYMBOL(PAIR); return Ret; #undef PAIR }); diff --git a/ThunkLibs/libvulkan/Guest.cpp b/ThunkLibs/libvulkan/Guest.cpp index 64a80ada9..e9e1567b4 100644 --- a/ThunkLibs/libvulkan/Guest.cpp +++ b/ThunkLibs/libvulkan/Guest.cpp @@ -29,9 +29,9 @@ extern "C" { // linked to the corresponding host function pointer const std::unordered_map HostPtrInvokers = std::invoke([]() { -#define PAIR(name, unused) Ret[#name] = reinterpret_cast(GetCallerForHostThunkFromRuntimePointer(name)); +#define PAIR(name, unused) Ret[#name] = reinterpret_cast(GetCallerForHostFunction(name)); std::unordered_map Ret; - FOREACH_internal_SYMBOL(PAIR) + FOREACH_internal_SYMBOL(PAIR); return Ret; #undef PAIR }); @@ -53,7 +53,7 @@ static PFN_vkVoidFunction MakeGuestCallable(const char* origin, PFN_vkVoidFuncti if (It == HostPtrInvokers.end()) { fprintf(stderr, "%s: Unknown Vulkan function at address %p: %s\n", origin, func, name); if (stub_unknown_functions) { - const auto StubHostPtrInvoker = CallHostThunkFromRuntimePointer; + const auto StubHostPtrInvoker = CallHostFunction; LinkAddressToFunction((uintptr_t)func, reinterpret_cast(StubHostPtrInvoker)); return func; } diff --git a/unittests/ThunkLibs/generator.cpp b/unittests/ThunkLibs/generator.cpp index 1a421c9a4..dd37ddcdd 100644 --- a/unittests/ThunkLibs/generator.cpp +++ b/unittests/ThunkLibs/generator.cpp @@ -280,6 +280,9 @@ SourceWithAST Fixture::run_thunkgen_guest(std::string_view prelude, std::string_ std::string result = "#include \n" "#define MAKE_THUNK(lib, name, hash) extern \"C\" int fexthunks_##lib##_##name(void*);\n" + "template\n" + "struct callback_thunk_defined;\n" + "#define MAKE_CALLBACK_THUNK(name, sig, hash) template<> struct callback_thunk_defined {};\n" "#define FEX_PACKFN_LINKAGE\n" "template\n" "Target *MakeHostTrampolineForGuestFunction(uint8_t HostPacker[32], void (*)(uintptr_t, void*), Target*);\n"; @@ -327,6 +330,10 @@ SourceWithAST Fixture::run_thunkgen_host(std::string_view prelude, std::string_v " void (*CallCallback)(uintptr_t, uintptr_t, void*);\n" " uintptr_t GuestUnpacker;\n" " uintptr_t GuestTarget;\n" + "};\n" + "template\n" + "struct CallbackUnpack {\n" + " static void ForIndirectCall(void* argsv);\n" "};\n"; for (auto& filename : { @@ -414,6 +421,31 @@ TEST_CASE_METHOD(Fixture, "VersionedLibrary") { })); } +TEST_CASE_METHOD(Fixture, "FunctionPointerViaType") { + const auto output = run_thunkgen("", + "template struct fex_gen_type {};\n" + "template<> struct fex_gen_type {};\n"); + + // Guest should apply MAKE_CALLBACK_THUNK to this signature + CHECK_THAT(output.guest, + matches(classTemplateSpecializationDecl( + // Should have signature matching input function + hasName("callback_thunk_defined"), + hasTemplateArgument(0, refersToType(asString("int (char, char)"))) + ))); + + // Host should export the unpacking function for callback arguments + CHECK_THAT(output.host, + matches(varDecl( + hasName("exports"), + hasType(constantArrayType(hasElementType(asString("struct ExportEntry")), hasSize(2))), + hasInitializer(hasDescendant(declRefExpr(to(cxxMethodDecl(hasName("ForIndirectCall"), ofClass(hasName("CallbackUnpack"))).bind("funcptr"))))) + )).check_binding("funcptr", +[](const clang::CXXMethodDecl* decl) { + auto parent = llvm::cast(decl->getParent()); + return parent->getTemplateArgs().get(0).getAsType().getAsString() == "int (char, char)"; + })); +} + // Parameter is a function pointer TEST_CASE_METHOD(Fixture, "FunctionPointerParameter") { const auto output = run_thunkgen("", @@ -433,12 +465,12 @@ TEST_CASE_METHOD(Fixture, "FunctionPointerParameter") { hasDescendant(callExpr(callee(functionDecl(hasName("MakeHostTrampolineForGuestFunction"))))) ))); - // Host should export the packing function for callback arguments + // Host should export the unpacking function for function pointer arguments CHECK_THAT(output.host, matches(varDecl( hasName("exports"), - hasType(constantArrayType(hasElementType(asString("struct ExportEntry")), hasSize(3))), - hasInitializer(hasDescendant(declRefExpr(to(functionDecl(hasName("fexfn_pack_guestcall_funcCBFN")))))) + hasType(constantArrayType(hasElementType(asString("struct ExportEntry")), hasSize(4))), + hasInitializer(hasDescendant(declRefExpr(to(cxxMethodDecl(hasName("ForIndirectCall"), ofClass(hasName("CallbackUnpack"))))))) ))); } From 4f8ae81562556dcc0e591079bb1f1bbb488571b5 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 29 Jul 2022 18:21:02 +0200 Subject: [PATCH 07/18] Thunks/gen: Remove now unneeded hostcall bits --- ThunkLibs/Generator/gen.cpp | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/ThunkLibs/Generator/gen.cpp b/ThunkLibs/Generator/gen.cpp index fa2e31118..665dba746 100644 --- a/ThunkLibs/Generator/gen.cpp +++ b/ThunkLibs/Generator/gen.cpp @@ -48,11 +48,6 @@ 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()); @@ -380,14 +375,8 @@ public: data.custom_host_impl = true; } - // For indirect calls, register a second thunk with the callee pointer as an extra argument + // For indirect calls, register the function signature as a function pointer type 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)); - funcptr_types.insert(context.getCanonicalType(emitted_function->getFunctionType())); } @@ -690,11 +679,7 @@ 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) { + if (thunk.custom_host_impl) { function_to_call = "fexfn_impl_" + libname + "_" + function_name; } From 3ac165000136ed144d239a3df6d060378d0cb60a Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 29 Jul 2022 18:21:02 +0200 Subject: [PATCH 08/18] Thunks/X11: Thunk function pointers set up in XOpenDisplay --- ThunkLibs/libX11/libX11_Guest.cpp | 38 ++++++++++++++++++++++++++- ThunkLibs/libX11/libX11_interface.cpp | 17 +++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/ThunkLibs/libX11/libX11_Guest.cpp b/ThunkLibs/libX11/libX11_Guest.cpp index 8c66d91fc..46fa16020 100644 --- a/ThunkLibs/libX11/libX11_Guest.cpp +++ b/ThunkLibs/libX11/libX11_Guest.cpp @@ -130,9 +130,45 @@ extern "C" { } } + Display* XOpenDisplay(const char* name) { + auto ret = fexfn_pack_XOpenDisplay(name); + + for (auto& funcptr : ret->event_vec) { + if (!funcptr) { + continue; + } + auto caller = (uintptr_t)GetCallerForHostFunction(funcptr); + LinkAddressToFunction((uintptr_t)funcptr, (uintptr_t)caller); + } + + for (auto& funcptr : ret->wire_vec) { + if (!funcptr) { + continue; + } + auto caller = (uintptr_t)GetCallerForHostFunction(funcptr); + LinkAddressToFunction((uintptr_t)funcptr, (uintptr_t)caller); + } + + { + auto caller = (uintptr_t)GetCallerForHostFunction(ret->resource_alloc); + LinkAddressToFunction((uintptr_t)ret->resource_alloc, (uintptr_t)caller); + } + + { + auto caller = (uintptr_t)GetCallerForHostFunction(ret->idlist_alloc); + LinkAddressToFunction((uintptr_t)ret->idlist_alloc, (uintptr_t)caller); + } + + { + auto caller = (uintptr_t)GetCallerForHostFunction(ret->exit_handler); + LinkAddressToFunction((uintptr_t)ret->exit_handler, (uintptr_t)caller); + } + + return ret; + } + void (*_XLockMutex_fn)(LockInfoPtr) = LockMutexFunction; void (*_XUnlockMutex_fn)(LockInfoPtr) = UnlockMutexFunction; - typedef struct _LockInfoRec *LockInfoPtr; LockInfoPtr _Xglobal_lock = (LockInfoPtr)0x4142434445464748ULL; } diff --git a/ThunkLibs/libX11/libX11_interface.cpp b/ThunkLibs/libX11/libX11_interface.cpp index 702695541..6ae995aa6 100644 --- a/ThunkLibs/libX11/libX11_interface.cpp +++ b/ThunkLibs/libX11/libX11_interface.cpp @@ -9,11 +9,16 @@ #include #include +#include + template struct fex_gen_config { unsigned version = 6; }; +template +struct fex_gen_type {}; + template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -453,7 +458,17 @@ 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_type {}; // XDisplay::resource_alloc + +// NOTE: only indirect calls to this are allowed +// NOTE: The char* and int arguments are only present in some configurations, but always enabling them interfers with our internal ABI... +template<> struct fex_gen_type {}; // XDisplay::lock_fns->lock_display + +template<> struct fex_gen_type {}; // XDisplay::idlist_alloc +template<> struct fex_gen_type> {}; // XDisplay::exit_handler +template<> struct fex_gen_config : fexgen::custom_guest_entrypoint {}; + template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; From e8ad1ca0a0a3810c7a225ccfa37c8fe6e87a8444 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 5 Aug 2022 17:41:25 +0200 Subject: [PATCH 09/18] Thunks: Use signature-based thunking of guest function pointers This changes how host trampolines for guest functions are created. Instead of doing this purely on the guest-side, it's either the host-side that creates them in a single step *or* a cooperative two-step initialization process must be used. In the latter, trampolines are allocated and partially initialized on the guest and must be finalized on the host before use. --- .../Source/Interface/HLE/Thunks/Thunks.cpp | 203 ++++++++++-------- Source/Tests/CMakeLists.txt | 3 + ThunkLibs/Generator/gen.cpp | 95 +------- ThunkLibs/HostLibs/CMakeLists.txt | 1 + ThunkLibs/include/common/Guest.h | 32 ++- ThunkLibs/include/common/Host.h | 62 +++++- unittests/ThunkLibs/generator.cpp | 25 ++- 7 files changed, 231 insertions(+), 190 deletions(-) diff --git a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp index fc835621e..d899b5ce8 100644 --- a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp +++ b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp @@ -107,7 +107,9 @@ namespace FEXCore { } }; - class ThunkHandler_impl final: public ThunkHandler { + HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker); + + struct ThunkHandler_impl final: public ThunkHandler { std::shared_mutex ThunksMutex; std::unordered_map Thunks = { @@ -132,9 +134,9 @@ namespace FEXCore { &LinkAddressToGuestFunction }, { - // sha256(fex:make_host_trampoline_for_guest_function) - { 0x1e, 0x51, 0x6b, 0x07, 0x39, 0xeb, 0x50, 0x59, 0xb3, 0xf3, 0x4f, 0xca, 0xdd, 0x58, 0x37, 0xe9, 0xf0, 0x30, 0xe5, 0x89, 0x81, 0xc7, 0x14, 0xfb, 0x24, 0xf9, 0xba, 0xe7, 0x0e, 0x00, 0x1e, 0x86 }, - &MakeHostTrampolineForGuestFunction + // sha256(fex:allocate_host_trampoline_for_guest_function) + { 0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, 0xf9, 0xbc, 0xb5, 0x27, 0xca, 0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a }, + &AllocateHostTrampolineForGuestFunction } }; @@ -212,91 +214,24 @@ namespace FEXCore { } /** - * Generates a host-callable trampoline to call guest functions via the host ABI. + * Guest-side helper to initiate creation of a host trampoline for + * calling guest functions. This must be followed by a host-side call + * to FinalizeHostTrampolineForGuestFunction to make the trampoline + * usable. * - * This trampoline uses the same calling convention as the given HostPacker. Trampolines - * are cached, so it's safe to call this function repeatedly on the same arguments without - * leaking memory. - * - * Invoking the returned trampoline has the effect of: - * - packing the arguments (using the HostPacker identified by its SHA256) - * - performing a host->guest transition - * - unpacking the arguments via GuestUnpacker - * - calling the function at GuestTarget - * - * The primary use case of this is ensuring that guest function pointers ("callbacks") - * passed to thunked APIs can safely be called by the native host library. + * This two-step initialization is equivalent to a host-side call to + * MakeHostTrampolineForGuestFunction. The split is needed if the + * host doesn't have all information needed to create the trampoline + * on its own. */ - static void MakeHostTrampolineForGuestFunction(void* ArgsRV) { - struct ArgsRV_t { - IR::SHA256Sum *HostPackerSha256; - uintptr_t GuestUnpacker; - uintptr_t GuestTarget; - uintptr_t rv; // Pointer to host trampoline + TrampolineInstanceInfo - } *args = reinterpret_cast(ArgsRV); + static void AllocateHostTrampolineForGuestFunction(void* ArgsRV) { + struct ArgsRV_t { + uintptr_t GuestUnpacker; + uintptr_t GuestTarget; + uintptr_t rv; // Pointer to host trampoline + TrampolineInstanceInfo + } *args = reinterpret_cast(ArgsRV); - LOGMAN_THROW_AA_FMT(args->GuestTarget, "Tried to create host-trampoline to null pointer guest function"); - - const auto CTX = Thread->CTX; - const auto ThunkHandler = reinterpret_cast(CTX->ThunkHandler.get()); - - const GuestcallInfo gci = { args->GuestUnpacker, args->GuestTarget }; - - // Try first with shared_lock - { - std::shared_lock lk(ThunkHandler->ThunksMutex); - - auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci); - if (found != ThunkHandler->GuestcallToHostTrampoline.end()) { - args->rv = reinterpret_cast(found->second); - return; - } - } - - std::lock_guard lk(ThunkHandler->ThunksMutex); - - // Retry lookup with full lock before making a new trampoline to avoid double trampolines - { - auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci); - if (found != ThunkHandler->GuestcallToHostTrampoline.end()) { - args->rv = reinterpret_cast(found->second); - return; - } - } - - // No entry found => create new trampoline - auto HostPackerEntry = ThunkHandler->Thunks.find(*args->HostPackerSha256); - if (HostPackerEntry == ThunkHandler->Thunks.end()) { - ERROR_AND_DIE_FMT("Unknown host packing function for callback"); - } - - LogMan::Msg::DFmt("Thunks: Adding host trampoline for guest function {:#x}", - args->GuestTarget); - - if (ThunkHandler->HostTrampolineInstanceDataAvailable < HostToGuestTrampolineSize) { - const auto allocation_step = 16 * 1024; - ThunkHandler->HostTrampolineInstanceDataAvailable = allocation_step; - ThunkHandler->HostTrampolineInstanceDataPtr = (uint8_t *)mmap( - 0, ThunkHandler->HostTrampolineInstanceDataAvailable, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - LOGMAN_THROW_AA_FMT(ThunkHandler->HostTrampolineInstanceDataPtr != MAP_FAILED, "Failed to mmap HostTrampolineInstanceDataPtr"); - } - - auto HostTrampoline = reinterpret_cast(ThunkHandler->HostTrampolineInstanceDataPtr); - ThunkHandler->HostTrampolineInstanceDataAvailable -= HostToGuestTrampolineSize; - ThunkHandler->HostTrampolineInstanceDataPtr += HostToGuestTrampolineSize; - memcpy(HostTrampoline, (void*)&HostToGuestTrampolineTemplate, HostToGuestTrampolineSize); - GetInstanceInfo(HostTrampoline) = TrampolineInstanceInfo { - .HostPacker = reinterpret_cast(HostPackerEntry->second), - .CallCallback = (uintptr_t)&CallCallback, - .GuestUnpacker = args->GuestUnpacker, - .GuestTarget = args->GuestTarget - }; - - args->rv = reinterpret_cast(HostTrampoline); - ThunkHandler->GuestcallToHostTrampoline[gci] = HostTrampoline; + args->rv = (uintptr_t)MakeHostTrampolineForGuestFunction(nullptr, args->GuestTarget, args->GuestUnpacker); } /** @@ -378,8 +313,6 @@ namespace FEXCore { } } - public: - ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) { std::shared_lock lk(ThunksMutex); @@ -401,4 +334,98 @@ namespace FEXCore { ThunkHandler* ThunkHandler::Create() { return new ThunkHandler_impl(); } + + /** + * Generates a host-callable trampoline to call guest functions via the host ABI. + * + * This trampoline uses the same calling convention as the given HostPacker. Trampolines + * are cached, so it's safe to call this function repeatedly on the same arguments without + * leaking memory. + * + * Invoking the returned trampoline has the effect of: + * - packing the arguments (using the HostPacker identified by its SHA256) + * - performing a host->guest transition + * - unpacking the arguments via GuestUnpacker + * - calling the function at GuestTarget + * + * The primary use case of this is ensuring that guest function pointers ("callbacks") + * passed to thunked APIs can safely be called by the native host library. + * + * Returns a pointer to the generated host trampoline and its TrampolineInstanceInfo. + * + * If HostPacker is zero, the trampoline will be partially initialized and needs to be + * finalized with a call to FinalizeHostTrampolineForGuestFunction. A typical use case + * is to allocate the trampoline for a given GuestTarget/GuestUnpacker on the guest-side, + * and provide the HostPacker host-side. + */ + __attribute__((visibility("default"))) + HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) { + LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function"); + + const auto CTX = Thread->CTX; + const auto ThunkHandler = reinterpret_cast(CTX->ThunkHandler.get()); + + const GuestcallInfo gci = { GuestUnpacker, GuestTarget }; + + // Try first with shared_lock + { + std::shared_lock lk(ThunkHandler->ThunksMutex); + + auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci); + if (found != ThunkHandler->GuestcallToHostTrampoline.end()) { + return found->second; + } + } + + std::lock_guard lk(ThunkHandler->ThunksMutex); + + // Retry lookup with full lock before making a new trampoline to avoid double trampolines + { + auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci); + if (found != ThunkHandler->GuestcallToHostTrampoline.end()) { + return found->second; + } + } + + LogMan::Msg::DFmt("Thunks: Adding host trampoline for guest function {:#x} via unpacker {:#x}", + GuestTarget, GuestUnpacker); + + if (ThunkHandler->HostTrampolineInstanceDataAvailable < HostToGuestTrampolineSize) { + const auto allocation_step = 16 * 1024; + ThunkHandler->HostTrampolineInstanceDataAvailable = allocation_step; + ThunkHandler->HostTrampolineInstanceDataPtr = (uint8_t *)mmap( + 0, ThunkHandler->HostTrampolineInstanceDataAvailable, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + LOGMAN_THROW_AA_FMT(ThunkHandler->HostTrampolineInstanceDataPtr != MAP_FAILED, "Failed to mmap HostTrampolineInstanceDataPtr"); + } + + auto HostTrampoline = reinterpret_cast(ThunkHandler->HostTrampolineInstanceDataPtr); + ThunkHandler->HostTrampolineInstanceDataAvailable -= HostToGuestTrampolineSize; + ThunkHandler->HostTrampolineInstanceDataPtr += HostToGuestTrampolineSize; + memcpy(HostTrampoline, (void*)&HostToGuestTrampolineTemplate, HostToGuestTrampolineSize); + GetInstanceInfo(HostTrampoline) = TrampolineInstanceInfo { + .HostPacker = HostPacker, + .CallCallback = (uintptr_t)&ThunkHandler_impl::CallCallback, + .GuestUnpacker = GuestUnpacker, + .GuestTarget = GuestTarget + }; + + ThunkHandler->GuestcallToHostTrampoline[gci] = HostTrampoline; + return HostTrampoline; + } + + __attribute__((visibility("default"))) + void FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr* TrampolineAddress, void* HostPacker) { + auto& Trampoline = GetInstanceInfo(TrampolineAddress); + + LOGMAN_THROW_A_FMT(Trampoline.CallCallback == (uintptr_t)&ThunkHandler_impl::CallCallback, + "Invalid trampoline at {} passed to {}", fmt::ptr(TrampolineAddress), __FUNCTION__); + + if (!Trampoline.HostPacker) { + LogMan::Msg::DFmt("Thunks: Finalizing trampoline at {} with host packer {}", fmt::ptr(TrampolineAddress), fmt::ptr(HostPacker)); + Trampoline.HostPacker = HostPacker; + } + } } diff --git a/Source/Tests/CMakeLists.txt b/Source/Tests/CMakeLists.txt index c4dbc54c9..7ca143bc0 100644 --- a/Source/Tests/CMakeLists.txt +++ b/Source/Tests/CMakeLists.txt @@ -6,6 +6,9 @@ add_executable(FEXLoader FEXLoader.cpp AOT/AOTGenerator.cpp) +# Enable FEX APIs to be used by targets that use target_link_libraries on FEXLoader +set_target_properties(FEXLoader PROPERTIES ENABLE_EXPORTS 1) + target_include_directories(FEXLoader PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Source/ diff --git a/ThunkLibs/Generator/gen.cpp b/ThunkLibs/Generator/gen.cpp index 665dba746..507f1f259 100644 --- a/ThunkLibs/Generator/gen.cpp +++ b/ThunkLibs/Generator/gen.cpp @@ -422,12 +422,8 @@ static std::string format_function_args(const FunctionParams& params, Fn&& forma return ret; }; -static std::string format_function_args(const FunctionParams& params) { - return format_function_args(params, - [](std::size_t idx) -> std::string { return "args->a_" + std::to_string(idx); }); -} - void GenerateThunkLibsAction::EndSourceFileAction() { + static auto format_decl = [](clang::QualType type, const std::string_view& name) { if (type->isFunctionPointerType()) { auto signature = type.getAsString(); @@ -491,22 +487,6 @@ void GenerateThunkLibsAction::EndSourceFileAction() { first = false; } file << "\")\n"; - - // Generate SHA256 sums for automatically handled callbacks - for (auto& [cb_idx, cb] : thunk.callbacks) { - if (!cb.is_stub && !cb.is_guest) { - bool is_first_cb = (cb_idx == thunk.callbacks.begin()->first); - auto cb_function_name = get_callback_name(function_name, cb_idx, is_first_cb); - - auto cb_sha256 = get_sha256(cb_function_name); - - file << "static uint8_t fexcallback_" << libname << "_" << cb_function_name << "[32] = { "; - for (auto c : cb_sha256) { - file << "0x" << std::hex << std::setw(2) << std::setfill('0') << +c << ", "; - } - file << "};\n"; - } - } } file << "}\n"; @@ -524,6 +504,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() { cb_sha256_str.pop_back(); // Thunk used for guest-side calls to host function pointers + file << " // " << funcptr_signature << "\n"; auto funcptr_idx = std::distance(funcptr_types.begin(), type_it); file << " MAKE_CALLBACK_THUNK(callback_" << funcptr_idx << ", " << funcptr_signature << ", \"" << cb_sha256_str << "\");\n"; } @@ -594,6 +575,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() { file << " char force_nonempty;\n"; } file << " } args;\n"; + for (std::size_t idx = 0; idx < data.param_types.size(); ++idx) { auto cb = data.callbacks.find(idx); @@ -602,9 +584,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() { file << "a_" << idx << ";\n"; } else { // Before passing guest function pointers to the host, wrap them in a host-callable trampoline - bool is_first_cb = (cb->first == data.callbacks.begin()->first); - auto cb_name = get_callback_name(function_name, cb->first, is_first_cb); - file << "MakeHostTrampolineForGuestFunction(fexcallback_" << libname << "_" << cb_name << ", &fexfn_unpack_" << libname << "_" << cb_name << ", a_" << std::to_string(idx) << ");\n"; + file << "AllocateHostTrampolineForGuestFunction(a_" << std::to_string(idx) << ");\n"; } } file << " fexthunks_" << libname << "_" << function_name << "(&args);\n"; @@ -650,28 +630,6 @@ void GenerateThunkLibsAction::EndSourceFileAction() { file << " fprintf(stderr, \"FATAL: Attempted to invoke callback stub for " << function_name << "\\n\");\n"; file << " std::abort();\n"; file << "}\n"; - } else if (!cb.is_guest) { - bool is_first_cb = (cb_idx == thunk.callbacks.begin()->first); - const char* variadic_ellipsis = cb.is_variadic ? ", ..." : ""; - auto cb_function_name = get_callback_name(function_name, cb_idx, is_first_cb); - - file << "static " << cb.return_type.getAsString() << " fexfn_pack_guestcall_" - << cb_function_name << "(" - << format_function_params(cb) << variadic_ellipsis << ") {\n"; - file << " GuestcallInfo *guestcall;\n"; - file << " LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(guestcall);\n"; - - auto args_struct_name = GeneratePackedArgs(cb_function_name, cb); - file << " " << args_struct_name << " argsrv;\n"; - for (std::size_t idx = 0; idx < cb.param_types.size(); ++idx) { - file << " argsrv.a_" << idx << " = a_" << idx << ";\n"; - } - file << " guestcall->CallCallback(guestcall->GuestUnpacker, guestcall->GuestTarget, &argsrv);\n"; - - if (!cb.return_type->isVoidType()) { - file << " return argsrv.rv;\n"; - } - file << "}\n"; } } @@ -693,6 +651,11 @@ void GenerateThunkLibsAction::EndSourceFileAction() { return "fexfn_unpack_" + get_callback_name(function_name, cb->first, is_first_cb) + "_stub"; } else if (cb != thunk.callbacks.end() && cb->second.is_guest) { return "fex_guest_function_ptr { args->a_" + std::to_string(idx) + " }"; + } else if (cb != thunk.callbacks.end()) { + auto arg_name = "args->a_" + std::to_string(idx); + // Use comma operator to inject a function call before returning the argument + return "(FinalizeHostTrampolineForGuestFunction(" + arg_name + "), " + arg_name + ")"; + } else { return "args->a_" + std::to_string(idx); } @@ -719,21 +682,6 @@ void GenerateThunkLibsAction::EndSourceFileAction() { file << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c; } file << "\", (void(*)(void *))&fexfn_unpack_" << libname << "_" << function_name << "}, // " << libname << ":" << function_name << "\n"; - - for (auto& [cb_idx, cb] : thunk.callbacks) { - if (cb.is_stub || cb.is_guest) - continue; - bool is_first_cb = (cb_idx == thunk.callbacks.begin()->first); - auto cb_function_name = get_callback_name(function_name, cb_idx, is_first_cb); - - auto cb_sha256 = get_sha256(cb_function_name); - - file << "{(uint8_t*)\""; - for (auto c : cb_sha256) { - file << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c; - } - file << "\", (void(*)(void *))&fexfn_pack_guestcall_" << cb_function_name << "}, // " << libname << ":" << cb_function_name << "\n"; - } } for (auto& type : funcptr_types) { @@ -784,31 +732,8 @@ void GenerateThunkLibsAction::EndSourceFileAction() { } if (!output_filenames.callback_unpacks.empty()) { + // TODO: Not needed anymore std::ofstream file(output_filenames.callback_unpacks); - - for (auto& thunk : thunks) { - for (const auto& [cb_idx, cb] : thunk.callbacks) { - if (cb.is_stub || cb.is_guest) { - continue; - } - - bool is_void = cb.return_type->isVoidType(); - bool is_first_cb = (cb_idx == thunk.callbacks.begin()->first); - auto cb_function_name = get_callback_name(thunk.function_name, cb_idx, is_first_cb); - file << "static void fexfn_unpack_" << libname << "_" << cb_function_name << "(uintptr_t cb, void* argsv) {\n"; - file << " typedef " << cb.return_type.getAsString() << " fn_t (" << format_function_params(cb) << ");\n"; - file << " auto callback = reinterpret_cast(cb);\n"; - file << " struct arg_t {\n"; - file << format_struct_members(cb, " "); - if (!is_void) { - file << " " << format_decl(cb.return_type, "rv") << ";\n"; - } - file << " };\n"; - file << " auto args = (arg_t*)argsv;\n"; - file << (is_void ? " " : " args->rv = ") << "callback(" << format_function_args(cb) << ");\n"; - file << "}\n"; - } - } } if (!output_filenames.symbol_list.empty()) { diff --git a/ThunkLibs/HostLibs/CMakeLists.txt b/ThunkLibs/HostLibs/CMakeLists.txt index 9b55fc955..771d5e26b 100644 --- a/ThunkLibs/HostLibs/CMakeLists.txt +++ b/ThunkLibs/HostLibs/CMakeLists.txt @@ -13,6 +13,7 @@ function(generate NAME SOURCE_FILE) # Interface target for the user to add include directories add_library(${NAME}-deps INTERFACE) target_include_directories(${NAME}-deps INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/../include") + target_link_libraries(${NAME}-deps INTERFACE FEXLoader) # 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 "$") diff --git a/ThunkLibs/include/common/Guest.h b/ThunkLibs/include/common/Guest.h index 525f583c1..00c1f07f1 100644 --- a/ThunkLibs/include/common/Guest.h +++ b/ThunkLibs/include/common/Guest.h @@ -49,7 +49,7 @@ MAKE_THUNK(fex, loadlib, "0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, MAKE_THUNK(fex, is_lib_loaded, "0xee, 0x57, 0xba, 0x0c, 0x5f, 0x6e, 0xef, 0x2a, 0x8c, 0xb5, 0x19, 0x81, 0xc9, 0x23, 0xe6, 0x51, 0xae, 0x65, 0x02, 0x8f, 0x2b, 0x5d, 0x59, 0x90, 0x6a, 0x7e, 0xe2, 0xe7, 0x1c, 0x33, 0x8a, 0xff") MAKE_THUNK(fex, is_host_heap_allocation, "0xf5, 0x77, 0x68, 0x43, 0xbb, 0x6b, 0x28, 0x18, 0x40, 0xb0, 0xdb, 0x8a, 0x66, 0xfb, 0x0e, 0x2d, 0x98, 0xc2, 0xad, 0xe2, 0x5a, 0x18, 0x5a, 0x37, 0x2e, 0x13, 0xc9, 0xe7, 0xb9, 0x8c, 0xa9, 0x3e") 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") -MAKE_THUNK(fex, make_host_trampoline_for_guest_function, "0x1e, 0x51, 0x6b, 0x07, 0x39, 0xeb, 0x50, 0x59, 0xb3, 0xf3, 0x4f, 0xca, 0xdd, 0x58, 0x37, 0xe9, 0xf0, 0x30, 0xe5, 0x89, 0x81, 0xc7, 0x14, 0xfb, 0x24, 0xf9, 0xba, 0xe7, 0x0e, 0x00, 0x1e, 0x86") +MAKE_THUNK(fex, allocate_host_trampoline_for_guest_function, "0x9b, 0xb2, 0xf4, 0xb4, 0x83, 0x7d, 0x28, 0x93, 0x40, 0xcb, 0xf4, 0x7a, 0x0b, 0x47, 0x85, 0x87, 0xf9, 0xbc, 0xb5, 0x27, 0xca, 0xa6, 0x93, 0xa5, 0xc0, 0x73, 0x27, 0x24, 0xae, 0xc8, 0xb8, 0x5a") #define LOAD_LIB_BASE(name, init_fn) \ __attribute__((constructor)) static void loadlib() \ @@ -118,23 +118,45 @@ static auto GetCallerForHostFunction(Result (*host_func)(Args...)) } template -inline Target *MakeHostTrampolineForGuestFunction(uint8_t HostPacker[32], void (*GuestUnpacker)(uintptr_t, void*), Target *GuestTarget) { +inline Target *AllocateHostTrampolineForGuestFunction(void (*GuestUnpacker)(uintptr_t, void*), Target *GuestTarget) { if (!GuestTarget) { return nullptr; } struct { - void *HostPacker; uintptr_t GuestUnpacker; uintptr_t GuestTarget; uintptr_t rv; - } argsrv = { HostPacker, (uintptr_t)GuestUnpacker, (uintptr_t)GuestTarget }; + } argsrv = { (uintptr_t)GuestUnpacker, (uintptr_t)GuestTarget }; - fexthunks_fex_make_host_trampoline_for_guest_function((void*)&argsrv); + fexthunks_fex_allocate_host_trampoline_for_guest_function((void*)&argsrv); return (Target *)argsrv.rv; } +template +struct CallbackUnpack; + +template +struct CallbackUnpack { + static void Unpack(uintptr_t cb, void* argsv) { + using fn_t = Result(Args...); + auto callback = reinterpret_cast(cb); + auto args = reinterpret_cast*>(argsv); + Invoke(callback, *args); + } +}; + +template +struct CallbackUnpack : CallbackUnpack { +}; + +template +inline Target *AllocateHostTrampolineForGuestFunction(Target *GuestTarget) { + return AllocateHostTrampolineForGuestFunction(CallbackUnpack::Unpack, + GuestTarget); +} + inline bool IsHostHeapAllocation(void* ptr) { struct { void* ptr; diff --git a/ThunkLibs/include/common/Host.h b/ThunkLibs/include/common/Host.h index 568d8d96e..d9b24f655 100644 --- a/ThunkLibs/include/common/Host.h +++ b/ThunkLibs/include/common/Host.h @@ -5,10 +5,35 @@ $end_info$ */ #pragma once -#include +#include +#include +#include #include "PackedArguments.h" +// Import FEXCore functions for use in host thunk libraries. +// +// Note these are statically linked into the FEX executable. The linker hence +// doesn't know about them when linking thunk libraries. This issue is avoided +// by declaring the functions as weak symbols. Their implementation in this +// file serves as a panicking fallback if matching symbols are not found. +namespace FEXCore { + struct HostToGuestTrampolinePtr; + + __attribute__((weak)) + HostToGuestTrampolinePtr* + MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) { + fprintf(stderr, "Failed to load %s from FEX executable\n", __FUNCTION__); + std::abort(); + } + __attribute__((weak)) + HostToGuestTrampolinePtr* + FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr*, void* HostPacker) { + fprintf(stderr, "Failed to load %s from FEX executable\n", __FUNCTION__); + std::abort(); + } +} + template struct function_traits; template @@ -47,7 +72,7 @@ public: #define EXPORTS(name) \ extern "C" { \ - ExportEntry* fexthunks_exports_##name() { \ + ExportEntry* fexthunks_exports_##name(uintptr_t allocate, uintptr_t finalize) { \ if (!fexldr_init_##name()) { \ return nullptr; \ } \ @@ -77,6 +102,9 @@ struct GuestcallInfo { #elif defined(_M_ARM_64) #define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) \ asm volatile("mov %0, x11" : "=r" (target_variable)) +#else +#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) \ + abort() #endif template @@ -84,6 +112,19 @@ struct CallbackUnpack; template struct CallbackUnpack { + static Result CallGuestPtr(Args... args) { + GuestcallInfo *guestcall; + LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(guestcall); + + PackedArguments packed_args = { + args... + }; + guestcall->CallCallback(guestcall->GuestUnpacker, guestcall->GuestTarget, &packed_args); + if constexpr (!std::is_void_v) { + return packed_args.rv; + } + } + static void ForIndirectCall(void* argsv) { auto args = reinterpret_cast*>(argsv); constexpr auto CBIndex = sizeof...(Args); @@ -133,6 +174,17 @@ struct CallbackUnpack { } }; -template -struct CallbackUnpack : CallbackUnpack { -}; +template +void MakeHostTrampolineForGuestFunctionAt(uintptr_t GuestTarget, uintptr_t GuestUnpacker, FuncType **Func) { + *Func = (FuncType*)FEXCore::MakeHostTrampolineForGuestFunction( + (void*)&CallbackUnpack::CallGuestPtr, + GuestTarget, + GuestUnpacker); +} + +template +void FinalizeHostTrampolineForGuestFunction(F* PreallocatedTrampolineForGuestFunction) { + FEXCore::FinalizeHostTrampolineForGuestFunction( + (FEXCore::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction, + (void*)&CallbackUnpack::CallGuestPtr); +} diff --git a/unittests/ThunkLibs/generator.cpp b/unittests/ThunkLibs/generator.cpp index dd37ddcdd..8c285d178 100644 --- a/unittests/ThunkLibs/generator.cpp +++ b/unittests/ThunkLibs/generator.cpp @@ -285,7 +285,9 @@ SourceWithAST Fixture::run_thunkgen_guest(std::string_view prelude, std::string_ "#define MAKE_CALLBACK_THUNK(name, sig, hash) template<> struct callback_thunk_defined {};\n" "#define FEX_PACKFN_LINKAGE\n" "template\n" - "Target *MakeHostTrampolineForGuestFunction(uint8_t HostPacker[32], void (*)(uintptr_t, void*), Target*);\n"; + "Target *MakeHostTrampolineForGuestFunction(uint8_t HostPacker[32], void (*)(uintptr_t, void*), Target*);\n" + "template\n" + "Target *AllocateHostTrampolineForGuestFunction(Target*);\n"; for (auto& filename : { output_filenames.thunks, output_filenames.callback_unpacks, @@ -334,7 +336,9 @@ SourceWithAST Fixture::run_thunkgen_host(std::string_view prelude, std::string_v "template\n" "struct CallbackUnpack {\n" " static void ForIndirectCall(void* argsv);\n" - "};\n"; + "};\n" + "template\n" + "void FinalizeHostTrampolineForGuestFunction(F*);\n"; for (auto& filename : { output_filenames.ldr_ptrs, @@ -459,17 +463,24 @@ TEST_CASE_METHOD(Fixture, "FunctionPointerParameter") { hasName("fexfn_pack_func"), returns(asString("void")), parameterCountIs(1), - hasParameter(0, hasType(asString("int (*)(char, char)"))), - - // Should call MakeHostTrampolineForGuestFunction - hasDescendant(callExpr(callee(functionDecl(hasName("MakeHostTrampolineForGuestFunction"))))) + hasParameter(0, hasType(asString("int (*)(char, char)"))) ))); + // Host packing function should call FinalizeHostTrampolineForGuestFunction on the argument + CHECK_THAT(output.host, + matches(functionDecl( + hasName("fexfn_unpack_libtest_func"), + hasDescendant(callExpr(callee(functionDecl(hasName("FinalizeHostTrampolineForGuestFunction"))), hasArgument(0, expr().bind("funcptr")))) + )).check_binding("funcptr", +[](const clang::Expr* funcptr) { + // Check that the argument type matches the function pointer + return funcptr->getType().getAsString() == "int (*)(char, char)"; + })); + // Host should export the unpacking function for function pointer arguments CHECK_THAT(output.host, matches(varDecl( hasName("exports"), - hasType(constantArrayType(hasElementType(asString("struct ExportEntry")), hasSize(4))), + hasType(constantArrayType(hasElementType(asString("struct ExportEntry")), hasSize(3))), hasInitializer(hasDescendant(declRefExpr(to(cxxMethodDecl(hasName("ForIndirectCall"), ofClass(hasName("CallbackUnpack"))))))) ))); } From 3524117c238900560cb61ef92f266cede996b898 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 6 Aug 2022 12:06:18 +0200 Subject: [PATCH 10/18] Thunks: Unindent code blocks from previous patch --- .../Source/Interface/HLE/Thunks/Thunks.cpp | 170 +++++++++--------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp index d899b5ce8..a825d29bc 100644 --- a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp +++ b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp @@ -335,97 +335,97 @@ namespace FEXCore { return new ThunkHandler_impl(); } - /** - * Generates a host-callable trampoline to call guest functions via the host ABI. - * - * This trampoline uses the same calling convention as the given HostPacker. Trampolines - * are cached, so it's safe to call this function repeatedly on the same arguments without - * leaking memory. - * - * Invoking the returned trampoline has the effect of: - * - packing the arguments (using the HostPacker identified by its SHA256) - * - performing a host->guest transition - * - unpacking the arguments via GuestUnpacker - * - calling the function at GuestTarget - * - * The primary use case of this is ensuring that guest function pointers ("callbacks") - * passed to thunked APIs can safely be called by the native host library. - * - * Returns a pointer to the generated host trampoline and its TrampolineInstanceInfo. - * - * If HostPacker is zero, the trampoline will be partially initialized and needs to be - * finalized with a call to FinalizeHostTrampolineForGuestFunction. A typical use case - * is to allocate the trampoline for a given GuestTarget/GuestUnpacker on the guest-side, - * and provide the HostPacker host-side. - */ - __attribute__((visibility("default"))) - HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) { - LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function"); + /** + * Generates a host-callable trampoline to call guest functions via the host ABI. + * + * This trampoline uses the same calling convention as the given HostPacker. Trampolines + * are cached, so it's safe to call this function repeatedly on the same arguments without + * leaking memory. + * + * Invoking the returned trampoline has the effect of: + * - packing the arguments (using the HostPacker identified by its SHA256) + * - performing a host->guest transition + * - unpacking the arguments via GuestUnpacker + * - calling the function at GuestTarget + * + * The primary use case of this is ensuring that guest function pointers ("callbacks") + * passed to thunked APIs can safely be called by the native host library. + * + * Returns a pointer to the generated host trampoline and its TrampolineInstanceInfo. + * + * If HostPacker is zero, the trampoline will be partially initialized and needs to be + * finalized with a call to FinalizeHostTrampolineForGuestFunction. A typical use case + * is to allocate the trampoline for a given GuestTarget/GuestUnpacker on the guest-side, + * and provide the HostPacker host-side. + */ + __attribute__((visibility("default"))) + HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) { + LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function"); - const auto CTX = Thread->CTX; - const auto ThunkHandler = reinterpret_cast(CTX->ThunkHandler.get()); + const auto CTX = Thread->CTX; + const auto ThunkHandler = reinterpret_cast(CTX->ThunkHandler.get()); - const GuestcallInfo gci = { GuestUnpacker, GuestTarget }; + const GuestcallInfo gci = { GuestUnpacker, GuestTarget }; - // Try first with shared_lock - { - std::shared_lock lk(ThunkHandler->ThunksMutex); + // Try first with shared_lock + { + std::shared_lock lk(ThunkHandler->ThunksMutex); - auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci); - if (found != ThunkHandler->GuestcallToHostTrampoline.end()) { - return found->second; - } - } - - std::lock_guard lk(ThunkHandler->ThunksMutex); - - // Retry lookup with full lock before making a new trampoline to avoid double trampolines - { - auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci); - if (found != ThunkHandler->GuestcallToHostTrampoline.end()) { - return found->second; - } - } - - LogMan::Msg::DFmt("Thunks: Adding host trampoline for guest function {:#x} via unpacker {:#x}", - GuestTarget, GuestUnpacker); - - if (ThunkHandler->HostTrampolineInstanceDataAvailable < HostToGuestTrampolineSize) { - const auto allocation_step = 16 * 1024; - ThunkHandler->HostTrampolineInstanceDataAvailable = allocation_step; - ThunkHandler->HostTrampolineInstanceDataPtr = (uint8_t *)mmap( - 0, ThunkHandler->HostTrampolineInstanceDataAvailable, - PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - - LOGMAN_THROW_AA_FMT(ThunkHandler->HostTrampolineInstanceDataPtr != MAP_FAILED, "Failed to mmap HostTrampolineInstanceDataPtr"); - } - - auto HostTrampoline = reinterpret_cast(ThunkHandler->HostTrampolineInstanceDataPtr); - ThunkHandler->HostTrampolineInstanceDataAvailable -= HostToGuestTrampolineSize; - ThunkHandler->HostTrampolineInstanceDataPtr += HostToGuestTrampolineSize; - memcpy(HostTrampoline, (void*)&HostToGuestTrampolineTemplate, HostToGuestTrampolineSize); - GetInstanceInfo(HostTrampoline) = TrampolineInstanceInfo { - .HostPacker = HostPacker, - .CallCallback = (uintptr_t)&ThunkHandler_impl::CallCallback, - .GuestUnpacker = GuestUnpacker, - .GuestTarget = GuestTarget - }; - - ThunkHandler->GuestcallToHostTrampoline[gci] = HostTrampoline; - return HostTrampoline; + auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci); + if (found != ThunkHandler->GuestcallToHostTrampoline.end()) { + return found->second; } + } - __attribute__((visibility("default"))) - void FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr* TrampolineAddress, void* HostPacker) { - auto& Trampoline = GetInstanceInfo(TrampolineAddress); + std::lock_guard lk(ThunkHandler->ThunksMutex); - LOGMAN_THROW_A_FMT(Trampoline.CallCallback == (uintptr_t)&ThunkHandler_impl::CallCallback, - "Invalid trampoline at {} passed to {}", fmt::ptr(TrampolineAddress), __FUNCTION__); - - if (!Trampoline.HostPacker) { - LogMan::Msg::DFmt("Thunks: Finalizing trampoline at {} with host packer {}", fmt::ptr(TrampolineAddress), fmt::ptr(HostPacker)); - Trampoline.HostPacker = HostPacker; - } + // Retry lookup with full lock before making a new trampoline to avoid double trampolines + { + auto found = ThunkHandler->GuestcallToHostTrampoline.find(gci); + if (found != ThunkHandler->GuestcallToHostTrampoline.end()) { + return found->second; } + } + + LogMan::Msg::DFmt("Thunks: Adding host trampoline for guest function {:#x} via unpacker {:#x}", + GuestTarget, GuestUnpacker); + + if (ThunkHandler->HostTrampolineInstanceDataAvailable < HostToGuestTrampolineSize) { + const auto allocation_step = 16 * 1024; + ThunkHandler->HostTrampolineInstanceDataAvailable = allocation_step; + ThunkHandler->HostTrampolineInstanceDataPtr = (uint8_t *)mmap( + 0, ThunkHandler->HostTrampolineInstanceDataAvailable, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + + LOGMAN_THROW_AA_FMT(ThunkHandler->HostTrampolineInstanceDataPtr != MAP_FAILED, "Failed to mmap HostTrampolineInstanceDataPtr"); + } + + auto HostTrampoline = reinterpret_cast(ThunkHandler->HostTrampolineInstanceDataPtr); + ThunkHandler->HostTrampolineInstanceDataAvailable -= HostToGuestTrampolineSize; + ThunkHandler->HostTrampolineInstanceDataPtr += HostToGuestTrampolineSize; + memcpy(HostTrampoline, (void*)&HostToGuestTrampolineTemplate, HostToGuestTrampolineSize); + GetInstanceInfo(HostTrampoline) = TrampolineInstanceInfo { + .HostPacker = HostPacker, + .CallCallback = (uintptr_t)&ThunkHandler_impl::CallCallback, + .GuestUnpacker = GuestUnpacker, + .GuestTarget = GuestTarget + }; + + ThunkHandler->GuestcallToHostTrampoline[gci] = HostTrampoline; + return HostTrampoline; + } + + __attribute__((visibility("default"))) + void FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr* TrampolineAddress, void* HostPacker) { + auto& Trampoline = GetInstanceInfo(TrampolineAddress); + + LOGMAN_THROW_A_FMT(Trampoline.CallCallback == (uintptr_t)&ThunkHandler_impl::CallCallback, + "Invalid trampoline at {} passed to {}", fmt::ptr(TrampolineAddress), __FUNCTION__); + + if (!Trampoline.HostPacker) { + LogMan::Msg::DFmt("Thunks: Finalizing trampoline at {} with host packer {}", fmt::ptr(TrampolineAddress), fmt::ptr(HostPacker)); + Trampoline.HostPacker = HostPacker; + } + } } From 7ae59055ffdf670f9443ad7bbd091be19af52445 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 29 Jul 2022 18:21:03 +0200 Subject: [PATCH 11/18] Thunks/X11: Handle function pointer thunking for XInitThreads and _XReply --- ThunkLibs/libX11/libX11_Guest.cpp | 26 +++++++++++++++++++++ ThunkLibs/libX11/libX11_Host.cpp | 33 +++++++++++++++++++++++++-- ThunkLibs/libX11/libX11_interface.cpp | 13 ++++++++++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/ThunkLibs/libX11/libX11_Guest.cpp b/ThunkLibs/libX11/libX11_Guest.cpp index 46fa16020..6f91fefcd 100644 --- a/ThunkLibs/libX11/libX11_Guest.cpp +++ b/ThunkLibs/libX11/libX11_Guest.cpp @@ -9,6 +9,7 @@ $end_info$ #include #include +#include #include // Include Xlibint.h and undefine some of its macros that clash with the standard library @@ -167,6 +168,31 @@ extern "C" { return ret; } + Status _XReply(Display* display, xReply* reply, int extra, Bool discard) { + for(auto handler = display->async_handlers; handler; handler = handler->next) { + // Make host-callable and overwrite in-place + // NOTE: This data seems to be stack-allocated specifically for XReply usually, so it's *probably* safe to overwrite + handler->handler = AllocateHostTrampolineForGuestFunction(handler->handler); + } + return fexfn_pack__XReply(display, reply, extra, discard); + } + + static int _XInitDisplayLock(Display* display) { + { + auto caller = (uintptr_t)GetCallerForHostFunction(display->lock_fns->lock_display); + LinkAddressToFunction((uintptr_t)display->lock_fns->lock_display, (uintptr_t)caller); + } + { + auto caller = (uintptr_t)GetCallerForHostFunction(display->lock_fns->unlock_display); + LinkAddressToFunction((uintptr_t)display->lock_fns->unlock_display, (uintptr_t)caller); + } + return 0; + } + + Status XInitThreads() { + return fexfn_pack_XInitThreadsInternal((uintptr_t)_XInitDisplayLock, (uintptr_t)CallbackUnpack::Unpack); + } + void (*_XLockMutex_fn)(LockInfoPtr) = LockMutexFunction; void (*_XUnlockMutex_fn)(LockInfoPtr) = UnlockMutexFunction; LockInfoPtr _Xglobal_lock = (LockInfoPtr)0x4142434445464748ULL; diff --git a/ThunkLibs/libX11/libX11_Host.cpp b/ThunkLibs/libX11/libX11_Host.cpp index f873424a0..c9845a125 100644 --- a/ThunkLibs/libX11/libX11_Host.cpp +++ b/ThunkLibs/libX11/libX11_Host.cpp @@ -9,8 +9,6 @@ $end_info$ #include #include - -// Include Xlibint.h and undefine some of its macros that clash with the standard library #include #undef min #undef max @@ -18,10 +16,13 @@ $end_info$ #include #include +#include + #include #include "common/Host.h" #include +#include #include "ldr_ptrs.inl" @@ -75,6 +76,10 @@ char* fexfn_impl_libX11_XGetIMValues_internal(XIM a_0, size_t count, void **list } } +Status fexfn_impl_libX11_XInitThreadsInternal(uintptr_t, uintptr_t); + +Status fexfn_impl_libX11__XReply(Display*, xReply*, int, Bool); + #include "function_unpacks.inl" static ExportEntry exports[] = { @@ -84,4 +89,28 @@ static ExportEntry exports[] = { #include "ldr.inl" +static int (*ACTUAL_XInitDisplayLock_fn)(Display*) = nullptr; +static int (*INTERNAL_XInitDisplayLock_fn)(Display*) = nullptr; + +static int _XInitDisplayLock(Display* display) { + auto ret = ACTUAL_XInitDisplayLock_fn(display); + INTERNAL_XInitDisplayLock_fn(display); + return ret; +} + +Status fexfn_impl_libX11_XInitThreadsInternal(uintptr_t GuestTarget, uintptr_t GuestUnpacker) { + auto ret = fexldr_ptr_libX11_XInitThreads(); + auto _XInitDisplayLock_fn = (int(**)(Display*))dlsym(fexldr_ptr_libX11_so, "_XInitDisplayLock_fn"); + ACTUAL_XInitDisplayLock_fn = std::exchange(*_XInitDisplayLock_fn, _XInitDisplayLock); + MakeHostTrampolineForGuestFunctionAt(GuestTarget, GuestUnpacker, &INTERNAL_XInitDisplayLock_fn); + return ret; +} + +Status fexfn_impl_libX11__XReply(Display* display, xReply* reply, int extra, Bool discard) { + for(auto handler = display->async_handlers; handler; handler = handler->next) { + FinalizeHostTrampolineForGuestFunction(handler->handler); + } + return fexldr_ptr_libX11__XReply(display, reply, extra, discard); +} + EXPORTS(libX11) diff --git a/ThunkLibs/libX11/libX11_interface.cpp b/ThunkLibs/libX11/libX11_interface.cpp index 6ae995aa6..1337f1b50 100644 --- a/ThunkLibs/libX11/libX11_interface.cpp +++ b/ThunkLibs/libX11/libX11_interface.cpp @@ -6,6 +6,8 @@ #include +#include + #include #include @@ -492,7 +494,12 @@ 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 {}; + +// Sets up vtables for various objects +Status XInitThreadsInternal(uintptr_t OnXInitDisplayLock, uintptr_t OnXInitDisplayLockPacker); +template<> struct fex_gen_config : fexgen::custom_guest_entrypoint {}; +template<> struct fex_gen_config : fexgen::custom_host_impl, fexgen::custom_guest_entrypoint {}; + template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -582,6 +589,10 @@ template<> struct fex_gen_config<_XReadPad> {}; template<> struct fex_gen_config<_XData32> {}; template<> struct fex_gen_config<_XEatData> {}; template<> struct fex_gen_config<_XEatDataWords> {}; + +template<> struct fex_gen_type {}; // XDisplay::async_handlers->handler +template<> struct fex_gen_config<_XReply> : fexgen::custom_host_impl, fexgen::custom_guest_entrypoint {}; + template<> struct fex_gen_config<_XGetAsyncReply> {}; template<> struct fex_gen_config<_XSend> {}; template<> struct fex_gen_config<_XFlush> {}; From 31b5181bca5d5f90d6e3ea6ee65617c9df7a5d81 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 6 Aug 2022 12:21:40 +0200 Subject: [PATCH 12/18] Thunks/gen: Drop now unneeded callback_unpacks file --- ThunkLibs/Generator/gen.cpp | 5 ----- ThunkLibs/Generator/interface.h | 2 -- ThunkLibs/Generator/main.cpp | 2 -- ThunkLibs/GuestLibs/CMakeLists.txt | 6 +++--- ThunkLibs/README.md | 1 - ThunkLibs/libX11/libX11_Guest.cpp | 1 - ThunkLibs/libXext/libXext_Guest.cpp | 1 - ThunkLibs/libxcb/libxcb_Guest.cpp | 1 - unittests/ThunkLibs/generator.cpp | 2 -- 9 files changed, 3 insertions(+), 18 deletions(-) diff --git a/ThunkLibs/Generator/gen.cpp b/ThunkLibs/Generator/gen.cpp index 507f1f259..059fb4e23 100644 --- a/ThunkLibs/Generator/gen.cpp +++ b/ThunkLibs/Generator/gen.cpp @@ -731,11 +731,6 @@ void GenerateThunkLibsAction::EndSourceFileAction() { } } - if (!output_filenames.callback_unpacks.empty()) { - // TODO: Not needed anymore - std::ofstream file(output_filenames.callback_unpacks); - } - if (!output_filenames.symbol_list.empty()) { std::ofstream file(output_filenames.symbol_list); diff --git a/ThunkLibs/Generator/interface.h b/ThunkLibs/Generator/interface.h index 67eeb3077..87092f8f1 100644 --- a/ThunkLibs/Generator/interface.h +++ b/ThunkLibs/Generator/interface.h @@ -17,8 +17,6 @@ struct OutputFilenames { std::string function_packs_public; // Guest + Host - std::string callback_unpacks; - std::string symbol_list; }; diff --git a/ThunkLibs/Generator/main.cpp b/ThunkLibs/Generator/main.cpp index 69e4e8bf9..184e57dc6 100644 --- a/ThunkLibs/Generator/main.cpp +++ b/ThunkLibs/Generator/main.cpp @@ -51,8 +51,6 @@ int main(int argc, char* argv[]) { output_filenames.function_packs = out_filename; } else if (target == "-function_packs_public") { output_filenames.function_packs_public = out_filename; - } else if (target == "-callback_unpacks") { - output_filenames.callback_unpacks = out_filename; } else if (target == "-symbol_list") { output_filenames.symbol_list = out_filename; } else { diff --git a/ThunkLibs/GuestLibs/CMakeLists.txt b/ThunkLibs/GuestLibs/CMakeLists.txt index 03d52b0b2..72d23c070 100644 --- a/ThunkLibs/GuestLibs/CMakeLists.txt +++ b/ThunkLibs/GuestLibs/CMakeLists.txt @@ -103,10 +103,10 @@ target_link_libraries(GL-guest PRIVATE X11) # target_link_libraries(SDL2-guest PRIVATE GL) # target_link_libraries(SDL2-guest PRIVATE dl) -generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp thunks function_packs function_packs_public callback_unpacks) +generate(libX11 ${CMAKE_CURRENT_SOURCE_DIR}/../libX11/libX11_interface.cpp thunks function_packs function_packs_public) add_guest_lib(X11) -generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp thunks function_packs function_packs_public callback_unpacks) +generate(libXext ${CMAKE_CURRENT_SOURCE_DIR}/../libXext/libXext_interface.cpp thunks function_packs function_packs_public) add_guest_lib(Xext) generate(libXrender ${CMAKE_CURRENT_SOURCE_DIR}/../libXrender/libXrender_interface.cpp thunks function_packs function_packs_public) @@ -119,7 +119,7 @@ generate(libvulkan ${CMAKE_CURRENT_SOURCE_DIR}/../libvulkan/libvulkan_interface. target_include_directories(libvulkan-guest-deps INTERFACE ${FEX_PROJECT_SOURCE_DIR}/External/Vulkan-Headers/include/) add_guest_lib(vulkan) -generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp thunks function_packs function_packs_public callback_unpacks) +generate(libxcb ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb/libxcb_interface.cpp thunks function_packs function_packs_public) add_guest_lib(xcb) generate(libxcb-dri2 ${CMAKE_CURRENT_SOURCE_DIR}/../libxcb-dri2/libxcb-dri2_interface.cpp thunks function_packs function_packs_public) diff --git a/ThunkLibs/README.md b/ThunkLibs/README.md index 09c20c9f9..32cb8d1b1 100644 --- a/ThunkLibs/README.md +++ b/ThunkLibs/README.md @@ -65,7 +65,6 @@ For each thunked library, the generator outputs the following files: - `ldr.inl`: Host loader that dlopens/dlsyms the "real" host library for the implementation functions. - `ldr_ptrs.inl`: Host loader pointer declarations, used by ldr and function_unpacks - `tab_function_unpacks.inl`: Host function unpackers list, passed to FEX after Host library init so it can resolve the Guest Thunks to Host functions -- `callback_unpacks.inl`: Guest callback unpackers ## Adding a new library diff --git a/ThunkLibs/libX11/libX11_Guest.cpp b/ThunkLibs/libX11/libX11_Guest.cpp index 6f91fefcd..5102a80eb 100644 --- a/ThunkLibs/libX11/libX11_Guest.cpp +++ b/ThunkLibs/libX11/libX11_Guest.cpp @@ -27,7 +27,6 @@ $end_info$ #include #include "thunks.inl" -#include "callback_unpacks.inl" #include "function_packs.inl" #include "function_packs_public.inl" diff --git a/ThunkLibs/libXext/libXext_Guest.cpp b/ThunkLibs/libXext/libXext_Guest.cpp index 1f80fcbe4..26b1af826 100644 --- a/ThunkLibs/libXext/libXext_Guest.cpp +++ b/ThunkLibs/libXext/libXext_Guest.cpp @@ -40,7 +40,6 @@ extern "C" { #include "common/Guest.h" #include "thunks.inl" -#include "callback_unpacks.inl" #include "function_packs.inl" #include "function_packs_public.inl" diff --git a/ThunkLibs/libxcb/libxcb_Guest.cpp b/ThunkLibs/libxcb/libxcb_Guest.cpp index 1ce77f368..f79103247 100644 --- a/ThunkLibs/libxcb/libxcb_Guest.cpp +++ b/ThunkLibs/libxcb/libxcb_Guest.cpp @@ -29,7 +29,6 @@ $end_info$ #include "thunks.inl" #include "function_packs.inl" #include "function_packs_public.inl" -#include "callback_unpacks.inl" static std::thread CBThread{}; static std::atomic CBDone{false}; diff --git a/unittests/ThunkLibs/generator.cpp b/unittests/ThunkLibs/generator.cpp index 8c285d178..ee7c1f41e 100644 --- a/unittests/ThunkLibs/generator.cpp +++ b/unittests/ThunkLibs/generator.cpp @@ -57,7 +57,6 @@ struct Fixture { tmpdir + "/thunks", tmpdir + "/function_packs", tmpdir + "/function_packs_public", - tmpdir + "/callback_unpacks", }; } @@ -290,7 +289,6 @@ SourceWithAST Fixture::run_thunkgen_guest(std::string_view prelude, std::string_ "Target *AllocateHostTrampolineForGuestFunction(Target*);\n"; for (auto& filename : { output_filenames.thunks, - output_filenames.callback_unpacks, output_filenames.function_packs_public, output_filenames.function_packs, }) { From 0317d381fedb25b4bdaf0cea7157f32c0047c18a Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Tue, 2 Aug 2022 20:10:51 +0200 Subject: [PATCH 13/18] Thunks/X11: Add definitions for more API functions --- ThunkLibs/libX11/libX11_Guest.cpp | 71 +++++++++++++++++++++++++++ ThunkLibs/libX11/libX11_Host.cpp | 58 +++++++++++++++++++--- ThunkLibs/libX11/libX11_interface.cpp | 23 ++++++++- 3 files changed, 144 insertions(+), 8 deletions(-) diff --git a/ThunkLibs/libX11/libX11_Guest.cpp b/ThunkLibs/libX11/libX11_Guest.cpp index 5102a80eb..1482457bd 100644 --- a/ThunkLibs/libX11/libX11_Guest.cpp +++ b/ThunkLibs/libX11/libX11_Guest.cpp @@ -56,6 +56,25 @@ extern "C" { return rv; } + char* XSetICValues(XIC ic, ...) { + fprintf(stderr, "XSetICValues\n"); + va_list ap; + std::vector args; + va_start(ap, ic); + for (;;) { + auto arg = va_arg(ap, unsigned long); + if (arg == 0) + break; + args.push_back(arg); + fprintf(stderr, "%016lX\n", arg); + } + + va_end(ap); + auto rv = fexfn_pack_XSetICValues_internal(ic, args.size(), &args[0]); + fprintf(stderr, "RV: %p\n", rv); + return rv; + } + char* XGetIMValues(XIM ic, ...) { fprintf(stderr, "XGetIMValues\n"); va_list ap; @@ -75,6 +94,25 @@ extern "C" { return rv; } + char* XSetIMValues(XIM ic, ...) { + fprintf(stderr, "XSetIMValues\n"); + va_list ap; + std::vector args; + va_start(ap, ic); + for (;;) { + auto arg = va_arg(ap, void*); + if (arg == 0) + break; + args.push_back(arg); + fprintf(stderr, "%p\n", arg); + } + + va_end(ap); + auto rv = fexfn_pack_XSetIMValues_internal(ic, args.size(), &args[0]); + fprintf(stderr, "RV: %p\n", rv); + return rv; + } + _XIC* XCreateIC(XIM im, ...) { fprintf(stderr, "XCreateIC\n"); va_list ap; @@ -94,6 +132,25 @@ extern "C" { return rv; } + XVaNestedList XVaCreateNestedList(int unused_arg, ...) { + fprintf(stderr, "XVaCreateNestedList\n"); + va_list ap; + std::vector args; + va_start(ap, unused_arg); + for (;;) { + auto arg = va_arg(ap, void*); + if (arg == 0) + break; + args.push_back(arg); + fprintf(stderr, "%p\n", arg); + } + + va_end(ap); + auto rv = fexfn_pack_XVaCreateNestedList_internal(unused_arg, args.size(), &args[0]); + fprintf(stderr, "RV: %p\n", rv); + return rv; + } + static void LockMutexFunction(LockInfoPtr) { fprintf(stderr, "libX11: LockMutex\n"); } @@ -192,6 +249,20 @@ extern "C" { return fexfn_pack_XInitThreadsInternal((uintptr_t)_XInitDisplayLock, (uintptr_t)CallbackUnpack::Unpack); } + XImage *XCreateImage( + Display* display, Visual* visual, + unsigned int depth, int format, + int offset, char* data, + unsigned int width, unsigned int height, + int pad, int bpp) { + auto ret = fexfn_pack_XCreateImage(display, visual, depth, format, offset, data, width, height, pad, bpp); + { + auto caller = (uintptr_t)GetCallerForHostFunction(ret->f.destroy_image); + LinkAddressToFunction((uintptr_t)ret->f.destroy_image, (uintptr_t)caller); + } + return ret; + } + void (*_XLockMutex_fn)(LockInfoPtr) = LockMutexFunction; void (*_XUnlockMutex_fn)(LockInfoPtr) = UnlockMutexFunction; LockInfoPtr _Xglobal_lock = (LockInfoPtr)0x4142434445464748ULL; diff --git a/ThunkLibs/libX11/libX11_Host.cpp b/ThunkLibs/libX11/libX11_Host.cpp index c9845a125..56a386937 100644 --- a/ThunkLibs/libX11/libX11_Host.cpp +++ b/ThunkLibs/libX11/libX11_Host.cpp @@ -42,8 +42,6 @@ _XIC *fexfn_impl_libX11_XCreateIC_internal(XIM a_0, size_t count, unsigned long } } -static char ErrorReply[] = "FEX: Unable to match arg count"; - char* fexfn_impl_libX11_XGetICValues_internal(XIC a_0, size_t count, unsigned long *list) { switch(count) { case 0: return fexldr_ptr_libX11_XGetICValues(a_0, nullptr); break; @@ -55,8 +53,24 @@ char* fexfn_impl_libX11_XGetICValues_internal(XIC a_0, size_t count, unsigned lo case 6: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break; case 7: return fexldr_ptr_libX11_XGetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break; default: - fprintf(stderr, "XCreateICValues_internal FAILURE\n"); - return ErrorReply; + fprintf(stderr, "XGetICValues_internal FAILURE\n"); + abort(); + } +} + +char* fexfn_impl_libX11_XSetICValues_internal(XIC a_0, size_t count, unsigned long *list) { + switch(count) { + case 0: return fexldr_ptr_libX11_XSetICValues(a_0, nullptr); break; + case 1: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], nullptr); break; + case 2: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], nullptr); break; + case 3: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], nullptr); break; + case 4: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], nullptr); break; + case 5: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break; + case 6: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break; + case 7: return fexldr_ptr_libX11_XSetICValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break; + default: + fprintf(stderr, "XSetICValues_internal FAILURE\n"); + abort(); } } @@ -71,8 +85,40 @@ char* fexfn_impl_libX11_XGetIMValues_internal(XIM a_0, size_t count, void **list case 6: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break; case 7: return fexldr_ptr_libX11_XGetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break; default: - fprintf(stderr, "XCreateIMValues_internal FAILURE\n"); - return ErrorReply; + fprintf(stderr, "XGetIMValues_internal FAILURE\n"); + abort(); + } +} + +char* fexfn_impl_libX11_XSetIMValues_internal(XIM a_0, size_t count, void **list) { + switch(count) { + case 0: return fexldr_ptr_libX11_XSetIMValues(a_0, nullptr); break; + case 1: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], nullptr); break; + case 2: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], nullptr); break; + case 3: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], nullptr); break; + case 4: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], nullptr); break; + case 5: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], nullptr); break; + case 6: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break; + case 7: return fexldr_ptr_libX11_XSetIMValues(a_0, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break; + default: + fprintf(stderr, "XSetIMValues_internal FAILURE\n"); + abort(); + } +} + +XVaNestedList fexfn_impl_libX11_XVaCreateNestedList_internal(int unused_arg, size_t count, void** list) { + switch(count) { + case 0: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, nullptr); break; + case 1: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], nullptr); break; + case 2: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], nullptr); break; + case 3: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], nullptr); break; + case 4: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], nullptr); break; + case 5: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], nullptr); break; + case 6: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], nullptr); break; + case 7: return fexldr_ptr_libX11_XVaCreateNestedList(unused_arg, list[0], list[1], list[2], list[3], list[4], list[5], list[6], nullptr); break; + default: + fprintf(stderr, "XVaCreateNestedList_internal FAILURE\n"); + abort(); } } diff --git a/ThunkLibs/libX11/libX11_interface.cpp b/ThunkLibs/libX11/libX11_interface.cpp index 1337f1b50..278ff304c 100644 --- a/ThunkLibs/libX11/libX11_interface.cpp +++ b/ThunkLibs/libX11/libX11_interface.cpp @@ -446,6 +446,10 @@ template<> struct fex_gen_config { using uniform_va_type = unsigned long; }; +template<> struct fex_gen_config { + using uniform_va_type = unsigned long; +}; + template<> struct fex_gen_config { using uniform_va_type = unsigned long; }; @@ -548,9 +552,15 @@ 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_type {}; // XImage::f.destroy_image +template<> struct fex_gen_config : fexgen::custom_guest_entrypoint {}; + 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 {}; @@ -558,12 +568,17 @@ 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::returns_guest_pointer {}; template<> struct fex_gen_config { using uniform_va_type = void*; }; +template<> struct fex_gen_config { + using uniform_va_type = void*; +}; + template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; template<> struct fex_gen_config : fexgen::returns_guest_pointer {}; @@ -609,9 +624,13 @@ template<> struct fex_gen_config<_XFreeTemp> {}; template<> struct fex_gen_config<_XUnknownNativeEvent> {}; template<> struct fex_gen_config<_XIsEventCookie> {}; -template<> struct fex_gen_config {}; // TODO: Vtable support... template<> struct fex_gen_config<_XInitImageFuncPtrs> {}; + + +template<> struct fex_gen_config { + using uniform_va_type = void*; +}; From a2615f0e5208a9fc77f3c8d1000f52cf2155b57c Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 4 Aug 2022 16:53:02 +0200 Subject: [PATCH 14/18] Thunks/gen: Use uniform function naming for stub callbacks --- ThunkLibs/Generator/gen.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ThunkLibs/Generator/gen.cpp b/ThunkLibs/Generator/gen.cpp index 059fb4e23..225c3fffc 100644 --- a/ThunkLibs/Generator/gen.cpp +++ b/ThunkLibs/Generator/gen.cpp @@ -469,8 +469,8 @@ void GenerateThunkLibsAction::EndSourceFileAction() { return sha256; }; - auto get_callback_name = [](std::string_view function_name, unsigned param_index, bool is_first_cb) -> std::string { - return std::string { function_name } + "CBFN" + (is_first_cb ? "" : std::to_string(param_index)); + auto get_callback_name = [](std::string_view function_name, unsigned param_index) -> std::string { + return std::string { function_name } + "CBFN" + std::to_string(param_index); }; if (!output_filenames.thunks.empty()) { @@ -621,9 +621,8 @@ void GenerateThunkLibsAction::EndSourceFileAction() { /* Generate stub callbacks */ for (auto& [cb_idx, cb] : thunk.callbacks) { if (cb.is_stub) { - bool is_first_cb = (cb_idx == thunk.callbacks.begin()->first); const char* variadic_ellipsis = cb.is_variadic ? ", ..." : ""; - auto cb_function_name = "fexfn_unpack_" + get_callback_name(function_name, cb_idx, is_first_cb) + "_stub"; + auto cb_function_name = "fexfn_unpack_" + get_callback_name(function_name, cb_idx) + "_stub"; file << "[[noreturn]] static " << cb.return_type.getAsString() << " " << cb_function_name << "(" << format_function_params(cb) << variadic_ellipsis << ") {\n"; @@ -647,8 +646,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() { auto format_param = [&](std::size_t idx) { auto cb = thunk.callbacks.find(idx); if (cb != thunk.callbacks.end() && cb->second.is_stub) { - bool is_first_cb = (cb->first == thunk.callbacks.begin()->first); - return "fexfn_unpack_" + get_callback_name(function_name, cb->first, is_first_cb) + "_stub"; + return "fexfn_unpack_" + get_callback_name(function_name, cb->first) + "_stub"; } else if (cb != thunk.callbacks.end() && cb->second.is_guest) { return "fex_guest_function_ptr { args->a_" + std::to_string(idx) + " }"; } else if (cb != thunk.callbacks.end()) { From 5005ebdfc9d46c04357fe64023a9569aacf9c09c Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Thu, 4 Aug 2022 16:56:48 +0200 Subject: [PATCH 15/18] Thunks/gen: Remove ThunkedCallback members not used anymore --- ThunkLibs/Generator/gen.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ThunkLibs/Generator/gen.cpp b/ThunkLibs/Generator/gen.cpp index 225c3fffc..a887ea69d 100644 --- a/ThunkLibs/Generator/gen.cpp +++ b/ThunkLibs/Generator/gen.cpp @@ -19,9 +19,6 @@ struct FunctionParams { struct ThunkedCallback : FunctionParams { clang::QualType return_type; - std::size_t callback_index; - std::size_t user_arg_index; - bool is_stub = false; // Callback will be replaced by a stub that calls std::abort bool is_guest = false; // Callback will never be called on the host bool is_variadic = false; @@ -338,8 +335,9 @@ public: funcptr_types.insert(context.getCanonicalType(funcptr)); } - // TODO: Support for more than one callback is untested - assert(data.callbacks.size() == 1); + if (data.callbacks.size() != 1) { + throw Error(decl->getBeginLoc(), "Support for more than one callback is untested"); + } if (funcptr->isVariadic() && !callback.is_stub) { throw Error(decl->getBeginLoc(), "Variadic callbacks are not supported"); } From 63517c377db9650604a29eee65e942f35a467cc1 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Sat, 6 Aug 2022 12:01:32 +0200 Subject: [PATCH 16/18] Thunks: Add a helper to make typed host function pointers guest-callable This recurring pattern combines the existing helpers GetCallerForHostFunction and LinkAddressToFunction. --- ThunkLibs/include/common/Guest.h | 7 ++++++ ThunkLibs/libX11/libX11_Guest.cpp | 38 +++++++------------------------ 2 files changed, 15 insertions(+), 30 deletions(-) diff --git a/ThunkLibs/include/common/Guest.h b/ThunkLibs/include/common/Guest.h index 00c1f07f1..d5feae9c7 100644 --- a/ThunkLibs/include/common/Guest.h +++ b/ThunkLibs/include/common/Guest.h @@ -117,6 +117,13 @@ static auto GetCallerForHostFunction(Result (*host_func)(Args...)) return CallHostFunction, Result, Args...>; } +// Ensures the given host function can safely be called from guest code. +template +inline void MakeHostFunctionGuestCallable(Result (*host_func)(Args...)) { + auto caller = (uintptr_t)GetCallerForHostFunction(host_func); + LinkAddressToFunction((uintptr_t)host_func, (uintptr_t)caller); +} + template inline Target *AllocateHostTrampolineForGuestFunction(void (*GuestUnpacker)(uintptr_t, void*), Target *GuestTarget) { if (!GuestTarget) { diff --git a/ThunkLibs/libX11/libX11_Guest.cpp b/ThunkLibs/libX11/libX11_Guest.cpp index 1482457bd..cfba05e29 100644 --- a/ThunkLibs/libX11/libX11_Guest.cpp +++ b/ThunkLibs/libX11/libX11_Guest.cpp @@ -194,32 +194,19 @@ extern "C" { if (!funcptr) { continue; } - auto caller = (uintptr_t)GetCallerForHostFunction(funcptr); - LinkAddressToFunction((uintptr_t)funcptr, (uintptr_t)caller); + MakeHostFunctionGuestCallable(funcptr); } for (auto& funcptr : ret->wire_vec) { if (!funcptr) { continue; } - auto caller = (uintptr_t)GetCallerForHostFunction(funcptr); - LinkAddressToFunction((uintptr_t)funcptr, (uintptr_t)caller); + MakeHostFunctionGuestCallable(funcptr); } - { - auto caller = (uintptr_t)GetCallerForHostFunction(ret->resource_alloc); - LinkAddressToFunction((uintptr_t)ret->resource_alloc, (uintptr_t)caller); - } - - { - auto caller = (uintptr_t)GetCallerForHostFunction(ret->idlist_alloc); - LinkAddressToFunction((uintptr_t)ret->idlist_alloc, (uintptr_t)caller); - } - - { - auto caller = (uintptr_t)GetCallerForHostFunction(ret->exit_handler); - LinkAddressToFunction((uintptr_t)ret->exit_handler, (uintptr_t)caller); - } + MakeHostFunctionGuestCallable(ret->resource_alloc); + MakeHostFunctionGuestCallable(ret->idlist_alloc); + MakeHostFunctionGuestCallable(ret->exit_handler); return ret; } @@ -234,14 +221,8 @@ extern "C" { } static int _XInitDisplayLock(Display* display) { - { - auto caller = (uintptr_t)GetCallerForHostFunction(display->lock_fns->lock_display); - LinkAddressToFunction((uintptr_t)display->lock_fns->lock_display, (uintptr_t)caller); - } - { - auto caller = (uintptr_t)GetCallerForHostFunction(display->lock_fns->unlock_display); - LinkAddressToFunction((uintptr_t)display->lock_fns->unlock_display, (uintptr_t)caller); - } + MakeHostFunctionGuestCallable(display->lock_fns->lock_display); + MakeHostFunctionGuestCallable(display->lock_fns->unlock_display); return 0; } @@ -256,10 +237,7 @@ extern "C" { unsigned int width, unsigned int height, int pad, int bpp) { auto ret = fexfn_pack_XCreateImage(display, visual, depth, format, offset, data, width, height, pad, bpp); - { - auto caller = (uintptr_t)GetCallerForHostFunction(ret->f.destroy_image); - LinkAddressToFunction((uintptr_t)ret->f.destroy_image, (uintptr_t)caller); - } + MakeHostFunctionGuestCallable(ret->f.destroy_image); return ret; } From c8951de9ddd59ed7c42f95b509cc1d4357407ff2 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Fri, 5 Aug 2022 17:16:40 +0200 Subject: [PATCH 17/18] Thunks/X11: Add thunks for _XInitImageFuncPtrs and XInitImage --- ThunkLibs/libX11/libX11_Guest.cpp | 24 +++++++++++++++++++++++- ThunkLibs/libX11/libX11_interface.cpp | 16 ++++++---------- 2 files changed, 29 insertions(+), 11 deletions(-) diff --git a/ThunkLibs/libX11/libX11_Guest.cpp b/ThunkLibs/libX11/libX11_Guest.cpp index cfba05e29..4818297e5 100644 --- a/ThunkLibs/libX11/libX11_Guest.cpp +++ b/ThunkLibs/libX11/libX11_Guest.cpp @@ -230,6 +230,22 @@ extern "C" { return fexfn_pack_XInitThreadsInternal((uintptr_t)_XInitDisplayLock, (uintptr_t)CallbackUnpack::Unpack); } + // Register the host function pointers written by _XInitImageFuncPtrs (and + // its callers) to be guest-callable + static void FixupImageFuncPtrs(XImage* image) { + image->f.create_image = XCreateImage; + MakeHostFunctionGuestCallable(image->f.destroy_image); + MakeHostFunctionGuestCallable(image->f.get_pixel); + MakeHostFunctionGuestCallable(image->f.put_pixel); + // TODO: image->f.sub_image + MakeHostFunctionGuestCallable(image->f.add_pixel); + } + + void _XInitImageFuncPtrs(XImage* image) { + fexfn_pack__XInitImageFuncPtrs(image); + FixupImageFuncPtrs(image); + } + XImage *XCreateImage( Display* display, Visual* visual, unsigned int depth, int format, @@ -237,7 +253,13 @@ extern "C" { unsigned int width, unsigned int height, int pad, int bpp) { auto ret = fexfn_pack_XCreateImage(display, visual, depth, format, offset, data, width, height, pad, bpp); - MakeHostFunctionGuestCallable(ret->f.destroy_image); + FixupImageFuncPtrs(ret); + return ret; + } + + Status XInitImage(XImage* image) { + auto ret = fexfn_pack_XInitImage(image); + FixupImageFuncPtrs(image); return ret; } diff --git a/ThunkLibs/libX11/libX11_interface.cpp b/ThunkLibs/libX11/libX11_interface.cpp index 278ff304c..a52638abd 100644 --- a/ThunkLibs/libX11/libX11_interface.cpp +++ b/ThunkLibs/libX11/libX11_interface.cpp @@ -218,7 +218,7 @@ template<> struct fex_gen_config {}; template<> struct fex_gen_config<_XReadEvents> {}; -template<> struct fex_gen_config {}; // TODO: Fixup vtable for guest use +template<> struct fex_gen_config : fexgen::custom_guest_entrypoint {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -552,10 +552,7 @@ template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; - -template<> struct fex_gen_type {}; // XImage::f.destroy_image template<> struct fex_gen_config : fexgen::custom_guest_entrypoint {}; - template<> struct fex_gen_config {}; template<> struct fex_gen_config {}; @@ -624,12 +621,11 @@ template<> struct fex_gen_config<_XFreeTemp> {}; template<> struct fex_gen_config<_XUnknownNativeEvent> {}; template<> struct fex_gen_config<_XIsEventCookie> {}; - - - -// TODO: Vtable support... -template<> struct fex_gen_config<_XInitImageFuncPtrs> {}; - +template<> struct fex_gen_type {}; // XImage::f.destroy_image +template<> struct fex_gen_type {}; // XImage::f.get_pixel +template<> struct fex_gen_type {}; // XImage::f.put_pixel +template<> struct fex_gen_type {}; // XImage::f.add_pixel +template<> struct fex_gen_config<_XInitImageFuncPtrs> : fexgen::custom_guest_entrypoint {}; template<> struct fex_gen_config { using uniform_va_type = void*; From 4b7ed9599dce23d9ef117c5b7ff0de55a52d38b9 Mon Sep 17 00:00:00 2001 From: Tony Wasserka Date: Mon, 8 Aug 2022 15:52:38 +0200 Subject: [PATCH 18/18] Thunks: Clarify return of a function pointer --- ThunkLibs/include/common/Guest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ThunkLibs/include/common/Guest.h b/ThunkLibs/include/common/Guest.h index d5feae9c7..e0414761a 100644 --- a/ThunkLibs/include/common/Guest.h +++ b/ThunkLibs/include/common/Guest.h @@ -114,7 +114,7 @@ inline Result CallHostFunction(Args... args) { template static auto GetCallerForHostFunction(Result (*host_func)(Args...)) -> Result(*)(Args...) { - return CallHostFunction, Result, Args...>; + return &CallHostFunction, Result, Args...>; } // Ensures the given host function can safely be called from guest code.