Merge pull request #1868 from neobrain/feature_thunk_funcptrs_by_signature

Implement signature-based thunking of function pointers
This commit is contained in:
Ryan Houdek authored and GitHub committed 2022-08-08 21:39:46 -07:00
commit 62d9961bd1
19 files changed
+988 -341

No files matched your search

+128 -97
View File
@@ -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<TrampolineInstanceInfo*>(reinterpret_cast<char*>(Trampoline) + InstanceInfoOffset);
}
struct GuestcallInfo {
uintptr_t GuestUnpacker;
uintptr_t GuestTarget;
@@ -96,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<IR::SHA256Sum, ThunkedFunction*, TruncatingSHA256Hash> Thunks = {
@@ -121,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
}
};
@@ -131,7 +144,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<std::string> Libs;
std::unordered_map<GuestcallInfo, uintptr_t, GuestcallInfoHash> GuestcallToHostTrampoline;
std::unordered_map<GuestcallInfo, HostToGuestTrampolinePtr*, GuestcallInfoHash> GuestcallToHostTrampoline;
uint8_t *HostTrampolineInstanceDataPtr;
size_t HostTrampolineInstanceDataAvailable = 0;
@@ -201,98 +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_t*>(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_t*>(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<ThunkHandler_impl *>(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 = 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 = 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);
const auto Length = __stop_HostToGuestTrampolineTemplate - __start_HostToGuestTrampolineTemplate;
const auto InstanceInfoOffset = Length - sizeof(TrampolineInstanceInfo);
if (ThunkHandler->HostTrampolineInstanceDataAvailable < Length) {
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");
}
const TrampolineInstanceInfo NewTrampolineInfo {
.HostPacker = reinterpret_cast<uintptr_t>(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));
args->rv = reinterpret_cast<uintptr_t>(HostTrampoline);
ThunkHandler->GuestcallToHostTrampoline[gci] = args->rv;
args->rv = (uintptr_t)MakeHostTrampolineForGuestFunction(nullptr, args->GuestTarget, args->GuestUnpacker);
}
/**
@@ -374,8 +313,6 @@ namespace FEXCore {
}
}
public:
ThunkedFunction* LookupThunk(const IR::SHA256Sum &sha256) {
std::shared_lock lk(ThunksMutex);
@@ -397,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<ThunkHandler_impl *>(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<HostToGuestTrampolinePtr* const>(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;
}
}
}
+3
View File
@@ -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/
+67 -112
View File
@@ -6,6 +6,7 @@
#include <iomanip>
#include <string_view>
#include <unordered_map>
#include <unordered_set>
#include <openssl/sha.h>
@@ -18,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;
@@ -47,11 +45,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());
@@ -93,6 +86,7 @@ struct ThunkedAPIFunction : FunctionParams {
static std::vector<ThunkedFunction> thunks;
static std::vector<ThunkedAPIFunction> thunked_api;
static std::unordered_set<const clang::Type*> funcptr_types;
static std::optional<unsigned> lib_version;
struct NamespaceInfo {
@@ -263,6 +257,20 @@ public:
* Matches "template<> struct fex_gen_config<LibraryFunc> { ... }"
*/
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,8 +331,13 @@ public:
}
data.callbacks.emplace(param_idx, callback);
// TODO: Support for more than one callback is untested
assert(data.callbacks.size() == 1);
if (!callback.is_stub && !callback.is_guest) {
funcptr_types.insert(context.getCanonicalType(funcptr));
}
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");
}
@@ -360,13 +373,9 @@ 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()));
}
thunks.push_back(std::move(data));
@@ -395,6 +404,7 @@ GenerateThunkLibsAction::GenerateThunkLibsAction(const std::string& libname_, co
thunks.clear();
thunked_api.clear();
funcptr_types.clear();
namespaces.clear();
lib_version = std::nullopt;
}
@@ -410,12 +420,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();
@@ -461,8 +467,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()) {
@@ -479,25 +485,27 @@ 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";
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
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";
}
}
if (!output_filenames.function_packs_public.empty()) {
@@ -565,6 +573,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);
@@ -573,9 +582,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";
@@ -612,37 +619,14 @@ 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";
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";
}
}
@@ -650,11 +634,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;
}
@@ -664,10 +644,14 @@ 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()) {
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);
}
@@ -694,20 +678,19 @@ 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);
for (auto& type : funcptr_types) {
std::string mangled_name = clang::QualType { type, 0 }.getAsString();
{
auto cb_sha256 = get_sha256("fexcallback_" + mangled_name);
auto cb_sha256 = get_sha256(cb_function_name);
file << "{(uint8_t*)\"";
std::stringstream cb_sha256_ss;
for (auto c : cb_sha256) {
file << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c;
cb_sha256_ss << "\\x" << std::hex << std::setw(2) << std::setfill('0') << +c;
}
file << "\", (void(*)(void *))&fexfn_pack_guestcall_" << cb_function_name << "}, // " << libname << ":" << cb_function_name << "\n";
auto cb_sha256_str = std::move(cb_sha256_ss).str();
file << " {(uint8_t*)\"" << cb_sha256_str << "\", (void(*)(void *))&CallbackUnpack<" << mangled_name << ">::ForIndirectCall},\n";
}
}
}
@@ -744,34 +727,6 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
}
}
if (!output_filenames.callback_unpacks.empty()) {
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<fn_t*>(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()) {
std::ofstream file(output_filenames.symbol_list);
-2
View File
@@ -17,8 +17,6 @@ struct OutputFilenames {
std::string function_packs_public;
// Guest + Host
std::string callback_unpacks;
std::string symbol_list;
};
-2
View File
@@ -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 {
+4 -4
View File
@@ -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
"$<$<BOOL:${prop}>:;-isystem$<JOIN:${prop},;-isystem>>"
VERBATIM
@@ -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)
+1
View File
@@ -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 "$<TARGET_PROPERTY:${NAME}-deps,INTERFACE_INCLUDE_DIRECTORIES>")
-1
View File
@@ -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
+54 -93
View File
@@ -2,10 +2,21 @@
#include <stdint.h>
#include <type_traits>
#include "PackedArguments.h"
template<typename signature>
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<signature>)(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.
@@ -15,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<signature>)(void*) = fexthunks_##name;
#endif
// Generated fexfn_pack_ symbols should be hidden by default, but clang does
@@ -35,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() \
@@ -68,94 +82,13 @@ inline bool IsLibLoaded(const char *libname) {
return argsrv.rv;
}
template<typename Result, typename... Args>
struct PackedArguments;
template<typename R, typename A0>
struct PackedArguments<R, A0> { A0 a0; R rv; };
template<typename R, typename A0, typename A1>
struct PackedArguments<R, A0, A1> { A0 a0; A1 a1; R rv; };
template<typename R, typename A0, typename A1, typename A2>
struct PackedArguments<R, A0, A1, A2> { A0 a0; A1 a1; A2 a2; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3>
struct PackedArguments<R, A0, A1, A2, A3> { A0 a0; A1 a1; A2 a2; A3 a3; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4>
struct PackedArguments<R, A0, A1, A2, A3, A4> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> { 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<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { 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<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { 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<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { 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<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18, typename A19,
typename A20, typename A21, typename A22, typename A23>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
A20, A21, A22, A23> {
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<typename A0>
struct PackedArguments<void, A0> { A0 a0; };
template<typename A0, typename A1>
struct PackedArguments<void, A0, A1> { A0 a0; A1 a1; };
template<typename A0, typename A1, typename A2>
struct PackedArguments<void, A0, A1, A2> { A0 a0; A1 a1; A2 a2; };
template<typename A0, typename A1, typename A2, typename A3>
struct PackedArguments<void, A0, A1, A2, A3> { A0 a0; A1 a1; A2 a2; A3 a3; };
template<typename A0, typename A1, typename A2, typename A3, typename A4>
struct PackedArguments<void, A0, A1, A2, A3, A4> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; A11 a11; A12 a12; A13 a13; A14 a14; A15 a15; A16 a16; A17 a17; };
// Helper template that packs the given arguments and invokes a thunk at the
// address stored in the `r11` guest register. The signature of the thunk must
// be specified at compile-time via the Thunk template parameter.
// Other than reading the thunk address from `r11`, this is equivalent to the
// fexfn_pack_* functions generated for global API functions.
template<auto Thunk, typename Result, typename... Args>
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));
@@ -176,33 +109,61 @@ inline Result CallHostThunkFromRuntimePointer(Args... args) {
}
}
// Convenience wrapper that returns the function pointer to a
// CallHostThunkFromRuntimePointer instantiation matching the function
// signature of `host_func`
template<auto Thunk, typename Result, typename...Args>
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<typename Result, typename...Args>
static auto GetCallerForHostFunction(Result (*host_func)(Args...))
-> Result(*)(Args...) {
return CallHostThunkFromRuntimePointer<Thunk, Result, Args...>;
return &CallHostFunction<fexthunks_invoke_callback<Result(Args...)>, Result, Args...>;
}
// Ensures the given host function can safely be called from guest code.
template<typename Result, typename...Args>
inline void MakeHostFunctionGuestCallable(Result (*host_func)(Args...)) {
auto caller = (uintptr_t)GetCallerForHostFunction(host_func);
LinkAddressToFunction((uintptr_t)host_func, (uintptr_t)caller);
}
template<typename Target>
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<typename F>
struct CallbackUnpack;
template<typename Result, typename... Args>
struct CallbackUnpack<Result(Args...)> {
static void Unpack(uintptr_t cb, void* argsv) {
using fn_t = Result(Args...);
auto callback = reinterpret_cast<fn_t*>(cb);
auto args = reinterpret_cast<PackedArguments<Result, Args...>*>(argsv);
Invoke(callback, *args);
}
};
template<typename Result, typename... Args>
struct CallbackUnpack<Result(*)(Args...)> : CallbackUnpack<Result(Args...)> {
};
template<typename Target>
inline Target *AllocateHostTrampolineForGuestFunction(Target *GuestTarget) {
return AllocateHostTrampolineForGuestFunction(CallbackUnpack<Target*>::Unpack,
GuestTarget);
}
inline bool IsHostHeapAllocation(void* ptr) {
struct {
void* ptr;
+114 -2
View File
@@ -5,7 +5,34 @@ $end_info$
*/
#pragma once
#include <stdint.h>
#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;
@@ -45,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; \
} \
@@ -75,4 +102,89 @@ 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<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);
}
+191
View File
@@ -0,0 +1,191 @@
#pragma once
#include <cstdint>
#include <type_traits>
template<typename Result, typename... Args>
struct PackedArguments;
template<typename R>
struct PackedArguments<R> { R rv; };
template<typename R, typename A0>
struct PackedArguments<R, A0> { A0 a0; R rv; };
template<typename R, typename A0, typename A1>
struct PackedArguments<R, A0, A1> { A0 a0; A1 a1; R rv; };
template<typename R, typename A0, typename A1, typename A2>
struct PackedArguments<R, A0, A1, A2> { A0 a0; A1 a1; A2 a2; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3>
struct PackedArguments<R, A0, A1, A2, A3> { A0 a0; A1 a1; A2 a2; A3 a3; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4>
struct PackedArguments<R, A0, A1, A2, A3, A4> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; R rv; };
template<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> { 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<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { 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<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { 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<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { 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<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18, typename A19,
typename A20, typename A21, typename A22>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
A20, A21, A22> {
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<typename R, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9,
typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17, typename A18, typename A19,
typename A20, typename A21, typename A22, typename A23>
struct PackedArguments<R, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9,
A10, A11, A12, A13, A14, A15, A16, A17, A18, A19,
A20, A21, A22, A23> {
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<void> { };
template<typename A0>
struct PackedArguments<void, A0> { A0 a0; };
template<typename A0, typename A1>
struct PackedArguments<void, A0, A1> { A0 a0; A1 a1; };
template<typename A0, typename A1, typename A2>
struct PackedArguments<void, A0, A1, A2> { A0 a0; A1 a1; A2 a2; };
template<typename A0, typename A1, typename A2, typename A3>
struct PackedArguments<void, A0, A1, A2, A3> { A0 a0; A1 a1; A2 a2; A3 a3; };
template<typename A0, typename A1, typename A2, typename A3, typename A4>
struct PackedArguments<void, A0, A1, A2, A3, A4> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10> { A0 a0; A1 a1; A2 a2; A3 a3; A4 a4; A5 a5; A6 a6; A7 a7; A8 a8; A9 a9; A10 a10; };
template<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16> { 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<typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename A14, typename A15, typename A16, typename A17>
struct PackedArguments<void, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15, A16, A17> { 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<typename Result, typename... Args>
void Invoke(Result(*func)(Args...), PackedArguments<Result, Args...>& args) {
constexpr auto NumArgs = sizeof...(Args);
static_assert(NumArgs <= 18 || NumArgs == 24);
if constexpr (std::is_void_v<Result>) {
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 == 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) {
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 == 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);
}
}
}
+2 -2
View File
@@ -35,9 +35,9 @@ typedef void voidFunc();
// linked to the corresponding host function pointer
const std::unordered_map<std::string_view, uintptr_t /* guest function address */> HostPtrInvokers =
std::invoke([]() {
#define PAIR(name, unused) Ret[#name] = reinterpret_cast<uintptr_t>(GetCallerForHostThunkFromRuntimePointer<fexthunks_libGL_hostcall_##name>(name));
#define PAIR(name, unused) Ret[#name] = reinterpret_cast<uintptr_t>(GetCallerForHostFunction(name));
std::unordered_map<std::string_view, uintptr_t> Ret;
FOREACH_internal_SYMBOL(PAIR)
FOREACH_internal_SYMBOL(PAIR);
return Ret;
#undef PAIR
});
+155 -2
View File
@@ -9,6 +9,9 @@ $end_info$
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/Xproto.h>
#include <X11/XKBlib.h>
// Include Xlibint.h and undefine some of its macros that clash with the standard library
#include <X11/Xlibint.h>
#undef min
@@ -24,7 +27,6 @@ $end_info$
#include <stdarg.h>
#include "thunks.inl"
#include "callback_unpacks.inl"
#include "function_packs.inl"
#include "function_packs_public.inl"
@@ -54,6 +56,63 @@ extern "C" {
return rv;
}
char* XSetICValues(XIC ic, ...) {
fprintf(stderr, "XSetICValues\n");
va_list ap;
std::vector<unsigned long> 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;
std::vector<void*> 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;
}
char* XSetIMValues(XIM ic, ...) {
fprintf(stderr, "XSetIMValues\n");
va_list ap;
std::vector<void*> 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;
@@ -73,6 +132,25 @@ extern "C" {
return rv;
}
XVaNestedList XVaCreateNestedList(int unused_arg, ...) {
fprintf(stderr, "XVaCreateNestedList\n");
va_list ap;
std::vector<void*> 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");
}
@@ -109,9 +187,84 @@ extern "C" {
}
}
Display* XOpenDisplay(const char* name) {
auto ret = fexfn_pack_XOpenDisplay(name);
for (auto& funcptr : ret->event_vec) {
if (!funcptr) {
continue;
}
MakeHostFunctionGuestCallable(funcptr);
}
for (auto& funcptr : ret->wire_vec) {
if (!funcptr) {
continue;
}
MakeHostFunctionGuestCallable(funcptr);
}
MakeHostFunctionGuestCallable(ret->resource_alloc);
MakeHostFunctionGuestCallable(ret->idlist_alloc);
MakeHostFunctionGuestCallable(ret->exit_handler);
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) {
MakeHostFunctionGuestCallable(display->lock_fns->lock_display);
MakeHostFunctionGuestCallable(display->lock_fns->unlock_display);
return 0;
}
Status XInitThreads() {
return fexfn_pack_XInitThreadsInternal((uintptr_t)_XInitDisplayLock, (uintptr_t)CallbackUnpack<decltype(_XInitDisplayLock)>::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,
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);
FixupImageFuncPtrs(ret);
return ret;
}
Status XInitImage(XImage* image) {
auto ret = fexfn_pack_XInitImage(image);
FixupImageFuncPtrs(image);
return ret;
}
void (*_XLockMutex_fn)(LockInfoPtr) = LockMutexFunction;
void (*_XUnlockMutex_fn)(LockInfoPtr) = UnlockMutexFunction;
typedef struct _LockInfoRec *LockInfoPtr;
LockInfoPtr _Xglobal_lock = (LockInfoPtr)0x4142434445464748ULL;
}
+104 -4
View File
@@ -9,10 +9,20 @@ $end_info$
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xlibint.h>
#undef min
#undef max
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/Xproto.h>
#include <X11/extensions/XKBstr.h>
#include "common/Host.h"
#include <dlfcn.h>
#include <utility>
#include "ldr_ptrs.inl"
@@ -32,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;
@@ -45,11 +53,79 @@ 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");
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();
}
}
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, "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();
}
}
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[] = {
@@ -59,4 +135,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)
+110 -4
View File
@@ -4,14 +4,23 @@
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/ImUtil.h>
#include <X11/extensions/Xext.h>
#include <X11/Xlibint.h>
#include <X11/XKBlib.h>
#include <type_traits>
template<auto>
struct fex_gen_config {
unsigned version = 6;
};
template<typename>
struct fex_gen_type {};
template<> struct fex_gen_config<XFetchBytes> {};
template<> struct fex_gen_config<XLocaleOfIM> {};
template<> struct fex_gen_config<XLocaleOfOM> {};
@@ -209,7 +218,7 @@ template<> struct fex_gen_config<XGetMotionEvents> {};
template<> struct fex_gen_config<_XReadEvents> {};
template<> struct fex_gen_config<XInitImage> {}; // TODO: Fixup vtable for guest use
template<> struct fex_gen_config<XInitImage> : fexgen::custom_guest_entrypoint {};
template<> struct fex_gen_config<XrmQuarkToString> {};
template<> struct fex_gen_config<XrmCombineFileDatabase> {};
template<> struct fex_gen_config<XrmGetResource> {};
@@ -437,11 +446,17 @@ template<> struct fex_gen_config<XGetICValues> {
using uniform_va_type = unsigned long;
};
template<> struct fex_gen_config<XSetICValues> {
using uniform_va_type = unsigned long;
};
template<> struct fex_gen_config<XCreateIC> {
using uniform_va_type = unsigned long;
};
template<> struct fex_gen_config<XIfEvent> {};
template<> struct fex_gen_config<XCheckIfEvent> {};
// TODO: Make returned function pointer guest-callable. For now, just pretend it already is.
template<> struct fex_gen_config<XSetErrorHandler> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XInternAtom> {};
@@ -449,7 +464,17 @@ template<> struct fex_gen_config<XListExtensions> {};
template<> struct fex_gen_config<XSetLocaleModifiers> {};
template<> struct fex_gen_config<XCreateColormap> {};
template<> struct fex_gen_config<XCreatePixmapCursor> {};
template<> struct fex_gen_config<XOpenDisplay> {};
template<> struct fex_gen_type<XID(Display*)> {}; // 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<void(Display*/*, char*, int*/)> {}; // XDisplay::lock_fns->lock_display
template<> struct fex_gen_type<void(_XDisplay*, XID*, int)> {}; // XDisplay::idlist_alloc
template<> struct fex_gen_type<std::remove_pointer_t<XIOErrorExitHandler>> {}; // XDisplay::exit_handler
template<> struct fex_gen_config<XOpenDisplay> : fexgen::custom_guest_entrypoint {};
template<> struct fex_gen_config<XChangeProperty> {};
template<> struct fex_gen_config<XCloseDisplay> {};
template<> struct fex_gen_config<XCloseIM> {};
@@ -473,7 +498,12 @@ template<> struct fex_gen_config<XGetWindowProperty> {};
template<> struct fex_gen_config<XGrabPointer> {};
template<> struct fex_gen_config<XGrabServer> {};
template<> struct fex_gen_config<XIconifyWindow> {};
template<> struct fex_gen_config<XInitThreads> {};
// Sets up vtables for various objects
Status XInitThreadsInternal(uintptr_t OnXInitDisplayLock, uintptr_t OnXInitDisplayLockPacker);
template<> struct fex_gen_config<XInitThreads> : fexgen::custom_guest_entrypoint {};
template<> struct fex_gen_config<XInitThreadsInternal> : fexgen::custom_host_impl, fexgen::custom_guest_entrypoint {};
template<> struct fex_gen_config<XLookupString> {};
template<> struct fex_gen_config<XMapRaised> {};
template<> struct fex_gen_config<XMoveResizeWindow> {};
@@ -522,5 +552,81 @@ template<> struct fex_gen_config<XDefaultScreen> {};
template<> struct fex_gen_config<XDisplayWidth> {};
template<> struct fex_gen_config<XMatchVisualInfo> {};
template<> struct fex_gen_config<XPutImage> {};
template<> struct fex_gen_config<XCreateImage> {};
template<> struct fex_gen_config<XCreateImage> : fexgen::custom_guest_entrypoint {};
template<> struct fex_gen_config<XDisplayHeight> {};
template<> struct fex_gen_config<XkbGetKeyboard> {};
template<> struct fex_gen_config<XkbUseExtension> {};
template<> struct fex_gen_config<XkbTranslateKeySym> {};
template<> struct fex_gen_config<XkbFreeKeyboard> {};
template<> struct fex_gen_config<XkbSetDetectableAutoRepeat> {};
template<> struct fex_gen_config<XkbGetMap> {};
template<> struct fex_gen_config<XkbGetUpdatedMap> {};
template<> struct fex_gen_config<XkbQueryExtension> {};
template<> struct fex_gen_config<XkbSelectEvents> {};
template<> struct fex_gen_config<XkbGetState> {};
template<> struct fex_gen_config<XkbFreeClientMap> {};
template<> struct fex_gen_config<XSynchronize> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XGetIMValues> {
using uniform_va_type = void*;
};
template<> struct fex_gen_config<XSetIMValues> {
using uniform_va_type = void*;
};
template<> struct fex_gen_config<XESetEventToWire> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetWireToEvent> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetCreateFont> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetFreeGC> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetFlushGC> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetCreateGC> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetCopyGC> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetFreeFont> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetCloseDisplay> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetCopyEventCookie> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetWireToEventCookie> : fexgen::returns_guest_pointer {};
// TODO: Should probably catch these with a custom handler
template<> struct fex_gen_config<XESetErrorString> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XESetError> : fexgen::returns_guest_pointer {};
template<> struct fex_gen_config<XSetIOErrorHandler> : 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_type<Bool(Display*, xReply*, char*, int, XPointer)> {}; // 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> {};
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_type<int(XImage*)> {}; // XImage::f.destroy_image
template<> struct fex_gen_type<unsigned long(XImage*, int, int)> {}; // XImage::f.get_pixel
template<> struct fex_gen_type<int(XImage*, int, int, unsigned long)> {}; // XImage::f.put_pixel
template<> struct fex_gen_type<int(XImage*, long)> {}; // XImage::f.add_pixel
template<> struct fex_gen_config<_XInitImageFuncPtrs> : fexgen::custom_guest_entrypoint {};
template<> struct fex_gen_config<XVaCreateNestedList> {
using uniform_va_type = void*;
};
-1
View File
@@ -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"
+3 -3
View File
@@ -29,9 +29,9 @@ extern "C" {
// linked to the corresponding host function pointer
const std::unordered_map<std::string_view, uintptr_t /* guest function address */> HostPtrInvokers =
std::invoke([]() {
#define PAIR(name, unused) Ret[#name] = reinterpret_cast<uintptr_t>(GetCallerForHostThunkFromRuntimePointer<fexthunks_libvulkan_hostcall_##name>(name));
#define PAIR(name, unused) Ret[#name] = reinterpret_cast<uintptr_t>(GetCallerForHostFunction(name));
std::unordered_map<std::string_view, uintptr_t> Ret;
FOREACH_internal_SYMBOL(PAIR)
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<FatalError, void>;
const auto StubHostPtrInvoker = CallHostFunction<FatalError, void>;
LinkAddressToFunction((uintptr_t)func, reinterpret_cast<uintptr_t>(StubHostPtrInvoker));
return func;
}
-1
View File
@@ -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<bool> CBDone{false};
+52 -11
View File
@@ -57,7 +57,6 @@ struct Fixture {
tmpdir + "/thunks",
tmpdir + "/function_packs",
tmpdir + "/function_packs_public",
tmpdir + "/callback_unpacks",
};
}
@@ -280,12 +279,16 @@ SourceWithAST Fixture::run_thunkgen_guest(std::string_view prelude, std::string_
std::string result =
"#include <cstdint>\n"
"#define MAKE_THUNK(lib, name, hash) extern \"C\" int fexthunks_##lib##_##name(void*);\n"
"template<typename>\n"
"struct callback_thunk_defined;\n"
"#define MAKE_CALLBACK_THUNK(name, sig, hash) template<> struct callback_thunk_defined<sig> {};\n"
"#define FEX_PACKFN_LINKAGE\n"
"template<typename Target>\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<typename Target>\n"
"Target *AllocateHostTrampolineForGuestFunction(Target*);\n";
for (auto& filename : {
output_filenames.thunks,
output_filenames.callback_unpacks,
output_filenames.function_packs_public,
output_filenames.function_packs,
}) {
@@ -321,13 +324,19 @@ SourceWithAST Fixture::run_thunkgen_host(std::string_view prelude, std::string_v
" using args_t = typename function_traits<decltype(Fn)>::arg_t;\n"
" return Fn(reinterpret_cast<args_t>(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"
" uintptr_t GuestUnpacker;\n"
" uintptr_t GuestTarget;\n"
"};\n";
"};\n"
"template<typename>\n"
"struct CallbackUnpack {\n"
" static void ForIndirectCall(void* argsv);\n"
"};\n"
"template<typename F>\n"
"void FinalizeHostTrampolineForGuestFunction(F*);\n";
for (auto& filename : {
output_filenames.ldr_ptrs,
@@ -414,6 +423,31 @@ TEST_CASE_METHOD(Fixture, "VersionedLibrary") {
}));
}
TEST_CASE_METHOD(Fixture, "FunctionPointerViaType") {
const auto output = run_thunkgen("",
"template<typename> struct fex_gen_type {};\n"
"template<> struct fex_gen_type<int(char, char)> {};\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<clang::ClassTemplateSpecializationDecl>(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("",
@@ -427,18 +461,25 @@ 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 should export the packing function for callback arguments
// 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(3))),
hasInitializer(hasDescendant(declRefExpr(to(functionDecl(hasName("fexfn_pack_guestcall_funcCBFN"))))))
hasInitializer(hasDescendant(declRefExpr(to(cxxMethodDecl(hasName("ForIndirectCall"), ofClass(hasName("CallbackUnpack")))))))
)));
}