Skip to content

Commit

Permalink
Thunks: Just move code around for a minor cleanup
Browse files Browse the repository at this point in the history
NFC
  • Loading branch information
Sonicadvance1 committed Sep 20, 2024
1 parent 6505dec commit 9619587
Show file tree
Hide file tree
Showing 2 changed files with 158 additions and 156 deletions.
312 changes: 156 additions & 156 deletions Source/Tools/LinuxEmulation/Thunks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,17 +70,8 @@ extern char __stop_HostToGuestTrampolineTemplate[];

namespace FEX::HLE {

struct LoadlibArgs {
const char* Name;
};

static thread_local FEX::HLE::ThreadStateObject* ThreadObject {};

struct ExportEntry {
uint8_t* sha256;
FEXCore::ThunkedFunction* Fn;
};

struct TrampolineInstanceInfo {
void* HostPacker;
uintptr_t CallCallback;
Expand Down Expand Up @@ -113,15 +104,6 @@ struct GuestcallInfoHash {
}
};

// Bits in a SHA256 sum are already randomly distributed, so truncation yields a suitable hash function
struct TruncatingSHA256Hash {
size_t operator()(const FEXCore::IR::SHA256Sum& SHA256Sum) const noexcept {
return (const size_t&)SHA256Sum;
}
};

HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker);

namespace ThunkFunctions {
void LoadLib(void* ArgsV);
void IsLibLoaded(void* ArgsRV);
Expand All @@ -133,29 +115,6 @@ namespace ThunkFunctions {
struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler {
std::shared_mutex ThunksMutex;

fextl::unordered_map<FEXCore::IR::SHA256Sum, FEXCore::ThunkedFunction*, TruncatingSHA256Hash> 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},
&ThunkFunctions::LoadLib},
{// sha256(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},
&ThunkFunctions::IsLibLoaded},
{// sha256(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},
&ThunkFunctions::IsHostHeapAllocation},
{// 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},
&ThunkFunctions::LinkAddressToGuestFunction},
{// 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},
&ThunkFunctions::AllocateHostTrampolineForGuestFunction},
};

// Can't be a string_view. We need to keep a copy of the library name in-case string_view pointer goes away.
// Ideally we track when a library has been unloaded and remove it from this set before the memory backing goes away.
fextl::set<fextl::string> Libs;
Expand All @@ -165,7 +124,6 @@ struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler {
uint8_t* HostTrampolineInstanceDataPtr;
size_t HostTrampolineInstanceDataAvailable = 0;


/*
Set arg0/1 to arg regs, use CTX::HandleCallback to handle the callback
*/
Expand Down Expand Up @@ -214,138 +172,91 @@ struct ThunkHandler_impl final : public FEX::HLE::ThunkHandler {
}
}

FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS);
FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32);
};

namespace ThunkFunctions {
void LoadLib(void* ArgsV) {
auto Args = reinterpret_cast<LoadlibArgs*>(ArgsV);

std::string_view Name = Args->Name;
auto ThunkHandler = reinterpret_cast<ThunkHandler_impl*>(FEX::HLE::_SyscallHandler->GetThunkHandler());

auto SOName =
(ThunkHandler->Is64BitMode() ? ThunkHandler->ThunkHostLibsPath() : ThunkHandler->ThunkHostLibsPath32()) + "/" + Name.data() + "-host.so";

LogMan::Msg::DFmt("LoadLib: {} -> {}", Name, SOName);

auto Handle = dlopen(SOName.c_str(), RTLD_LOCAL | RTLD_NOW);
if (!Handle) {
ERROR_AND_DIE_FMT("LoadLib: Failed to dlopen thunk library {}: {}", SOName, dlerror());
}

// Library names often include dashes, which may not be used in C++ identifiers.
// They are replaced with underscores hence.
auto InitSym = "fexthunks_exports_" + fextl::string {Name};
std::replace(InitSym.begin(), InitSym.end(), '-', '_');
void LoadLib(std::string_view Name);

ExportEntry* (*InitFN)();
(void*&)InitFN = dlsym(Handle, InitSym.c_str());
if (!InitFN) {
ERROR_AND_DIE_FMT("LoadLib: Failed to find export {}", InitSym);
private:
// Bits in a SHA256 sum are already randomly distributed, so truncation yields a suitable hash function
struct TruncatingSHA256Hash {
size_t operator()(const FEXCore::IR::SHA256Sum& SHA256Sum) const noexcept {
return (const size_t&)SHA256Sum;
}
};

auto Exports = InitFN();
if (!Exports) {
ERROR_AND_DIE_FMT("LoadLib: Failed to initialize thunk library {}. "
"Check if the corresponding host library is installed "
"or disable thunking of this library.",
Name);
}
fextl::unordered_map<FEXCore::IR::SHA256Sum, FEXCore::ThunkedFunction*, TruncatingSHA256Hash> 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},
&ThunkFunctions::LoadLib},
{// sha256(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},
&ThunkFunctions::IsLibLoaded},
{// sha256(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},
&ThunkFunctions::IsHostHeapAllocation},
{// 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},
&ThunkFunctions::LinkAddressToGuestFunction},
{// 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},
&ThunkFunctions::AllocateHostTrampolineForGuestFunction},
};

{
std::lock_guard lk(ThunkHandler->ThunksMutex);
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
FEX_CONFIG_OPT(ThunkHostLibsPath, THUNKHOSTLIBS);
FEX_CONFIG_OPT(ThunkHostLibsPath32, THUNKHOSTLIBS32);
};

ThunkHandler->Libs.insert(fextl::string {Name});
void ThunkHandler_impl::LoadLib(std::string_view Name) {
auto SOName = (Is64BitMode() ? ThunkHostLibsPath() : ThunkHostLibsPath32()) + "/" + Name.data() + "-host.so";

int i;
for (i = 0; Exports[i].sha256; i++) {
ThunkHandler->Thunks[*reinterpret_cast<FEXCore::IR::SHA256Sum*>(Exports[i].sha256)] = Exports[i].Fn;
}
LogMan::Msg::DFmt("LoadLib: {} -> {}", Name, SOName);

LogMan::Msg::DFmt("Loaded {} syms", i);
}
auto Handle = dlopen(SOName.c_str(), RTLD_LOCAL | RTLD_NOW);
if (!Handle) {
ERROR_AND_DIE_FMT("LoadLib: Failed to dlopen thunk library {}: {}", SOName, dlerror());
}

void IsLibLoaded(void* ArgsRV) {
struct ArgsRV_t {
const char* Name;
bool rv;
};
// Library names often include dashes, which may not be used in C++ identifiers.
// They are replaced with underscores hence.
auto InitSym = "fexthunks_exports_" + fextl::string {Name};
std::replace(InitSym.begin(), InitSym.end(), '-', '_');

auto& [Name, rv] = *reinterpret_cast<ArgsRV_t*>(ArgsRV);
auto ThunkHandler = reinterpret_cast<ThunkHandler_impl*>(FEX::HLE::_SyscallHandler->GetThunkHandler());
struct ExportEntry {
uint8_t* sha256;
FEXCore::ThunkedFunction* Fn;
};

{
std::shared_lock lk(ThunkHandler->ThunksMutex);
rv = ThunkHandler->Libs.contains(Name);
}
ExportEntry* (*InitFN)();
(void*&)InitFN = dlsym(Handle, InitSym.c_str());
if (!InitFN) {
ERROR_AND_DIE_FMT("LoadLib: Failed to find export {}", InitSym);
}

/**
* Checks if the given pointer is allocated on the host heap.
*
* This is useful for thunking APIs that need to work with both guest
* and host heap pointers.
*/
void IsHostHeapAllocation(void* ArgsRV) {
#ifdef ENABLE_JEMALLOC_GLIBC
struct ArgsRV_t {
void* ptr;
bool rv;
}* args = reinterpret_cast<ArgsRV_t*>(ArgsRV);

args->rv = glibc_je_is_known_allocation(args->ptr);
#else
// Thunks usage without jemalloc isn't supported
ERROR_AND_DIE_FMT("Unsupported: Thunks querying for host heap allocation information");
#endif
auto Exports = InitFN();
if (!Exports) {
ERROR_AND_DIE_FMT("LoadLib: Failed to initialize thunk library {}. "
"Check if the corresponding host library is installed "
"or disable thunking of this library.",
Name);
}

/**
* 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.
*
* For 32-bit the implicit argument is stored in the lower 32-bits of mm0.
*
* The primary use case of this is ensuring that host function pointers
* returned from thunked APIs can safely be called by the guest.
*/
void LinkAddressToGuestFunction(void* argsv) {
struct args_t {
uintptr_t original_callee;
uintptr_t target_addr; // Guest function to call when branching to original_callee
};
{
std::lock_guard lk(ThunksMutex);

auto args = reinterpret_cast<args_t*>(argsv);
auto CTX = static_cast<FEXCore::Context::Context*>(ThreadObject->Thread->CTX);
CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr);
}
Libs.insert(fextl::string {Name});

/**
* 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 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.
*/
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);
int i;
for (i = 0; Exports[i].sha256; i++) {
Thunks[*reinterpret_cast<FEXCore::IR::SHA256Sum*>(Exports[i].sha256)] = Exports[i].Fn;
}

args->rv = (uintptr_t)MakeHostTrampolineForGuestFunction(nullptr, args->GuestTarget, args->GuestUnpacker);
LogMan::Msg::DFmt("Loaded {} syms", i);
}
} // namespace ThunkFunctions
}

/**
* Generates a host-callable trampoline to call guest functions via the host ABI.
Expand Down Expand Up @@ -423,7 +334,6 @@ MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uint
}

FEX_DEFAULT_VISIBILITY void FinalizeHostTrampolineForGuestFunction(HostToGuestTrampolinePtr* TrampolineAddress, void* HostPacker) {

if (TrampolineAddress == nullptr) {
return;
}
Expand All @@ -439,6 +349,96 @@ FEX_DEFAULT_VISIBILITY void FinalizeHostTrampolineForGuestFunction(HostToGuestTr
}
}

namespace ThunkFunctions {
void LoadLib(void* ArgsV) {
struct LoadlibArgs {
const char* Name;
};

auto Args = reinterpret_cast<LoadlibArgs*>(ArgsV);
auto ThunkHandler = reinterpret_cast<ThunkHandler_impl*>(FEX::HLE::_SyscallHandler->GetThunkHandler());

ThunkHandler->LoadLib(Args->Name);
}

void IsLibLoaded(void* ArgsRV) {
struct ArgsRV_t {
const char* Name;
bool rv;
};

auto& [Name, rv] = *reinterpret_cast<ArgsRV_t*>(ArgsRV);
auto ThunkHandler = reinterpret_cast<ThunkHandler_impl*>(FEX::HLE::_SyscallHandler->GetThunkHandler());

{
std::shared_lock lk(ThunkHandler->ThunksMutex);
rv = ThunkHandler->Libs.contains(Name);
}
}

/**
* Checks if the given pointer is allocated on the host heap.
*
* This is useful for thunking APIs that need to work with both guest
* and host heap pointers.
*/
void IsHostHeapAllocation(void* ArgsRV) {
#ifdef ENABLE_JEMALLOC_GLIBC
struct ArgsRV_t {
void* ptr;
bool rv;
}* args = reinterpret_cast<ArgsRV_t*>(ArgsRV);

args->rv = glibc_je_is_known_allocation(args->ptr);
#else
// Thunks usage without jemalloc isn't supported
ERROR_AND_DIE_FMT("Unsupported: Thunks querying for host heap allocation information");
#endif
}

/**
* 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.
*
* For 32-bit the implicit argument is stored in the lower 32-bits of mm0.
*
* The primary use case of this is ensuring that host function pointers
* returned from thunked APIs can safely be called by the guest.
*/
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 = static_cast<FEXCore::Context::Context*>(ThreadObject->Thread->CTX);
CTX->AddThunkTrampolineIRHandler(args->original_callee, args->target_addr);
}

/**
* 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 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.
*/
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);

args->rv = (uintptr_t)MakeHostTrampolineForGuestFunction(nullptr, args->GuestTarget, args->GuestUnpacker);
}
} // namespace ThunkFunctions

FEX_DEFAULT_VISIBILITY void* GetGuestStack() {
if (!ThreadObject) {
ERROR_AND_DIE_FMT("Thunked library attempted to query guest stack pointer asynchronously");
Expand Down
2 changes: 2 additions & 0 deletions Source/Tools/LinuxEmulation/Thunks.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include <FEXCore/Core/Thunks.h>
#include <FEXCore/fextl/memory.h>

#include <span>

namespace FEX::HLE {
struct ThreadStateObject;

Expand Down

0 comments on commit 9619587

Please sign in to comment.