diff --git a/External/FEXCore/Source/CMakeLists.txt b/External/FEXCore/Source/CMakeLists.txt index c603ac949..1a3f0bb5f 100644 --- a/External/FEXCore/Source/CMakeLists.txt +++ b/External/FEXCore/Source/CMakeLists.txt @@ -164,6 +164,7 @@ set (SRCS Interface/HLE/Syscalls/Timer.cpp Interface/HLE/Syscalls/NotImplemented.cpp Interface/HLE/Syscalls/Stubs.cpp + Interface/HLE/Thunks/Thunks.cpp Interface/Memory/MemMapper.cpp Interface/Memory/SharedMem.cpp Interface/IR/IR.cpp diff --git a/External/FEXCore/Source/Interface/Config/Config.cpp b/External/FEXCore/Source/Interface/Config/Config.cpp index 7b6b24511..d2d570ddb 100644 --- a/External/FEXCore/Source/Interface/Config/Config.cpp +++ b/External/FEXCore/Source/Interface/Config/Config.cpp @@ -42,6 +42,9 @@ namespace FEXCore::Config { case CONFIG_ROOTFSPATH: CTX->Config.RootFSPath = Config; break; + case CONFIG_THUNKLIBSPATH: + CTX->Config.ThunkLibsPath = Config; + break; default: LogMan::Msg::A("Unknown configuration option"); } } diff --git a/External/FEXCore/Source/Interface/Context/Context.h b/External/FEXCore/Source/Interface/Context/Context.h index 93b5c5f4f..e23c0a739 100644 --- a/External/FEXCore/Source/Interface/Context/Context.h +++ b/External/FEXCore/Source/Interface/Context/Context.h @@ -17,6 +17,7 @@ namespace FEXCore { class SyscallHandler; +class ThunkHandler; class BlockSamplingData; class GdbServer; @@ -52,6 +53,7 @@ namespace FEXCore::Context { bool GdbServer {false}; bool UnifiedMemory {true}; std::string RootFSPath; + std::string ThunkLibsPath; bool Is64BitMode {true}; uint64_t EmulatedCPUCores{1}; @@ -80,6 +82,8 @@ namespace FEXCore::Context { FEXCore::CPUIDEmu CPUID; std::unique_ptr SyscallHandler; + std::unique_ptr ThunkHandler; + CustomCPUFactoryType CustomCPUFactory; CustomCPUFactoryType FallbackCPUFactory; std::function CustomExitHandler; diff --git a/External/FEXCore/Source/Interface/Core/Core.cpp b/External/FEXCore/Source/Interface/Core/Core.cpp index 6eabab064..45e30de25 100644 --- a/External/FEXCore/Source/Interface/Core/Core.cpp +++ b/External/FEXCore/Source/Interface/Core/Core.cpp @@ -19,6 +19,8 @@ #include #include +#include "Interface/HLE/Thunks/Thunks.h" + #include #include @@ -229,6 +231,8 @@ namespace FEXCore::Context { bool Context::InitCore(FEXCore::CodeLoader *Loader) { SyscallHandler.reset(FEXCore::CreateHandler(Config.Is64BitMode ? OperatingMode::MODE_64BIT : OperatingMode::MODE_32BIT, this)); + ThunkHandler.reset(FEXCore::ThunkHandler::Create()); + LocalLoader = Loader; using namespace FEXCore::Core; FEXCore::Core::CPUState NewThreadState{}; diff --git a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp index 4b0f3c289..a1b20a544 100644 --- a/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp +++ b/External/FEXCore/Source/Interface/Core/Interpreter/InterpreterCore.cpp @@ -11,6 +11,8 @@ #include #include +#include "Interface/HLE/Thunks/Thunks.h" + #include #include #include @@ -248,6 +250,15 @@ void InterpreterCore::ExecuteCode(FEXCore::Core::InternalThreadState *Thread) { GD = Res; break; } + case IR::OP_THUNK: { + auto Op = IROp->C(); + + //LogMan::Msg::D("Thunk function: %s, %p, %p\n", Op->ThunkName, Op->ThunkFnPtr, *GetSrc(Op->Header.Args[0])); + + reinterpret_cast(Op->ThunkFnPtr)(CTX, *GetSrc(Op->Header.Args[0])); + + break; + } case IR::OP_CPUID: { auto Op = IROp->C(); uint64_t *DstPtr = GetDest(*WrapperOp); diff --git a/External/FEXCore/Source/Interface/Core/JIT/Arm64/BranchOps.cpp b/External/FEXCore/Source/Interface/Core/JIT/Arm64/BranchOps.cpp index 67a71127a..62466cb32 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/Arm64/BranchOps.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/Arm64/BranchOps.cpp @@ -123,6 +123,45 @@ DEF_OP(Syscall) { add(sp, sp, SPOffset); } +DEF_OP(Thunk) { + auto Op = IROp->C(); + // Arguments are passed as follows: + // X0: CTX + // X1: Args (from guest stack) + + uint64_t SPOffset = AlignUp((RA64.size() + 1) * 8, 16); + + sub(sp, sp, SPOffset); + + int i = 0; + for (auto RA : RA64) { + str(RA, MemOperand(sp, i * 8)); + i++; + } + str(lr, MemOperand(sp, RA64.size() * 8 + 0 * 8)); + + LoadConstant(x0, reinterpret_cast(CTX)); + mov(x1, GetReg(Op->Header.Args[0].ID())); + +#if _M_X86_64 + ERROR_AND_DIE("JIT: OP_THUNK not supported with arm simulator") +#else + LoadConstant(x2, Op->ThunkFnPtr); + blr(x2); +#endif + + // Fix the stack and any values that were stepped on + i = 0; + for (auto RA : RA64) { + ldr(RA, MemOperand(sp, i * 8)); + i++; + } + + ldr(lr, MemOperand(sp, RA64.size() * 8 + 0 * 8)); + + add(sp, sp, SPOffset); +} + DEF_OP(CPUID) { auto Op = IROp->C(); uint64_t SPOffset = AlignUp((RA64.size() + 2 + 2) * 8, 16); @@ -173,6 +212,7 @@ void JITCore::RegisterBranchHandlers() { REGISTER_OP(JUMP, Jump); REGISTER_OP(CONDJUMP, CondJump); REGISTER_OP(SYSCALL, Syscall); + REGISTER_OP(THUNK, Thunk); REGISTER_OP(CPUID, CPUID); #undef REGISTER_OP } diff --git a/External/FEXCore/Source/Interface/Core/JIT/Arm64/JITClass.h b/External/FEXCore/Source/Interface/Core/JIT/Arm64/JITClass.h index f08b69b95..526ec435c 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/Arm64/JITClass.h +++ b/External/FEXCore/Source/Interface/Core/JIT/Arm64/JITClass.h @@ -277,6 +277,7 @@ private: DEF_OP(Jump); DEF_OP(CondJump); DEF_OP(Syscall); + DEF_OP(Thunk); DEF_OP(CPUID); ///< Conversion ops diff --git a/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp b/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp index 3420c8e46..b7f341153 100644 --- a/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp +++ b/External/FEXCore/Source/Interface/Core/JIT/x86_64/JIT.cpp @@ -2006,6 +2006,33 @@ void *JITCore::CompileCode([[maybe_unused]] FEXCore::IR::IRListView const mov (GetDst(Node), rax); break; } + case IR::OP_THUNK: { + auto Op = IROp->C(); + + auto NumPush = 1 + RA64.size(); + push(rdi); + + for (auto &Reg : RA64) + push(Reg); + + if (NumPush & 1) + sub(rsp, 8); // Align + + mov(rdi, reinterpret_cast(CTX)); + mov(rsi, GetSrc(Op->Header.Args[0].ID())); + + mov(rax, reinterpret_cast(Op->ThunkFnPtr)); + call(rax); + + if (NumPush & 1) + add(rsp, 8); // Align + + for (uint32_t i = RA64.size(); i > 0; --i) + pop(RA64[i - 1]); + + pop(rdi); + break; + } case IR::OP_VEXTRACTTOGPR: { auto Op = IROp->C(); diff --git a/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp b/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp index 0afbb6b5f..9a992d6ed 100644 --- a/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp +++ b/External/FEXCore/Source/Interface/Core/LLVMJIT/LLVMCore.cpp @@ -106,6 +106,7 @@ private: struct LLVMCurrentState { llvm::Function *SyscallFunction; + llvm::Function *ThunkFunction; llvm::Function *CPUIDFunction; llvm::Function *ExitVMFunction; llvm::Function *ValuePrinter; @@ -991,6 +992,31 @@ void LLVMJITCore::CreateGlobalVariables(llvm::ExecutionEngine *Engine, llvm::Mod Engine->addGlobalMapping(JITCurrentState.SyscallFunction, Ptr.Data); } + // Thunk Function + { + auto FuncType = FunctionType::get(voidTy, + { + i64, // Technically a this pointer + i64, // Technically a this pointer + i64, // Technically a this pointer + }, + false); + JITCurrentState.ThunkFunction = Function::Create(FuncType, + Function::ExternalLinkage, + "ThunkCaller", + FunctionModule); + using ClassPtrType = void (*)(void (*)(void*, void*), void*, void*); + union PtrCast { + ClassPtrType ClassPtr; + void* Data; + }; + PtrCast Ptr; + Ptr.ClassPtr = [](void(*fn)(void*, void*), void* ctx, void* args) { + fn(ctx, args); + }; + Engine->addGlobalMapping(JITCurrentState.ThunkFunction, Ptr.Data); + } + // CPUID Function { auto FuncType = FunctionType::get(i128, @@ -1687,6 +1713,17 @@ void LLVMJITCore::HandleIR(FEXCore::IR::IRListView const *IR, IR::NodeWrap SetDest(*WrapperOp, Result); break; } + case IR::OP_THUNK: { + auto Op = IROp->C(); + std::vector Args{}; + + Args.emplace_back(JITState.IRBuilder->getInt64(Op->ThunkFnPtr)); // function to call + Args.emplace_back(JITState.IRBuilder->getInt64(reinterpret_cast(CTX))); // context + Args.emplace_back(GetSrc(Op->Header.Args[0])); // params + + JITState.IRBuilder->CreateCall(JITCurrentState.ThunkFunction, Args); + break; + } case IR::OP_CPUID: { auto Op = IROp->C(); auto Src = GetSrc(Op->Header.Args[0]); diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 7504785da..20206d0ed 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -1,5 +1,7 @@ #include "Interface/Context/Context.h" #include "Interface/Core/OpcodeDispatcher.h" +#include "Interface/HLE/Thunks/Thunks.h" + #include #include #include @@ -85,6 +87,41 @@ void OpDispatchBuilder::SyscallOp(OpcodeArgs) { _StoreContext(GPRClass, GPRSize, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RAX]), SyscallOp); } +void OpDispatchBuilder::ThunkOp(OpcodeArgs) { + uint8_t GPRSize = CTX->Config.Is64BitMode ? 8 : 4; + const char *name; + + if (CTX->Config.UnifiedMemory) { + name = (const char*)(Op->PC + 2); + } else { + name = CTX->MemoryMapper.GetPointer(Op->PC + 2); + } + + _Thunk( + _LoadContext(GPRSize, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDI]), GPRClass), + name, + (uintptr_t)CTX->ThunkHandler->LookupThunk(name) + ); + + auto Constant = _Constant(GPRSize); + + auto OldSP = _LoadContext(GPRSize, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSP]), GPRClass); + + auto NewRIP = _LoadMem(GPRClass, GPRSize, OldSP, GPRSize); + + OrderedNode *NewSP; + + NewSP = _Add(OldSP, Constant); + + // Store the new stack pointer + _StoreContext(GPRClass, GPRSize, offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSP]), NewSP); + + // Store the new RIP + _StoreContext(GPRClass, GPRSize, offsetof(FEXCore::Core::CPUState, rip), NewRIP); + _ExitFunction(); + BlockSetRIP = true; +} + void OpDispatchBuilder::LEAOp(OpcodeArgs) { if (CTX->Config.Is64BitMode) { uint32_t DstSize = X86Tables::DecodeFlags::GetOpAddr(Op->Flags, 0) == X86Tables::DecodeFlags::FLAG_OPERAND_SIZE_LAST ? 2 : @@ -7082,6 +7119,7 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) { {0x31, 1, &OpDispatchBuilder::RDTSCOp}, + {0x3F, 1, &OpDispatchBuilder::ThunkOp}, {0x40, 16, &OpDispatchBuilder::CMOVOp}, {0x6E, 1, &OpDispatchBuilder::MOVBetweenGPR_FPR}, {0x6F, 1, &OpDispatchBuilder::MOVUPSOp}, diff --git a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h index f8b47c7cb..cbf8ce641 100644 --- a/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h +++ b/External/FEXCore/Source/Interface/Core/OpcodeDispatcher.h @@ -100,6 +100,7 @@ public: void ALUOp(OpcodeArgs); void INTOp(OpcodeArgs); void SyscallOp(OpcodeArgs); + void ThunkOp(OpcodeArgs); void LEAOp(OpcodeArgs); void NOPOp(OpcodeArgs); void RETOp(OpcodeArgs); diff --git a/External/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp b/External/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp index 0dcef4737..6a7eb8de7 100644 --- a/External/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp +++ b/External/FEXCore/Source/Interface/Core/X86Tables/SecondaryTables.cpp @@ -57,7 +57,7 @@ void InitializeSecondaryTables() { {0x38, 1, X86InstInfo{"", TYPE_0F38_TABLE, FLAGS_NO_OVERLAY, 0, nullptr}}, {0x39, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0, nullptr}}, {0x3A, 1, X86InstInfo{"", TYPE_0F3A_TABLE, FLAGS_NO_OVERLAY, 0, nullptr}}, - {0x3B, 5, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0, nullptr}}, + {0x3B, 4, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0, nullptr}}, {0x40, 1, X86InstInfo{"CMOVO", TYPE_INST, FLAGS_MODRM | FLAGS_NO_OVERLAY, 0, nullptr}}, {0x41, 1, X86InstInfo{"CMOVNO", TYPE_INST, FLAGS_MODRM | FLAGS_NO_OVERLAY, 0, nullptr}}, @@ -248,6 +248,12 @@ void InitializeSecondaryTables() { {0xFD, 1, X86InstInfo{"PADDW", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 0, nullptr}}, {0xFE, 1, X86InstInfo{"PADDD", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 0, nullptr}}, {0xFF, 1, X86InstInfo{"UD0", TYPE_INST, FLAGS_DEBUG | FLAGS_BLOCK_END, 0, nullptr}}, + + // FEX reserved instructions + // Unused x86 encoding instruction. + + // This was originally used by VIA to jump to its alternative instruction set. Used for OP_THUNK + {0x3F, 1, X86InstInfo{"ALTINST", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 0, nullptr}}, }; const U8U8InfoStruct RepModOpTable[] = { diff --git a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp new file mode 100644 index 000000000..346bb5b4a --- /dev/null +++ b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.cpp @@ -0,0 +1,92 @@ +#include "Thunks.h" +#include + +#include "stdio.h" +#include + + +#include +#include +#include +#include +#include +#include + +namespace FEXCore { + + struct ExportEntry { const char* Name; ThunkedFunction* Fn; }; + + class ThunkHandler_impl final: public ThunkHandler { + std::shared_mutex ThunksMutex; + + std::map Thunks = { + { "fex:loadlib", &loadlib} + }; + + static void loadlib(FEXCore::Context::Context *CTX, void* Name) { + if (!CTX->Config.UnifiedMemory) { + Name = CTX->MemoryMapper.GetPointer((uintptr_t)Name); + } + + auto SOName = CTX->Config.ThunkLibsPath + "/" + (const char*)Name + "-host.so"; + + LogMan::Msg::D("Load lib: %s -> %s", Name, SOName.c_str()); + + auto Handle = dlopen(SOName.c_str(), RTLD_LOCAL | RTLD_NOW); + + if (!Handle) { + LogMan::Msg::E("Load lib: failed to dlopen %s", SOName.c_str()); + return; + } + + ExportEntry* (*InitFN)(); + + auto InitSym = std::string("fexthunks_exports_") + (const char*)Name; + + (void*&)InitFN = dlsym(Handle, InitSym.c_str()); + + if (!InitFN) { + LogMan::Msg::E("Load lib: failed to find export %s", InitSym.c_str()); + return; + } + + auto Exports = InitFN(); + + auto That = reinterpret_cast(CTX->ThunkHandler.get()); + + { + std::unique_lock lk(That->ThunksMutex); + + int i; + for (i = 0; Exports[i].Name; i++) { + That->Thunks[Exports[i].Name] = Exports[i].Fn; + } + + LogMan::Msg::D("Loaded %d syms", i); + } + } + + public: + + ThunkedFunction* LookupThunk(const char *Name) { + + std::shared_lock lk(ThunksMutex); + + auto it = Thunks.find(Name); + + if (it != Thunks.end()) { + return it->second; + } else { + return nullptr; + } + } + + ThunkHandler_impl() { + + } + }; + + ThunkHandler* ThunkHandler::Create() { + return new ThunkHandler_impl(); + } +} \ No newline at end of file diff --git a/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.h b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.h new file mode 100644 index 000000000..2bc2a0080 --- /dev/null +++ b/External/FEXCore/Source/Interface/HLE/Thunks/Thunks.h @@ -0,0 +1,15 @@ +namespace FEXCore::Context { + struct Context; +} + +namespace FEXCore { + typedef void ThunkedFunction(FEXCore::Context::Context *CTX, void* ArgsRv); + + class ThunkHandler { + public: + virtual ThunkedFunction* LookupThunk(const char *name) = 0; + virtual ~ThunkHandler() { } + + static ThunkHandler* Create(); + }; +}; \ No newline at end of file diff --git a/External/FEXCore/Source/Interface/IR/IR.cpp b/External/FEXCore/Source/Interface/IR/IR.cpp index bf5e7aa4e..109472021 100644 --- a/External/FEXCore/Source/Interface/IR/IR.cpp +++ b/External/FEXCore/Source/Interface/IR/IR.cpp @@ -15,6 +15,10 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView *out << "#0x" << std::hex << Arg; } +static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, const char* Arg) { + *out << Arg; +} + static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, CondClassType Arg) { std::array CondNames = { "EQ", diff --git a/External/FEXCore/Source/Interface/IR/IR.json b/External/FEXCore/Source/Interface/IR/IR.json index 895d78a85..929db5454 100644 --- a/External/FEXCore/Source/Interface/IR/IR.json +++ b/External/FEXCore/Source/Interface/IR/IR.json @@ -483,6 +483,19 @@ ] }, + "Thunk": { + "HasSideEffects": true, + "OpClass": "Branch", + "SSAArgs": "1", + "SSANames": [ + "ArgPtr" + ], + "Args":[ + "const char*", "ThunkName", + "uintptr_t", "ThunkFnPtr" + ] + }, + "LoadMem": { "OpClass": "Memory", "HasDest": true, diff --git a/External/FEXCore/include/FEXCore/Config/Config.h b/External/FEXCore/include/FEXCore/Config/Config.h index 74083dd5e..d706b0ab8 100644 --- a/External/FEXCore/include/FEXCore/Config/Config.h +++ b/External/FEXCore/include/FEXCore/Config/Config.h @@ -12,6 +12,7 @@ namespace FEXCore::Config { CONFIG_SINGLESTEP, CONFIG_GDBSERVER, CONFIG_ROOTFSPATH, + CONFIG_THUNKLIBSPATH, CONFIG_UNIFIED_MEMORY, CONFIG_IS64BIT_MODE, CONFIG_EMULATED_CPU_CORES, diff --git a/Source/Common/ArgumentLoader.cpp b/Source/Common/ArgumentLoader.cpp index 607d8e5e6..16d614d90 100644 --- a/Source/Common/ArgumentLoader.cpp +++ b/Source/Common/ArgumentLoader.cpp @@ -66,11 +66,16 @@ namespace FEX::ArgLoader { } { Parser.set_defaults("RootFS", ""); + Parser.set_defaults("ThunkLibs", ""); EmulationGroup.add_option("-R", "--rootfs") .dest("RootFS") .help("Which Root filesystem prefix to use"); + EmulationGroup.add_option("-t", "--thunklibs") + .dest("ThunkLibs") + .help("Folder to find the host-side thunking libs"); + EmulationGroup.add_option("-U", "--unified-memory") .dest("UnifiedMemory") .action("store_true") @@ -177,6 +182,11 @@ namespace FEX::ArgLoader { Config::Add("RootFS", Option); } + if (Options.is_set_by_user("ThunkLibs")) { + std::string Option = Options["ThunkLibs"]; + Config::Add("ThunkLibs", Option); + } + if (Options.is_set_by_user("UnifiedMemory")) { bool Option = Options.get("UnifiedMemory"); Config::Add("UnifiedMemory", std::to_string(Option)); diff --git a/Source/Tests/ELFLoader.cpp b/Source/Tests/ELFLoader.cpp index 3d8f13e26..700f6cb46 100644 --- a/Source/Tests/ELFLoader.cpp +++ b/Source/Tests/ELFLoader.cpp @@ -101,6 +101,7 @@ int main(int argc, char **argv, char **const envp) { FEX::Config::Value ThreadsConfig{"Threads", 1}; FEX::Config::Value UnifiedMemory{"UnifiedMemory", true}; FEX::Config::Value LDPath{"RootFS", ""}; + FEX::Config::Value ThunkLibsPath{"ThunkLibs", ""}; FEX::Config::Value SilentLog{"SilentLog", false}; FEX::Config::Value Environment{"Env", ""}; @@ -129,6 +130,7 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_MAXBLOCKINST, BlockSizeConfig()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_GDBSERVER, GdbServerConfig()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_ROOTFSPATH, LDPath()); + FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_THUNKLIBSPATH, ThunkLibsPath()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_UNIFIED_MEMORY, UnifiedMemory()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode()); FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_EMULATED_CPU_CORES, ThreadsConfig()); diff --git a/unittests/ASM/TwoByte/0F_3F.asm b/unittests/ASM/TwoByte/0F_3F.asm new file mode 100644 index 000000000..de3ccc057 --- /dev/null +++ b/unittests/ASM/TwoByte/0F_3F.asm @@ -0,0 +1,26 @@ +%ifdef CONFIG +{ + "RegData": { + }, + "MemoryRegions": { + "0x100000000": "4096" + } +} +%endif + +; this just ensures the thunking system runs +; it doesn't actually load a lib + +lea rdi, [rel libname] +lea rax, [rel thunk_test] +call rax + +hlt + +thunk_test: +db 0xf, 0x3f +db 'fex:loadlib' +db 0 + +libname db 'not_a_real_lib' +db 0 \ No newline at end of file