[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)
This commit is contained in:
emoose 2020-01-07 10:21:41 +00:00
parent f5f8d0dd90
commit e9eca0a4c9
6 changed files with 92 additions and 40 deletions

View File

@ -11,6 +11,10 @@
#include <algorithm> #include <algorithm>
#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/byte_order.h"
#include "xenia/base/logging.h" #include "xenia/base/logging.h"
#include "xenia/base/math.h" #include "xenia/base/math.h"
@ -20,13 +24,9 @@
#include "xenia/cpu/lzx.h" #include "xenia/cpu/lzx.h"
#include "xenia/cpu/processor.h" #include "xenia/cpu/processor.h"
#include "xenia/kernel/kernel_state.h" #include "xenia/kernel/kernel_state.h"
#include "xenia/kernel/user_module.h"
#include "xenia/kernel/xmodule.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] = { static const uint8_t xe_xex2_retail_key[16] = {
0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3, 0x20, 0xB1, 0x85, 0xA5, 0x9D, 0x28, 0xFD, 0xC3,
0x40, 0x58, 0x3F, 0xBB, 0x08, 0x96, 0xBF, 0x91}; 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; return 0;
} }
memory()->LookupHeap(base_address_)->Reset();
aes_decrypt_buffer( aes_decrypt_buffer(
use_dev_key ? xe_xex2_devkit_key : xe_xex2_retail_key, use_dev_key ? xe_xex2_devkit_key : xe_xex2_retail_key,
reinterpret_cast<const uint8_t*>(xex_security_info()->aes_key), 16, reinterpret_cast<const uint8_t*>(xex_security_info()->aes_key), 16,
@ -1095,6 +1093,11 @@ bool XexModule::SetupLibraryImports(const char* name,
auto user_module = kernel_state_->GetModule(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; std::string libbasename = name;
auto dot = libbasename.find_last_of('.'); auto dot = libbasename.find_last_of('.');
if (dot != libbasename.npos) { if (dot != libbasename.npos) {

View File

@ -233,35 +233,54 @@ object_ref<XThread> KernelState::LaunchModule(object_ref<UserModule> module) {
return nullptr; return nullptr;
} }
SetExecutableModule(module); XELOGI("KernelState: Launching module %s...", module->path().c_str());
XELOGI("KernelState: Launching module...");
// Create a thread to run in. // Create a thread to run in.
// We start suspended so we can run the debugger prep. // We start suspended so we can run the debugger prep.
auto thread = object_ref<XThread>( xe::kernel::object_ref<XThread> thread = nullptr;
new XThread(kernel_state(), module->stack_size(), 0,
module->entry_point(), 0, X_CREATE_SUSPENDED, true, true));
// We know this is the 'main thread'. if (!module->is_dll_module()) {
// Not a DLL module, run entrypoint as normal
thread = object_ref<XThread>(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<XThread>(
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 '<dllname> thread'
char thread_name[32]; 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->handle());
thread->set_name(thread_name); thread->set_name(thread_name);
X_STATUS result = thread->Create(); X_STATUS result = thread->Create();
if (XFAILED(result)) { 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; return nullptr;
} }
// Waits for a debugger client, if desired. // Waits for a debugger client, if desired.
emulator()->processor()->PreLaunch(); if (!module->is_dll_module()) {
emulator()->processor()->PreLaunch();
}
// Resume the thread now. // Resume the thread now.
// If the debugger has requested a suspend this will just decrement the // If the debugger has requested a suspend this will just decrement the
// suspend count without resuming it until the debugger wants. // suspend count without resuming it until the debugger wants.
thread->Resume(); 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; return thread;
} }
@ -378,6 +397,9 @@ object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name,
module = object_ref<UserModule>(new UserModule(this)); module = object_ref<UserModule>(new UserModule(this));
X_STATUS status = module->LoadFromFile(path); X_STATUS status = module->LoadFromFile(path);
if (XFAILED(status)) { if (XFAILED(status)) {
if (executable_module_.get() == module.get()) {
executable_module_.reset();
}
object_table()->ReleaseHandle(module->handle()); object_table()->ReleaseHandle(module->handle());
return nullptr; return nullptr;
} }
@ -391,16 +413,22 @@ object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name,
module->Dump(); module->Dump();
if (module->is_dll_module() && module->entry_point() && call_entry) { if (module->is_dll_module() && module->entry_point() && call_entry) {
// Call DllMain(DLL_PROCESS_ATTACH): // If we're not in a thread, use LaunchModule to run DllMain for us
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx // Otherwise we can just execute it in the current thread instead
uint64_t args[] = { if (!XThread::IsInThread()) {
module->handle(), LaunchModule(module);
1, // DLL_PROCESS_ATTACH } else {
0, // 0 because always dynamic // Call DllMain(DLL_PROCESS_ATTACH):
}; // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx
auto thread_state = XThread::GetCurrentThread()->thread_state(); uint64_t args[] = {
processor()->Execute(thread_state, module->entry_point(), args, module->handle(),
xe::countof(args)); 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; return module;

View File

@ -244,9 +244,9 @@ X_STATUS UserModule::LoadXexContinue() {
return X_STATUS_SUCCESS; return X_STATUS_SUCCESS;
} }
// Finish XexModule load (PE sections/imports/symbols...) // Notify kernel that we have an executable if we haven't got one already
if (!xex_module()->LoadContinue()) { if (!kernel_state_->GetExecutableModule()) {
return X_STATUS_UNSUCCESSFUL; kernel_state_->SetExecutableModule(object_ref<UserModule>(this));
} }
// Copy the xex2 header into guest memory. // 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->image_base = this->xex_module()->base_address();
ldr_data->entry_point = entry_point_; ldr_data->entry_point = entry_point_;
// Finish XexModule load (PE sections/imports/symbols...)
if (!xex_module()->LoadContinue()) {
return X_STATUS_UNSUCCESSFUL;
}
OnLoad(); OnLoad();
return X_STATUS_SUCCESS; return X_STATUS_SUCCESS;

View File

@ -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)); std::max((uint32_t)0x4000, ((actual_stack_size + 0xFFF) & 0xFFFFF000));
auto thread = object_ref<XThread>( auto thread = object_ref<XThread>(
new XThread(kernel_state(), actual_stack_size, xapi_thread_startup, new XThread(kernel_state(), actual_stack_size,
start_address.guest_address(), start_context.guest_address(), xapi_thread_startup ? XThread::StartupType::XapiThreadStartup
creation_flags, true)); : XThread::StartupType::Normal,
xapi_thread_startup, start_address.guest_address(),
start_context.guest_address(), creation_flags, true));
X_STATUS result = thread->Create(); X_STATUS result = thread->Create();
if (XFAILED(result)) { if (XFAILED(result)) {

View File

@ -50,15 +50,16 @@ XThread::XThread(KernelState* kernel_state)
: XObject(kernel_state, kType), guest_thread_(true) {} : XObject(kernel_state, kType), guest_thread_(true) {}
XThread::XThread(KernelState* kernel_state, uint32_t stack_size, XThread::XThread(KernelState* kernel_state, uint32_t stack_size,
uint32_t xapi_thread_startup, uint32_t start_address, StartupType startup_type, uint32_t xapi_thread_startup,
uint32_t start_context, uint32_t creation_flags, uint32_t start_address, uint32_t start_context,
bool guest_thread, bool main_thread) uint32_t creation_flags, bool guest_thread, bool main_thread)
: XObject(kernel_state, kType), : XObject(kernel_state, kType),
thread_id_(++next_xthread_id_), thread_id_(++next_xthread_id_),
guest_thread_(guest_thread), guest_thread_(guest_thread),
main_thread_(main_thread), main_thread_(main_thread),
apc_list_(kernel_state->memory()) { apc_list_(kernel_state->memory()) {
creation_params_.stack_size = stack_size; creation_params_.stack_size = stack_size;
creation_params_.startup_type = startup_type;
creation_params_.xapi_thread_startup = xapi_thread_startup; creation_params_.xapi_thread_startup = xapi_thread_startup;
creation_params_.start_address = start_address; creation_params_.start_address = start_address;
creation_params_.start_context = start_context; 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. // If a XapiThreadStartup value is present, we use that as a trampoline.
// Otherwise, we are a raw thread. // 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, uint64_t args[] = {creation_params_.start_address,
creation_params_.start_context}; creation_params_.start_context};
kernel_state()->processor()->Execute(thread_state_, kernel_state()->processor()->Execute(thread_state_,
creation_params_.xapi_thread_startup, creation_params_.xapi_thread_startup,
args, xe::countof(args)); 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 { } else {
// Run user code. // Run user code.
uint64_t args[] = {creation_params_.start_context}; uint64_t args[] = {creation_params_.start_context};
@ -1036,7 +1047,8 @@ object_ref<XThread> XThread::Restore(KernelState* kernel_state,
XHostThread::XHostThread(KernelState* kernel_state, uint32_t stack_size, XHostThread::XHostThread(KernelState* kernel_state, uint32_t stack_size,
uint32_t creation_flags, std::function<int()> host_fn) uint32_t creation_flags, std::function<int()> 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) { host_fn_(host_fn) {
// By default host threads are not debugger suspendable. If the thread runs // By default host threads are not debugger suspendable. If the thread runs
// any guest code this must be overridden. // any guest code this must be overridden.

View File

@ -106,9 +106,11 @@ static_assert_size(X_KTHREAD, 0xAB0);
class XThread : public XObject, public cpu::Thread { class XThread : public XObject, public cpu::Thread {
public: public:
static const Type kType = kTypeThread; static const Type kType = kTypeThread;
enum class StartupType { Normal, XapiThreadStartup, DllMain };
struct CreationParams { struct CreationParams {
uint32_t stack_size; uint32_t stack_size;
StartupType startup_type;
uint32_t xapi_thread_startup; uint32_t xapi_thread_startup;
uint32_t start_address; uint32_t start_address;
uint32_t start_context; uint32_t start_context;
@ -117,9 +119,9 @@ class XThread : public XObject, public cpu::Thread {
XThread(KernelState* kernel_state); XThread(KernelState* kernel_state);
XThread(KernelState* kernel_state, uint32_t stack_size, XThread(KernelState* kernel_state, uint32_t stack_size,
uint32_t xapi_thread_startup, uint32_t start_address, StartupType startup_type, uint32_t xapi_thread_startup,
uint32_t start_context, uint32_t creation_flags, bool guest_thread, uint32_t start_address, uint32_t start_context,
bool main_thread = false); uint32_t creation_flags, bool guest_thread, bool main_thread = false);
~XThread() override; ~XThread() override;
static bool IsInThread(XThread* other); static bool IsInThread(XThread* other);