From e9eca0a4c95c8d3e8cbc7f94dbdf3af5ce9db2df Mon Sep 17 00:00:00 2001 From: emoose Date: Tue, 7 Jan 2020 10:21:41 +0000 Subject: [PATCH] [Kernel/CPU] Support loading import libraries This mostly works the same as https://github.com/xenia-project/xenia/pull/1354, with a couple minor fixes (thanks to Dr. Chat for the suggestions!) Eg. the library will only be loaded inside SetupLibraryImports now, instead of inside GetModule - this seems like a better solution, as Dr. Chat said GetModule could be used in places that aren't meant to load any extra modules. Libraries loaded by guest code (eg. LoadLibrary) should still execute under the same thread as the caller now too - I'm sure I already had code for this in the PR already, but seems not... maybe forgot to commit or something. So now only libraries that are included in the import headers will have a guest thread created to run DllMain (as there wouldn't be any guest threads available to run it inside, since the main XEX is still being loaded) I've also added some code in this commit to wait for this DllMain thread to complete before continuing the import load, which should help prevent any race conditions. With these changes Fuzion Frenzy 2 will now get to the intro movies (as long as signin_state > 0) but quickly freezes during them... unsure why this happens. (the old imports PR also did the same AFAIK, and apparently also broke BF3, haven't been able to test that yet though) --- src/xenia/cpu/xex_module.cc | 17 +++-- src/xenia/kernel/kernel_state.cc | 66 +++++++++++++------ src/xenia/kernel/user_module.cc | 11 +++- .../kernel/xboxkrnl/xboxkrnl_threading.cc | 8 ++- src/xenia/kernel/xthread.cc | 22 +++++-- src/xenia/kernel/xthread.h | 8 ++- 6 files changed, 92 insertions(+), 40 deletions(-) diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index 4fed0907c..a20cbf40d 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -11,6 +11,10 @@ #include +#include "third_party/crypto/TinySHA1.hpp" +#include "third_party/crypto/rijndael-alg-fst.c" +#include "third_party/crypto/rijndael-alg-fst.h" +#include "third_party/pe/pe_image.h" #include "xenia/base/byte_order.h" #include "xenia/base/logging.h" #include "xenia/base/math.h" @@ -20,13 +24,9 @@ #include "xenia/cpu/lzx.h" #include "xenia/cpu/processor.h" #include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/user_module.h" #include "xenia/kernel/xmodule.h" -#include "third_party/crypto/TinySHA1.hpp" -#include "third_party/crypto/rijndael-alg-fst.c" -#include "third_party/crypto/rijndael-alg-fst.h" -#include "third_party/pe/pe_image.h" - static const uint8_t xe_xex2_retail_key[16] = { 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3, 0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91}; @@ -484,8 +484,6 @@ int XexModule::ReadImage(const void* xex_addr, size_t xex_length, return 0; } - memory()->LookupHeap(base_address_)->Reset(); - aes_decrypt_buffer( use_dev_key ? xe_xex2_devkit_key : xe_xex2_retail_key, reinterpret_cast(xex_security_info()->aes_key), 16, @@ -1095,6 +1093,11 @@ bool XexModule::SetupLibraryImports(const char* name, auto user_module = kernel_state_->GetModule(name); + if (!kernel_resolver && !user_module) { + // Library module hasn't been loaded in, ask kernel to load it: + user_module = kernel_state_->LoadUserModule(name); + } + std::string libbasename = name; auto dot = libbasename.find_last_of('.'); if (dot != libbasename.npos) { diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 5c55e9f88..7b7397b89 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -233,35 +233,54 @@ object_ref KernelState::LaunchModule(object_ref module) { return nullptr; } - SetExecutableModule(module); - XELOGI("KernelState: Launching module..."); + XELOGI("KernelState: Launching module %s...", module->path().c_str()); // Create a thread to run in. // We start suspended so we can run the debugger prep. - auto thread = object_ref( - new XThread(kernel_state(), module->stack_size(), 0, - module->entry_point(), 0, X_CREATE_SUSPENDED, true, true)); + xe::kernel::object_ref thread = nullptr; - // We know this is the 'main thread'. + if (!module->is_dll_module()) { + // Not a DLL module, run entrypoint as normal + thread = object_ref(new XThread( + kernel_state(), module->stack_size(), XThread::StartupType::Normal, 0, + module->entry_point(), 0, X_CREATE_SUSPENDED, true, true)); + } else { + // Run entrypoint as DllMain, using module handle as start context + thread = object_ref( + new XThread(kernel_state(), module->stack_size(), + XThread::StartupType::DllMain, 0, module->entry_point(), + module->handle(), X_CREATE_SUSPENDED, true, true)); + } + + // We know this is the 'main thread', or ' thread' char thread_name[32]; - std::snprintf(thread_name, xe::countof(thread_name), "Main XThread%08X", + std::snprintf(thread_name, xe::countof(thread_name), "%s XThread%08X", + module->is_dll_module() ? module->name().c_str() : "Main", thread->handle()); thread->set_name(thread_name); X_STATUS result = thread->Create(); if (XFAILED(result)) { - XELOGE("Could not create launch thread: %.8X", result); + XELOGE("Could not create launch thread for %s: %.8X", + module->path().c_str(), result); return nullptr; } // Waits for a debugger client, if desired. - emulator()->processor()->PreLaunch(); + if (!module->is_dll_module()) { + emulator()->processor()->PreLaunch(); + } // Resume the thread now. // If the debugger has requested a suspend this will just decrement the // suspend count without resuming it until the debugger wants. thread->Resume(); + // If this is a DLL, wait for DllMain to complete before continuing + if (module->is_dll_module()) { + thread->Wait(0, 0, false, 0); + } + return thread; } @@ -378,6 +397,9 @@ object_ref KernelState::LoadUserModule(const char* raw_name, module = object_ref(new UserModule(this)); X_STATUS status = module->LoadFromFile(path); if (XFAILED(status)) { + if (executable_module_.get() == module.get()) { + executable_module_.reset(); + } object_table()->ReleaseHandle(module->handle()); return nullptr; } @@ -391,16 +413,22 @@ object_ref KernelState::LoadUserModule(const char* raw_name, module->Dump(); if (module->is_dll_module() && module->entry_point() && call_entry) { - // Call DllMain(DLL_PROCESS_ATTACH): - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx - uint64_t args[] = { - module->handle(), - 1, // DLL_PROCESS_ATTACH - 0, // 0 because always dynamic - }; - auto thread_state = XThread::GetCurrentThread()->thread_state(); - processor()->Execute(thread_state, module->entry_point(), args, - xe::countof(args)); + // If we're not in a thread, use LaunchModule to run DllMain for us + // Otherwise we can just execute it in the current thread instead + if (!XThread::IsInThread()) { + LaunchModule(module); + } else { + // Call DllMain(DLL_PROCESS_ATTACH): + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx + uint64_t args[] = { + module->handle(), + 1, // DLL_PROCESS_ATTACH + 0, // 0 because always dynamic + }; + auto thread_state = XThread::GetCurrentThread()->thread_state(); + processor()->Execute(thread_state, module->entry_point(), args, + xe::countof(args)); + } } return module; diff --git a/src/xenia/kernel/user_module.cc b/src/xenia/kernel/user_module.cc index f567ed502..525af2881 100644 --- a/src/xenia/kernel/user_module.cc +++ b/src/xenia/kernel/user_module.cc @@ -244,9 +244,9 @@ X_STATUS UserModule::LoadXexContinue() { return X_STATUS_SUCCESS; } - // Finish XexModule load (PE sections/imports/symbols...) - if (!xex_module()->LoadContinue()) { - return X_STATUS_UNSUCCESSFUL; + // Notify kernel that we have an executable if we haven't got one already + if (!kernel_state_->GetExecutableModule()) { + kernel_state_->SetExecutableModule(object_ref(this)); } // Copy the xex2 header into guest memory. @@ -272,6 +272,11 @@ X_STATUS UserModule::LoadXexContinue() { ldr_data->image_base = this->xex_module()->base_address(); ldr_data->entry_point = entry_point_; + // Finish XexModule load (PE sections/imports/symbols...) + if (!xex_module()->LoadContinue()) { + return X_STATUS_UNSUCCESSFUL; + } + OnLoad(); return X_STATUS_SUCCESS; diff --git a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc index b12325dcd..70aba9a66 100644 --- a/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl/xboxkrnl_threading.cc @@ -122,9 +122,11 @@ dword_result_t ExCreateThread(lpdword_t handle_ptr, dword_t stack_size, std::max((uint32_t)0x4000, ((actual_stack_size + 0xFFF) & 0xFFFFF000)); auto thread = object_ref( - new XThread(kernel_state(), actual_stack_size, xapi_thread_startup, - start_address.guest_address(), start_context.guest_address(), - creation_flags, true)); + new XThread(kernel_state(), actual_stack_size, + xapi_thread_startup ? XThread::StartupType::XapiThreadStartup + : XThread::StartupType::Normal, + xapi_thread_startup, start_address.guest_address(), + start_context.guest_address(), creation_flags, true)); X_STATUS result = thread->Create(); if (XFAILED(result)) { diff --git a/src/xenia/kernel/xthread.cc b/src/xenia/kernel/xthread.cc index 593096c23..6ea139ec8 100644 --- a/src/xenia/kernel/xthread.cc +++ b/src/xenia/kernel/xthread.cc @@ -50,15 +50,16 @@ XThread::XThread(KernelState* kernel_state) : XObject(kernel_state, kType), guest_thread_(true) {} XThread::XThread(KernelState* kernel_state, uint32_t stack_size, - uint32_t xapi_thread_startup, uint32_t start_address, - uint32_t start_context, uint32_t creation_flags, - bool guest_thread, bool main_thread) + StartupType startup_type, uint32_t xapi_thread_startup, + uint32_t start_address, uint32_t start_context, + uint32_t creation_flags, bool guest_thread, bool main_thread) : XObject(kernel_state, kType), thread_id_(++next_xthread_id_), guest_thread_(guest_thread), main_thread_(main_thread), apc_list_(kernel_state->memory()) { creation_params_.stack_size = stack_size; + creation_params_.startup_type = startup_type; creation_params_.xapi_thread_startup = xapi_thread_startup; creation_params_.start_address = start_address; creation_params_.start_context = start_context; @@ -515,12 +516,22 @@ void XThread::Execute() { // If a XapiThreadStartup value is present, we use that as a trampoline. // Otherwise, we are a raw thread. - if (creation_params_.xapi_thread_startup) { + if (creation_params_.startup_type == StartupType::XapiThreadStartup) { uint64_t args[] = {creation_params_.start_address, creation_params_.start_context}; kernel_state()->processor()->Execute(thread_state_, creation_params_.xapi_thread_startup, args, xe::countof(args)); + } else if (creation_params_.startup_type == StartupType::DllMain) { + // Call DllMain(DLL_PROCESS_ATTACH): + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx + uint64_t args[] = { + creation_params_.start_context, + 1, // DLL_PROCESS_ATTACH + 0, // 0 because always dynamic + }; + kernel_state()->processor()->Execute( + thread_state_, creation_params_.start_address, args, xe::countof(args)); } else { // Run user code. uint64_t args[] = {creation_params_.start_context}; @@ -1036,7 +1047,8 @@ object_ref XThread::Restore(KernelState* kernel_state, XHostThread::XHostThread(KernelState* kernel_state, uint32_t stack_size, uint32_t creation_flags, std::function host_fn) - : XThread(kernel_state, stack_size, 0, 0, 0, creation_flags, false), + : XThread(kernel_state, stack_size, XThread::StartupType::Normal, 0, 0, 0, + creation_flags, false), host_fn_(host_fn) { // By default host threads are not debugger suspendable. If the thread runs // any guest code this must be overridden. diff --git a/src/xenia/kernel/xthread.h b/src/xenia/kernel/xthread.h index cfb53967e..b3bfca80d 100644 --- a/src/xenia/kernel/xthread.h +++ b/src/xenia/kernel/xthread.h @@ -106,9 +106,11 @@ static_assert_size(X_KTHREAD, 0xAB0); class XThread : public XObject, public cpu::Thread { public: static const Type kType = kTypeThread; + enum class StartupType { Normal, XapiThreadStartup, DllMain }; struct CreationParams { uint32_t stack_size; + StartupType startup_type; uint32_t xapi_thread_startup; uint32_t start_address; uint32_t start_context; @@ -117,9 +119,9 @@ class XThread : public XObject, public cpu::Thread { XThread(KernelState* kernel_state); XThread(KernelState* kernel_state, uint32_t stack_size, - uint32_t xapi_thread_startup, uint32_t start_address, - uint32_t start_context, uint32_t creation_flags, bool guest_thread, - bool main_thread = false); + StartupType startup_type, uint32_t xapi_thread_startup, + uint32_t start_address, uint32_t start_context, + uint32_t creation_flags, bool guest_thread, bool main_thread = false); ~XThread() override; static bool IsInThread(XThread* other);