Skip to content

Commit

Permalink
Merge pull request #1760 from neobrain/feature_guest_callable_hostptrs
Browse files Browse the repository at this point in the history
Thunks: Support returning host function pointers to the guest
  • Loading branch information
Sonicadvance1 authored Jun 25, 2022
2 parents aa17f64 + ec49100 commit 04a1ac9
Show file tree
Hide file tree
Showing 15 changed files with 533 additions and 463 deletions.
64 changes: 4 additions & 60 deletions Data/ThunksDB.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,75 +31,19 @@
"@PREFIX_LIB@/x86_64-linux-gnu/libX11.so.6.4.0"
]
},
"Vulkan-radeon": {
"Library": "libvulkan_radeon-guest.so",
"Vulkan": {
"Library": "libvulkan-guest.so",
"Depends": [
"xcb"
],
"Overlay": [
"@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_radeon.so"
"@PREFIX_LIB@/x86_64-linux-gnu/libvulkan.so",
"@PREFIX_LIB@/x86_64-linux-gnu/libvulkan.so.1",
],
"Comment": [
"Vulkan library relies on xcb, otherwise it crashes with jemalloc"
]
},
"Vulkan-lavapipe": {
"Library": "libvulkan_lvp-guest.so",
"Depends": [
"xcb"
],
"Overlay": [
"@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_lvp.so"
]
},
"Vulkan-freedreno": {
"Library": "libvulkan_freedreno-guest.so",
"Depends": [
"xcb"
],
"Overlay": [
"@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_freedreno.so"
]
},
"Vulkan-intel": {
"Library": "libvulkan_intel-guest.so",
"Depends": [
"xcb"
],
"Overlay": [
"@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_intel.so"
]
},
"Vulkan-panfrost": {
"Library": "libvulkan_panfrost-guest.so",
"Depends": [
"xcb"
],
"Overlay": [
"@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_panfrost.so"
]
},
"Vulkan-nvidia": {
"Library": "libvulkan_nvidia-guest.so",
"Depends": [
"xcb"
],
"Overlay": [
"@PREFIX_LIB@/x86_64-linux-gnu/libGLX_nvidia.so.0"
],
"Comment": [
"Not currently wired up"
]
},
"Vulkan-virtio": {
"Library": "libvulkan_virtio-guest.so",
"Depends": [
"xcb"
],
"Overlay": [
"@PREFIX_LIB@/x86_64-linux-gnu/libvulkan_virtio.so"
]
},
"xcb": {
"Library": "libxcb-guest.so",
"Overlay": [
Expand Down
56 changes: 55 additions & 1 deletion External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ tags: glue|thunks
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/IR/IR.h>
#include <FEXCore/IR/IREmitter.h>
#include "Thunks.h"

#include <dlfcn.h>
Expand Down Expand Up @@ -41,8 +42,13 @@ namespace FEXCore {
std::map<IR::SHA256Sum, ThunkedFunction*> Thunks = {
{
// sha256(fex:loadlib)
{ 0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80},
{ 0x27, 0x7e, 0xb7, 0x69, 0x5b, 0xe9, 0xab, 0x12, 0x6e, 0xf7, 0x85, 0x9d, 0x4b, 0xc9, 0xa2, 0x44, 0x46, 0xcf, 0xbd, 0xb5, 0x87, 0x43, 0xef, 0x28, 0xa2, 0x65, 0xba, 0xfc, 0x89, 0x0f, 0x77, 0x80 },
&LoadLib
},
{
// sha256(fex:link_address_to_function)
{ 0xe6, 0xa8, 0xec, 0x1c, 0x7b, 0x74, 0x35, 0x27, 0xe9, 0x4f, 0x5b, 0x6e, 0x2d, 0xc9, 0xa0, 0x27, 0xd6, 0x1f, 0x2b, 0x87, 0x8f, 0x2d, 0x35, 0x50, 0xea, 0x16, 0xb8, 0xc4, 0x5e, 0x42, 0xfd, 0x77 },
&LinkAddressToGuestFunction
}
};

Expand All @@ -56,6 +62,54 @@ namespace FEXCore {
Thread->CTX->HandleCallback(Thread, (uintptr_t)callback);
}

/**
* Instructs the Core to redirect calls to functions at the given
* address to another function. The original callee address is passed
* to the target function through an implicit argument stored in r11.
*
* The primary use case of this is ensuring that host function pointers
* returned from thunked APIs can safely be called by the guest.
*/
static void LinkAddressToGuestFunction(void* argsv) {
struct args_t {
uintptr_t original_callee;
uintptr_t target_addr; // Guest function to call when branching to original_callee
};

auto args = reinterpret_cast<args_t*>(argsv);
auto CTX = Thread->CTX;

LOGMAN_THROW_A_FMT(args->original_callee, "Tried to link null pointer address to guest function");
LOGMAN_THROW_A_FMT(args->target_addr, "Tried to link address to null pointer guest function");
if (!CTX->Config.Is64BitMode) {
LOGMAN_THROW_A_FMT((args->original_callee >> 32) == 0, "Tried to link 64-bit address in 32-bit mode");
LOGMAN_THROW_A_FMT((args->target_addr >> 32) == 0, "Tried to link 64-bit address in 32-bit mode");
}

LogMan::Msg::DFmt("Thunks: Adding trampoline from address {:#x} to guest function {:#x}",
args->original_callee, args->target_addr);

auto Result = Thread->CTX->AddCustomIREntrypoint(
args->original_callee,
[CTX, GuestThunkEntrypoint = args->target_addr](uintptr_t Entrypoint, FEXCore::IR::IREmitter *emit) {
auto IRHeader = emit->_IRHeader(emit->Invalid(), 0);
auto Block = emit->CreateCodeNode();
IRHeader.first->Blocks = emit->WrapNode(Block);
emit->SetCurrentCodeBlock(Block);

const uint8_t GPRSize = CTX->GetGPRSize();

emit->_StoreContext(GPRSize, IR::GPRClass, emit->_Constant(Entrypoint), offsetof(Core::CPUState, gregs[X86State::REG_R11]));
emit->_ExitFunction(emit->_Constant(GuestThunkEntrypoint));
}, CTX->ThunkHandler.get(), (void*)args->target_addr);

if (!Result) {
if (Result.Creator != CTX->ThunkHandler.get() || Result.Data != (void*)args->target_addr) {
ERROR_AND_DIE_FMT("Input address for LinkAddressToGuestFunction is already linked elsewhere");
}
}
}

static void LoadLib(void *ArgsV) {
auto CTX = Thread->CTX;

Expand Down
43 changes: 38 additions & 5 deletions ThunkLibs/Generator/gen.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#include "clang/AST/RecursiveASTVisitor.h"

#include <fstream>
#include <numeric>
#include <iostream>
#include <iomanip>
#include <string_view>
#include <unordered_map>

#include <openssl/sha.h>

Expand Down Expand Up @@ -45,6 +47,11 @@ struct ThunkedFunction : FunctionParams {
// This is implied e.g. for thunks generated for variadic functions
bool custom_host_impl = false;

// If true, the unpacking function will use an extra argument for a
// host function pointer that is called instead of calling the host library
// function directly.
bool is_hostcall = false;

std::string GetOriginalFunctionName() const {
const std::string suffix = "_internal";
assert(function_name.length() > suffix.size());
Expand Down Expand Up @@ -96,6 +103,8 @@ struct NamespaceInfo {
std::string host_loader;

bool generate_guest_symtable;

bool indirect_guest_calls;
};

// List of namespaces with a non-specialized fex_gen_config definition (including the global namespace, represented with an empty name)
Expand All @@ -114,6 +123,7 @@ class ASTVisitor : public clang::RecursiveASTVisitor<ASTVisitor> {
std::optional<unsigned> version;
std::optional<std::string> load_host_endpoint_via;
bool generate_guest_symtable = false;
bool indirect_guest_calls = false;
};

struct Annotations {
Expand All @@ -138,6 +148,8 @@ class ASTVisitor : public clang::RecursiveASTVisitor<ASTVisitor> {
auto annotation = base.getType().getAsString();
if (annotation == "fexgen::generate_guest_symtable") {
ret.generate_guest_symtable = true;
} else if (annotation == "fexgen::indirect_guest_calls") {
ret.indirect_guest_calls = true;
} else {
throw Error(base.getSourceRange().getBegin(), "Unknown namespace annotation");
}
Expand Down Expand Up @@ -231,7 +243,8 @@ class ASTVisitor : public clang::RecursiveASTVisitor<ASTVisitor> {
auto namespace_decl = llvm::dyn_cast<clang::NamespaceDecl>(decl->getDeclContext());
namespaces.push_back({ namespace_decl ? namespace_decl->getNameAsString() : "",
annotations.load_host_endpoint_via.value_or(""),
annotations.generate_guest_symtable });
annotations.generate_guest_symtable,
annotations.indirect_guest_calls });

if (annotations.version) {
if (namespace_decl) {
Expand Down Expand Up @@ -276,7 +289,6 @@ class ASTVisitor : public clang::RecursiveASTVisitor<ASTVisitor> {

const auto annotations = GetAnnotations(decl);
if (return_type->isFunctionPointerType() && !annotations.returns_guest_pointer) {
// TODO: Should regular pointers require annotation, too?
throw Error(decl->getBeginLoc(),
"Function pointer return types require explicit annotation\n");
}
Expand Down Expand Up @@ -345,10 +357,21 @@ class ASTVisitor : public clang::RecursiveASTVisitor<ASTVisitor> {
// This function is thunked through an "_internal" symbol since its signature
// is different from the one in the native host/guest libraries.
data.function_name = data.function_name + "_internal";
assert(!data.custom_host_impl && "Custom host impl requested but this is implied by the function signature already");
if (data.custom_host_impl) {
throw Error(decl->getBeginLoc(), "Custom host impl requested but this is implied by the function signature already");
}
data.custom_host_impl = true;
}

// For indirect calls, register a second thunk with the callee pointer as an extra argument
if (namespace_info.indirect_guest_calls) {
auto hostcall_data = data;
hostcall_data.function_name = "hostcall_" + data.function_name;
hostcall_data.param_types.push_back(context.getUIntPtrType());
hostcall_data.is_hostcall = true;
thunks.push_back(std::move(hostcall_data));
}

thunks.push_back(std::move(data));

return true;
Expand Down Expand Up @@ -574,8 +597,18 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
}
}

FunctionParams args = thunk;
auto function_to_call = "fexldr_ptr_" + libname + "_" + function_name;
if (thunk.is_hostcall) {
// Get the host function pointer by casting the last parameter to the correct signature
args.param_types.pop_back();
function_to_call = "reinterpret_cast<" + thunk.return_type.getAsString() + "(*)(" + format_function_params(args) + ")>(args->a_" + std::to_string(args.param_types.size()) + ")";
} else if (thunk.custom_host_impl) {
function_to_call = "fexfn_impl_" + libname + "_" + function_name;
}

file << "static void fexfn_unpack_" << libname << "_" << function_name << "(fexfn_packed_args_" << libname << "_" << function_name << "* args) {\n";
file << (is_void ? " " : " args->rv = ") << (thunk.custom_host_impl ? "fexfn_impl_" : "fexldr_ptr_") << libname << "_" << function_name << "(";
file << (is_void ? " " : " args->rv = ") << function_to_call << "(";
{
auto format_param = [&](std::size_t idx) {
auto cb = thunk.callbacks.find(idx);
Expand All @@ -589,7 +622,7 @@ void GenerateThunkLibsAction::EndSourceFileAction() {
}
};

file << format_function_args(thunk, format_param);
file << format_function_args(args, format_param);
}
file << ");\n";
file << "}\n";
Expand Down
Loading

0 comments on commit 04a1ac9

Please sign in to comment.