mirror of
https://github.com/FEX-Emu/FEX.git
synced 2025-02-13 11:13:38 +00:00
![Tony Wasserka](/assets/img/avatar_default.png)
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.
191 lines
5.8 KiB
C++
191 lines
5.8 KiB
C++
/*
|
|
$info$
|
|
category: thunklibs ~ These are generated + glue logic 1:1 thunks unless noted otherwise
|
|
$end_info$
|
|
*/
|
|
|
|
#pragma once
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
|
|
#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<typename Fn>
|
|
struct function_traits;
|
|
template<typename Result, typename Arg>
|
|
struct function_traits<Result(*)(Arg)> {
|
|
using result_t = Result;
|
|
using arg_t = Arg;
|
|
};
|
|
|
|
template<auto Fn>
|
|
static typename function_traits<decltype(Fn)>::result_t
|
|
fexfn_type_erased_unpack(void* argsv) {
|
|
using args_t = typename function_traits<decltype(Fn)>::arg_t;
|
|
return Fn(reinterpret_cast<args_t>(argsv));
|
|
}
|
|
|
|
struct ExportEntry { uint8_t* sha256; void(*fn)(void *); };
|
|
|
|
typedef void fex_call_callback_t(uintptr_t callback, void *arg0, void* arg1);
|
|
|
|
/**
|
|
* Opaque wrapper around a guest function pointer.
|
|
*
|
|
* This prevents accidental calls to foreign function pointers while still
|
|
* allowing us to label function pointers as such.
|
|
*/
|
|
struct fex_guest_function_ptr {
|
|
private:
|
|
[[maybe_unused]] void* value = nullptr;
|
|
|
|
public:
|
|
fex_guest_function_ptr() = default;
|
|
|
|
template<typename Ret, typename... Args>
|
|
fex_guest_function_ptr(Ret (*ptr)(Args...)) : value(reinterpret_cast<void*>(ptr)) {}
|
|
};
|
|
|
|
#define EXPORTS(name) \
|
|
extern "C" { \
|
|
ExportEntry* fexthunks_exports_##name(uintptr_t allocate, uintptr_t finalize) { \
|
|
if (!fexldr_init_##name()) { \
|
|
return nullptr; \
|
|
} \
|
|
return exports; \
|
|
} \
|
|
}
|
|
|
|
#define LOAD_LIB_INIT(init_fn) \
|
|
__attribute__((constructor)) static void loadlib() \
|
|
{ \
|
|
init_fn (); \
|
|
}
|
|
|
|
struct GuestcallInfo {
|
|
uintptr_t HostPacker;
|
|
void (*CallCallback)(uintptr_t GuestUnpacker, uintptr_t GuestTarget, void* argsrv);
|
|
uintptr_t GuestUnpacker;
|
|
uintptr_t GuestTarget;
|
|
};
|
|
|
|
// Helper macro for reading an internal argument passed through the `r11`
|
|
// host register. This macro must be placed at the very beginning of
|
|
// the function it is used in.
|
|
#if defined(_M_X86_64)
|
|
#define LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(target_variable) \
|
|
asm volatile("mov %%r11, %0" : "=r" (target_variable))
|
|
#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<typename>
|
|
struct CallbackUnpack;
|
|
|
|
template<typename Result, typename... Args>
|
|
struct CallbackUnpack<Result(Args...)> {
|
|
static Result CallGuestPtr(Args... args) {
|
|
GuestcallInfo *guestcall;
|
|
LOAD_INTERNAL_GUESTPTR_VIA_CUSTOM_ABI(guestcall);
|
|
|
|
PackedArguments<Result, Args...> packed_args = {
|
|
args...
|
|
};
|
|
guestcall->CallCallback(guestcall->GuestUnpacker, guestcall->GuestTarget, &packed_args);
|
|
if constexpr (!std::is_void_v<Result>) {
|
|
return packed_args.rv;
|
|
}
|
|
}
|
|
|
|
static void ForIndirectCall(void* argsv) {
|
|
auto args = reinterpret_cast<PackedArguments<Result, Args..., uintptr_t>*>(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<Result(*)(Args..., uintptr_t)>(cb);
|
|
Invoke(callback, *args);
|
|
}
|
|
};
|
|
|
|
template<typename FuncType>
|
|
void MakeHostTrampolineForGuestFunctionAt(uintptr_t GuestTarget, uintptr_t GuestUnpacker, FuncType **Func) {
|
|
*Func = (FuncType*)FEXCore::MakeHostTrampolineForGuestFunction(
|
|
(void*)&CallbackUnpack<FuncType>::CallGuestPtr,
|
|
GuestTarget,
|
|
GuestUnpacker);
|
|
}
|
|
|
|
template<typename F>
|
|
void FinalizeHostTrampolineForGuestFunction(F* PreallocatedTrampolineForGuestFunction) {
|
|
FEXCore::FinalizeHostTrampolineForGuestFunction(
|
|
(FEXCore::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction,
|
|
(void*)&CallbackUnpack<F>::CallGuestPtr);
|
|
}
|