diff --git a/src/xenia/base/string.cc b/src/xenia/base/string.cc index 068c1b6ac..acd00ce76 100644 --- a/src/xenia/base/string.cc +++ b/src/xenia/base/string.cc @@ -68,6 +68,20 @@ std::vector split_path(const std::string& path) { return parts; } +std::string join_paths(const std::string& left, const std::string& right, + char sep) { + if (!left.size()) { + return right; + } else if (!right.size()) { + return left; + } + if (left[left.size() - 1] == sep) { + return left + right; + } else { + return left + sep + right; + } +} + std::wstring join_paths(const std::wstring& left, const std::wstring& right, wchar_t sep) { if (!left.size()) { @@ -166,4 +180,20 @@ std::wstring find_name_from_path(const std::wstring& path) { return name; } +std::string find_base_path(const std::string& path) { + auto last_slash = path.find_last_of('\\'); + if (last_slash == std::string::npos) { + return path; + } else if (last_slash == path.length() - 1) { + auto prev_slash = path.find_last_of('\\', last_slash - 1); + if (prev_slash == std::string::npos) { + return ""; + } else { + return path.substr(0, prev_slash + 1); + } + } else { + return path.substr(0, last_slash + 1); + } +} + } // namespace xe diff --git a/src/xenia/base/string.h b/src/xenia/base/string.h index 9ed7a1603..79f159e35 100644 --- a/src/xenia/base/string.h +++ b/src/xenia/base/string.h @@ -32,6 +32,8 @@ std::wstring to_absolute_path(const std::wstring& path); std::vector split_path(const std::string& path); // Joins two path segments with the given separator. +std::string join_paths(const std::string& left, const std::string& right, + char sep = xe::path_separator); std::wstring join_paths(const std::wstring& left, const std::wstring& right, wchar_t sep = xe::path_separator); @@ -42,10 +44,13 @@ std::wstring fix_path_separators(const std::wstring& source, std::string fix_path_separators(const std::string& source, char new_sep = xe::path_separator); -// Find the top directory name or filename from a path +// Find the top directory name or filename from a path. std::string find_name_from_path(const std::string& path); std::wstring find_name_from_path(const std::wstring& path); +// Get parent path of the given directory or filename. +std::string find_base_path(const std::string& path); + } // namespace xe #endif // XENIA_BASE_STRING_H_ diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index ffb884a7f..6a7e0ef0a 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -325,15 +325,18 @@ bool Processor::Execute(ThreadState* thread_state, uint32_t address) { PPCContext* context = thread_state->context(); + // Setup registers. + uint64_t previous_lr = context->lr; // This could be set to anything to give us a unique identifier to track // re-entrancy/etc. - uint32_t lr = 0xBEBEBEBE; - - // Setup registers. - context->lr = lr; + context->lr = 0xBEBEBEBE; // Execute the function. - return fn->Call(thread_state, lr); + auto result = fn->Call(thread_state, uint32_t(context->lr)); + + context->lr = previous_lr; + + return result; } uint64_t Processor::Execute(ThreadState* thread_state, uint32_t address, diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 151f862e7..453eb2dcb 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -13,6 +13,7 @@ #include "xenia/base/assert.h" #include "xenia/base/string.h" +#include "xenia/cpu/processor.h" #include "xenia/emulator.h" #include "xenia/kernel/dispatcher.h" #include "xenia/kernel/xam_module.h" @@ -113,8 +114,11 @@ XModule* KernelState::GetModule(const char* name) { } else { std::lock_guard lock(object_mutex_); - for (XUserModule *module : user_modules_) { - if (module->name() == name) { + for (XUserModule* module : user_modules_) { + if ((strcasecmp(xe::find_name_from_path(module->path()).c_str(), name) == + 0) || + (strcasecmp(module->name().c_str(), name) == 0) || + (strcasecmp(module->path().c_str(), name) == 0)) { module->Retain(); return module; } @@ -147,27 +151,54 @@ void KernelState::SetExecutableModule(XUserModule* module) { } } -XUserModule* KernelState::LoadUserModule(const char *name) { - std::lock_guard lock(object_mutex_); +XUserModule* KernelState::LoadUserModule(const char* raw_name) { + // Some games try to load relative to launch module, others specify full path. + std::string name = xe::find_name_from_path(raw_name); + std::string path(raw_name); + if (name == raw_name) { + path = xe::join_paths(xe::find_base_path(executable_module_->path()), name); + } - // See if we've already loaded it - for (XUserModule *module : user_modules_) { - if (module->name() == name) { - module->Retain(); - return module; + XUserModule* module = nullptr; + { + std::lock_guard lock(object_mutex_); + + // See if we've already loaded it + for (XUserModule* existing_module : user_modules_) { + if (existing_module->path() == path) { + existing_module->Retain(); + return existing_module; + } } + + // Module wasn't loaded, so load it. + module = new XUserModule(this, path.c_str()); + X_STATUS status = module->LoadFromFile(path); + if (XFAILED(status)) { + module->Release(); + return nullptr; + } + + user_modules_.push_back(module); + module->Retain(); } - // Module wasn't loaded, so load it. - XUserModule *module = new XUserModule(this, name); - X_STATUS status = module->LoadFromFile(name); - if (XFAILED(status)) { - module->Release(); - return nullptr; + module->Dump(); + + auto xex_header = module->xex_header(); + if (xex_header->exe_entry_point) { + // 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, xex_header->exe_entry_point, args, + xe::countof(args)); } - user_modules_.push_back(module); - module->Retain(); return module; } @@ -184,6 +215,52 @@ void KernelState::UnregisterThread(XThread* thread) { } } +void KernelState::OnThreadExecute(XThread* thread) { + std::lock_guard lock(object_mutex_); + + // Must be called on executing thread. + assert_true(XThread::GetCurrentThread() == thread); + + // Call DllMain(DLL_THREAD_ATTACH) for each user module: + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx + auto thread_state = thread->thread_state(); + for (auto user_module : user_modules_) { + auto xex_header = user_module->xex_header(); + if (xex_header->exe_entry_point) { + uint64_t args[] = { + user_module->handle(), + 2, // DLL_THREAD_ATTACH + 0, // 0 because always dynamic + }; + processor()->Execute(thread_state, xex_header->exe_entry_point, args, + xe::countof(args)); + } + } +} + +void KernelState::OnThreadExit(XThread* thread) { + std::lock_guard lock(object_mutex_); + + // Must be called on executing thread. + assert_true(XThread::GetCurrentThread() == thread); + + // Call DllMain(DLL_THREAD_DETACH) for each user module: + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx + auto thread_state = thread->thread_state(); + for (auto user_module : user_modules_) { + auto xex_header = user_module->xex_header(); + if (xex_header->exe_entry_point) { + uint64_t args[] = { + user_module->handle(), + 3, // DLL_THREAD_DETACH + 0, // 0 because always dynamic + }; + processor()->Execute(thread_state, xex_header->exe_entry_point, args, + xe::countof(args)); + } + } +} + XThread* KernelState::GetThreadByID(uint32_t thread_id) { std::lock_guard lock(object_mutex_); XThread* thread = nullptr; diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index b2a413607..18bcdc3e5 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -73,6 +73,8 @@ class KernelState { void RegisterThread(XThread* thread); void UnregisterThread(XThread* thread); + void OnThreadExecute(XThread* thread); + void OnThreadExit(XThread* thread); XThread* GetThreadByID(uint32_t thread_id); void RegisterNotifyListener(XNotifyListener* listener); diff --git a/src/xenia/kernel/objects/xthread.cc b/src/xenia/kernel/objects/xthread.cc index 1de9bb376..7d293f426 100644 --- a/src/xenia/kernel/objects/xthread.cc +++ b/src/xenia/kernel/objects/xthread.cc @@ -109,10 +109,6 @@ uint32_t XThread::GetCurrentThreadId(const uint8_t* thread_state_block) { return xe::load_and_swap(thread_state_block + 0x14C); } -uint32_t XThread::thread_state() { return thread_state_address_; } - -uint32_t XThread::thread_id() { return thread_id_; } - uint32_t XThread::last_error() { uint8_t* p = memory()->TranslateVirtual(thread_state_address_); return xe::load_and_swap(p + 0x160); @@ -326,6 +322,9 @@ void XThread::Execute() { thread_id_, handle(), name_.c_str(), xe::threading::current_thread_id()); + // Let the kernel know we are starting. + kernel_state()->OnThreadExecute(this); + // All threads get a mandatory sleep. This is to deal with some buggy // games that are assuming the 360 is so slow to create threads that they // have time to initialize shared structures AFTER CreateThread (RR). @@ -349,6 +348,9 @@ void XThread::Execute() { // Treat the return code as an implicit exit code. Exit(exit_code); } + + // Let the kernel know we are exiting. + kernel_state()->OnThreadExit(this); } void XThread::EnterCriticalRegion() { diff --git a/src/xenia/kernel/objects/xthread.h b/src/xenia/kernel/objects/xthread.h index 90aaa8a5c..d210d7edd 100644 --- a/src/xenia/kernel/objects/xthread.h +++ b/src/xenia/kernel/objects/xthread.h @@ -35,8 +35,9 @@ class XThread : public XObject { static uint32_t GetCurrentThreadHandle(); static uint32_t GetCurrentThreadId(const uint8_t* thread_state_block); - uint32_t thread_state(); - uint32_t thread_id(); + uint32_t thread_state_ptr() const { return thread_state_address_; } + cpu::ThreadState* thread_state() const { return thread_state_; } + uint32_t thread_id() const { return thread_id_; } uint32_t last_error(); void set_last_error(uint32_t error_code); const std::string& name() const { return name_; } diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 5ea4d268b..265898daa 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -34,7 +34,7 @@ const xe_xex2_header_t* XUserModule::xex_header() { return xe_xex2_get_header(xex_); } -X_STATUS XUserModule::LoadFromFile(const char* path) { +X_STATUS XUserModule::LoadFromFile(std::string path) { X_STATUS result = X_STATUS_UNSUCCESSFUL; XFile* file = NULL; @@ -42,7 +42,7 @@ X_STATUS XUserModule::LoadFromFile(const char* path) { // TODO(benvanik): make this code shared? auto fs_entry = kernel_state()->file_system()->ResolvePath(path); if (!fs_entry) { - XELOGE("File not found: %s", path); + XELOGE("File not found: %s", path.c_str()); return X_STATUS_NO_SUCH_FILE; } diff --git a/src/xenia/kernel/objects/xuser_module.h b/src/xenia/kernel/objects/xuser_module.h index e97165e49..3ea84b315 100644 --- a/src/xenia/kernel/objects/xuser_module.h +++ b/src/xenia/kernel/objects/xuser_module.h @@ -10,9 +10,10 @@ #ifndef XENIA_KERNEL_XBOXKRNL_XUSER_MODULE_H_ #define XENIA_KERNEL_XBOXKRNL_XUSER_MODULE_H_ -#include "xenia/kernel/objects/xmodule.h" +#include #include "xenia/cpu/export_resolver.h" +#include "xenia/kernel/objects/xmodule.h" #include "xenia/kernel/util/xex2.h" #include "xenia/xbox.h" @@ -29,7 +30,7 @@ class XUserModule : public XModule { uint32_t execution_info_ptr() const { return execution_info_ptr_; } - X_STATUS LoadFromFile(const char* path); + X_STATUS LoadFromFile(std::string path); X_STATUS LoadFromMemory(const void* addr, const size_t length); uint32_t GetProcAddressByOrdinal(uint16_t ordinal) override; diff --git a/src/xenia/kernel/xboxkrnl_modules.cc b/src/xenia/kernel/xboxkrnl_modules.cc index 3afae32ca..f105a8436 100644 --- a/src/xenia/kernel/xboxkrnl_modules.cc +++ b/src/xenia/kernel/xboxkrnl_modules.cc @@ -8,6 +8,7 @@ */ #include "xenia/base/logging.h" +#include "xenia/cpu/processor.h" #include "xenia/kernel/kernel_state.h" #include "xenia/kernel/objects/xuser_module.h" #include "xenia/kernel/util/shim_utils.h" @@ -15,8 +16,6 @@ #include "xenia/kernel/xboxkrnl_private.h" #include "xenia/xbox.h" -#include "xenia/cpu/processor.h" - namespace xe { namespace kernel { @@ -218,51 +217,33 @@ SHIM_CALL XexLoadImage_shim(PPCContext* ppc_state, KernelState* state) { X_STATUS result = X_STATUS_NO_SUCH_FILE; + X_HANDLE module_handle = X_INVALID_HANDLE_VALUE; XModule* module = state->GetModule(module_name); if (module) { - module->RetainHandle(); - SHIM_SET_MEM_32(handle_ptr, module->handle()); - module->Release(); - - result = X_STATUS_SUCCESS; + // Existing module found, just add a reference and obtain a handle. + result = state->object_table()->AddHandle(module, &module_handle); } else { - XUserModule* usermod = state->LoadUserModule(module_name); - if (usermod) { - // If the module has an entry point function, we have to call it. - const xe_xex2_header_t* header = usermod->xex_header(); - if (header->exe_entry_point) { - // Return address - uint32_t lr = ppc_state->thread_state->context()->lr; - - // TODO: What are these args for? - // param 2: val 1 seems to make CRT initialize - uint64_t args[] = { 0, 1, 0 }; - state->processor()->Execute(ppc_state->thread_state, - header->exe_entry_point, - args, xe::countof(args)); - - ppc_state->thread_state->context()->lr = lr; - } - + // Not found; attempt to load as a user module. + module = state->LoadUserModule(module_name); + if (module) { + module->RetainHandle(); + module_handle = module->handle(); result = X_STATUS_SUCCESS; - - usermod->RetainHandle(); - SHIM_SET_MEM_32(handle_ptr, usermod->handle()); - usermod->Release(); } } + SHIM_SET_MEM_32(handle_ptr, module_handle); SHIM_SET_RETURN_32(result); } SHIM_CALL XexUnloadImage_shim(PPCContext* ppc_state, KernelState* state) { - uint32_t handle = SHIM_GET_ARG_32(0); + uint32_t module_handle = SHIM_GET_ARG_32(0); - XELOGD("XexUnloadImage(%.8X)", handle); + XELOGD("XexUnloadImage(%.8X)", module_handle); X_STATUS result = X_STATUS_INVALID_HANDLE; - result = state->object_table()->RemoveHandle(handle); + result = state->object_table()->RemoveHandle(module_handle); SHIM_SET_RETURN_32(result); } diff --git a/src/xenia/kernel/xboxkrnl_ob.cc b/src/xenia/kernel/xboxkrnl_ob.cc index 538652ff0..9aaaf1e09 100644 --- a/src/xenia/kernel/xboxkrnl_ob.cc +++ b/src/xenia/kernel/xboxkrnl_ob.cc @@ -69,7 +69,7 @@ SHIM_CALL ObReferenceObjectByHandle_shim(PPCContext* ppc_state, case 0xD01BBEEF: // ExThreadObjectType { XThread* thread = (XThread*)object; - native_ptr = thread->thread_state(); + native_ptr = thread->thread_state_ptr(); } break; default: assert_unhandled_case(object_type_ptr);