[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 "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<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);
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) {

View File

@ -233,35 +233,54 @@ object_ref<XThread> KernelState::LaunchModule(object_ref<UserModule> 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<XThread>(
new XThread(kernel_state(), module->stack_size(), 0,
module->entry_point(), 0, X_CREATE_SUSPENDED, true, true));
xe::kernel::object_ref<XThread> 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<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];
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.
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<UserModule> KernelState::LoadUserModule(const char* raw_name,
module = object_ref<UserModule>(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,6 +413,11 @@ object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name,
module->Dump();
if (module->is_dll_module() && module->entry_point() && call_entry) {
// 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[] = {
@ -402,6 +429,7 @@ object_ref<UserModule> KernelState::LoadUserModule(const char* raw_name,
processor()->Execute(thread_state, module->entry_point(), args,
xe::countof(args));
}
}
return module;
}

View File

@ -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<UserModule>(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;

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));
auto thread = object_ref<XThread>(
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)) {

View File

@ -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> XThread::Restore(KernelState* kernel_state,
XHostThread::XHostThread(KernelState* kernel_state, uint32_t stack_size,
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) {
// By default host threads are not debugger suspendable. If the thread runs
// 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 {
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);