diff --git a/src/xenia/cpu/xex_module.cc b/src/xenia/cpu/xex_module.cc index f3fbcb3e3..40400e6a3 100644 --- a/src/xenia/cpu/xex_module.cc +++ b/src/xenia/cpu/xex_module.cc @@ -18,21 +18,25 @@ #include "xenia/cpu/cpu-private.h" #include "xenia/cpu/export_resolver.h" #include "xenia/cpu/processor.h" +#include "xenia/kernel/kernel_state.h" +#include "xenia/kernel/objects/xmodule.h" namespace xe { namespace cpu { using namespace xe::cpu; +using namespace xe::kernel; using PPCContext = xe::cpu::frontend::PPCContext; void UndefinedImport(PPCContext* ppc_state, void* arg0, void* arg1) { - XELOGE("call to undefined kernel import"); + XELOGE("call to undefined import"); } -XexModule::XexModule(Processor* processor) +XexModule::XexModule(Processor* processor, KernelState* state) : Module(processor), processor_(processor), + kernel_state_(state), xex_(nullptr), base_address_(0), low_address_(0), @@ -104,8 +108,23 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) { for (size_t n = 0; n < import_info_count; n++) { const xe_xex2_import_info_t* info = &import_infos[n]; - KernelExport* kernel_export = - export_resolver->GetExportByOrdinal(library->name, info->ordinal); + // Strip off the extension (for the symbol name) + std::string libname = library->name; + auto dot = libname.find_last_of('.'); + if (dot != libname.npos) { + libname = libname.substr(0, dot); + } + + KernelExport* kernel_export = NULL; // kernel export info + uint32_t user_export_addr = 0; // user export address + + if (kernel_state_->IsKernelModule(library->name)) { + kernel_export = + export_resolver->GetExportByOrdinal(library->name, info->ordinal); + } else { + XModule* module = kernel_state_->GetModule(library->name); + user_export_addr = module->GetProcAddressByOrdinal(info->ordinal); + } if (kernel_export) { if (info->thunk_address) { @@ -114,7 +133,7 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) { snprintf(name, xe::countof(name), "%s", kernel_export->name); } } else { - snprintf(name, xe::countof(name), "__imp_%s_%.3X", library->name, + snprintf(name, xe::countof(name), "__imp_%s_%.3X", libname, info->ordinal); } @@ -151,53 +170,77 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) { kernel_export->name); } } + } else { + auto slot = memory_->TranslateVirtual(info->value_address); + + // Assuming this is correct... + xe::store_and_swap(slot, user_export_addr); } if (info->thunk_address) { if (kernel_export) { snprintf(name, xe::countof(name), "%s", kernel_export->name); + } else if (user_export_addr) { + snprintf(name, xe::countof(name), "__%s_%.3X", libname, + info->ordinal); } else { - snprintf(name, xe::countof(name), "__kernel_%s_%.3X", library->name, + snprintf(name, xe::countof(name), "__kernel_%s_%.3X", libname, info->ordinal); } - // On load we have something like this in memory: - // li r3, 0 - // li r4, 0x1F5 - // mtspr CTR, r11 - // bctr - // Real consoles rewrite this with some code that sets r11. - // If we did that we'd still have to put a thunk somewhere and do the - // dynamic lookup. Instead, we rewrite it to use syscalls, as they - // aren't used on the 360. CPU backends can either take the syscall - // or do something smarter. - // sc - // blr - // nop - // nop - uint8_t* p = memory()->TranslateVirtual(info->thunk_address); - xe::store_and_swap(p + 0x0, 0x44000002); - xe::store_and_swap(p + 0x4, 0x4E800020); - xe::store_and_swap(p + 0x8, 0x60000000); - xe::store_and_swap(p + 0xC, 0x60000000); - - FunctionInfo::ExternHandler handler = 0; - void* handler_data = 0; + // Kernel exports go up to the host shim functions if (kernel_export) { - handler = - (FunctionInfo::ExternHandler)kernel_export->function_data.shim; - handler_data = kernel_export->function_data.shim_data; - } else { - handler = (FunctionInfo::ExternHandler)UndefinedImport; - handler_data = this; - } + // On load we have something like this in memory: + // li r3, 0 + // li r4, 0x1F5 + // mtspr CTR, r11 + // bctr + // Real consoles rewrite this with some code that sets r11. + // If we did that we'd still have to put a thunk somewhere and do the + // dynamic lookup. Instead, we rewrite it to use syscalls, as they + // aren't used on the 360. CPU backends can either take the syscall + // or do something smarter. + // sc + // blr + // nop + // nop + uint8_t* p = memory()->TranslateVirtual(info->thunk_address); + xe::store_and_swap(p + 0x0, 0x44000002); + xe::store_and_swap(p + 0x4, 0x4E800020); + xe::store_and_swap(p + 0x8, 0x60000000); + xe::store_and_swap(p + 0xC, 0x60000000); - FunctionInfo* fn_info; - DeclareFunction(info->thunk_address, &fn_info); - fn_info->set_end_address(info->thunk_address + 16 - 4); - fn_info->set_name(name); - fn_info->SetupExtern(handler, handler_data, NULL); - fn_info->set_status(SymbolInfo::STATUS_DECLARED); + FunctionInfo::ExternHandler handler = 0; + void* handler_data = 0; + if (kernel_export) { + handler = + (FunctionInfo::ExternHandler)kernel_export->function_data.shim; + handler_data = kernel_export->function_data.shim_data; + } else { + handler = (FunctionInfo::ExternHandler)UndefinedImport; + handler_data = this; + } + + FunctionInfo* fn_info; + DeclareFunction(info->thunk_address, &fn_info); + fn_info->set_end_address(info->thunk_address + 16 - 4); + fn_info->set_name(name); + fn_info->SetupExtern(handler, handler_data, NULL); + fn_info->set_status(SymbolInfo::STATUS_DECLARED); + } else if (user_export_addr) { + // Rewrite PPC code to set r11 to the target address + // So we'll have: + // lis r11, user_export_addr + // ori r11, r11, user_export_addr + // mtspr CTR, r11 + // bctr + uint16_t hi_addr = (user_export_addr >> 16) & 0xFFFF; + uint16_t low_addr = user_export_addr & 0xFFFF; + + uint8_t* p = memory()->TranslateVirtual(info->thunk_address); + xe::store_and_swap(p + 0x0, 0x3D600000 | hi_addr); + xe::store_and_swap(p + 0x4, 0x616B0000 | low_addr); + } } } diff --git a/src/xenia/cpu/xex_module.h b/src/xenia/cpu/xex_module.h index 0856ad59b..6806b912c 100644 --- a/src/xenia/cpu/xex_module.h +++ b/src/xenia/cpu/xex_module.h @@ -16,13 +16,17 @@ #include "xenia/kernel/util/xex2.h" namespace xe { + +// KernelState forward decl. +namespace kernel { class KernelState; } + namespace cpu { class Runtime; class XexModule : public xe::cpu::Module { public: - XexModule(Processor* processor); + XexModule(Processor* processor, kernel::KernelState* state); virtual ~XexModule(); xe_xex2_ref xex() const { return xex_; } @@ -40,6 +44,7 @@ class XexModule : public xe::cpu::Module { private: Processor* processor_; + kernel::KernelState* kernel_state_; std::string name_; std::string path_; xe_xex2_ref xex_; diff --git a/src/xenia/kernel/kernel_state.cc b/src/xenia/kernel/kernel_state.cc index 4a6676cbf..4d64d7f2e 100644 --- a/src/xenia/kernel/kernel_state.cc +++ b/src/xenia/kernel/kernel_state.cc @@ -97,6 +97,19 @@ void KernelState::RegisterModule(XModule* module) {} void KernelState::UnregisterModule(XModule* module) {} +bool KernelState::IsKernelModule(const char* name) { + if (!name) { + // executing module isn't a kernel module + return false; + } else if (strcasecmp(name, "xam.xex") == 0) { + return true; + } else if (strcasecmp(name, "xboxkrnl.exe") == 0) { + return true; + } + + return false; +} + XModule* KernelState::GetModule(const char* name) { if (!name) { // NULL name = self. @@ -114,7 +127,7 @@ XModule* KernelState::GetModule(const char* name) { // Some games request this, for some reason. wtf. return nullptr; } else { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); for (XUserModule* module : user_modules_) { if ((strcasecmp(xe::find_name_from_path(module->path()).c_str(), name) == @@ -163,7 +176,7 @@ XUserModule* KernelState::LoadUserModule(const char* raw_name) { XUserModule* module = nullptr; { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); // See if we've already loaded it for (XUserModule* existing_module : user_modules_) { @@ -205,12 +218,12 @@ XUserModule* KernelState::LoadUserModule(const char* raw_name) { } void KernelState::RegisterThread(XThread* thread) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); threads_by_id_[thread->thread_id()] = thread; } void KernelState::UnregisterThread(XThread* thread) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); auto it = threads_by_id_.find(thread->thread_id()); if (it != threads_by_id_.end()) { threads_by_id_.erase(it); @@ -218,7 +231,7 @@ void KernelState::UnregisterThread(XThread* thread) { } void KernelState::OnThreadExecute(XThread* thread) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); // Must be called on executing thread. assert_true(XThread::GetCurrentThread() == thread); @@ -241,7 +254,7 @@ void KernelState::OnThreadExecute(XThread* thread) { } void KernelState::OnThreadExit(XThread* thread) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); // Must be called on executing thread. assert_true(XThread::GetCurrentThread() == thread); @@ -264,7 +277,7 @@ void KernelState::OnThreadExit(XThread* thread) { } XThread* KernelState::GetThreadByID(uint32_t thread_id) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); XThread* thread = nullptr; auto it = threads_by_id_.find(thread_id); if (it != threads_by_id_.end()) { @@ -276,7 +289,7 @@ XThread* KernelState::GetThreadByID(uint32_t thread_id) { } void KernelState::RegisterNotifyListener(XNotifyListener* listener) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); notify_listeners_.push_back(listener); // Games seem to expect a few notifications on startup, only for the first @@ -300,7 +313,7 @@ void KernelState::RegisterNotifyListener(XNotifyListener* listener) { } void KernelState::UnregisterNotifyListener(XNotifyListener* listener) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); for (auto it = notify_listeners_.begin(); it != notify_listeners_.end(); ++it) { if (*it == listener) { @@ -311,7 +324,7 @@ void KernelState::UnregisterNotifyListener(XNotifyListener* listener) { } void KernelState::BroadcastNotification(XNotificationID id, uint32_t data) { - std::lock_guard lock(object_mutex_); + std::lock_guard lock(object_mutex_); for (auto it = notify_listeners_.begin(); it != notify_listeners_.end(); ++it) { (*it)->EnqueueNotification(id, data); diff --git a/src/xenia/kernel/kernel_state.h b/src/xenia/kernel/kernel_state.h index 5e705eb08..f70f123e6 100644 --- a/src/xenia/kernel/kernel_state.h +++ b/src/xenia/kernel/kernel_state.h @@ -63,13 +63,14 @@ class KernelState { ContentManager* content_manager() const { return content_manager_.get(); } ObjectTable* object_table() const { return object_table_; } - std::mutex& object_mutex() { return object_mutex_; } + std::recursive_mutex& object_mutex() { return object_mutex_; } uint32_t process_type() const { return process_type_; } void set_process_type(uint32_t value) { process_type_ = value; } void RegisterModule(XModule* module); void UnregisterModule(XModule* module); + bool IsKernelModule(const char* name); XModule* GetModule(const char* name); XUserModule* GetExecutableModule(); void SetExecutableModule(XUserModule* module); @@ -105,7 +106,7 @@ class KernelState { std::unique_ptr content_manager_; ObjectTable* object_table_; - std::mutex object_mutex_; + std::recursive_mutex object_mutex_; std::unordered_map threads_by_id_; std::vector notify_listeners_; bool has_notified_startup_; diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 3d83a1b48..230627477 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -129,7 +129,7 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { // Prepare the module for execution. // Runtime takes ownership. - auto xex_module = std::make_unique(processor); + auto xex_module = std::make_unique(processor, kernel_state()); if (!xex_module->Load(name_, path_, xex_)) { return X_STATUS_UNSUCCESSFUL; } @@ -351,18 +351,38 @@ void XUserModule::Dump() { int unimpl_count = 0; for (size_t m = 0; m < import_info_count; m++) { const xe_xex2_import_info_t* info = &import_infos[m]; - KernelExport* kernel_export = + + if (kernel_state_->IsKernelModule(library->name)) { + KernelExport* kernel_export = export_resolver->GetExportByOrdinal(library->name, info->ordinal); - if (kernel_export) { - known_count++; - if (kernel_export->is_implemented) { - impl_count++; + if (kernel_export) { + known_count++; + if (kernel_export->is_implemented) { + impl_count++; + } else { + unimpl_count++; + } } else { + unknown_count++; unimpl_count++; } } else { - unknown_count++; - unimpl_count++; + // User module + XModule* module = kernel_state_->GetModule(library->name); + if (module) { + uint32_t export_addr = + module->GetProcAddressByOrdinal(info->ordinal); + if (export_addr) { + impl_count++; + known_count++; + } else { + unimpl_count++; + unknown_count++; + } + } else { + unimpl_count++; + unknown_count++; + } } } printf(" Total: %4u\n", uint32_t(import_info_count)); @@ -377,13 +397,23 @@ void XUserModule::Dump() { // Listing. for (size_t m = 0; m < import_info_count; m++) { const xe_xex2_import_info_t* info = &import_infos[m]; - KernelExport* kernel_export = - export_resolver->GetExportByOrdinal(library->name, info->ordinal); const char* name = "UNKNOWN"; bool implemented = false; - if (kernel_export) { - name = kernel_export->name; - implemented = kernel_export->is_implemented; + + KernelExport* kernel_export; + if (kernel_state_->IsKernelModule(library->name)) { + kernel_export = + export_resolver->GetExportByOrdinal(library->name, info->ordinal); + if (kernel_export) { + name = kernel_export->name; + implemented = kernel_export->is_implemented; + } + } else { + XModule* module = kernel_state_->GetModule(library->name); + if (module && module->GetProcAddressByOrdinal(info->ordinal)) { + // TODO: Name lookup + implemented = true; + } } if (kernel_export && kernel_export->type == KernelExport::Variable) { printf(" V %.8X %.3X (%3d) %s %s\n", info->value_address, diff --git a/src/xenia/kernel/util/xex2.cc b/src/xenia/kernel/util/xex2.cc index 7a38b57e8..da6409b20 100644 --- a/src/xenia/kernel/util/xex2.cc +++ b/src/xenia/kernel/util/xex2.cc @@ -610,8 +610,8 @@ int xe_xex2_read_image_basic_compressed(const xe_xex2_header_t *header, return 1; } uint8_t *buffer = memory->TranslateVirtual(header->exe_address); + std::memset(buffer, 0, total_size); // Quickly zero the contents. uint8_t *d = buffer; - std::memset(buffer, 0, uncompressed_size); uint32_t rk[4 * (MAXNR + 1)]; uint8_t ivec[16] = {0}; diff --git a/src/xenia/kernel/xobject.cc b/src/xenia/kernel/xobject.cc index 79c155a84..7dc1ffb8a 100644 --- a/src/xenia/kernel/xobject.cc +++ b/src/xenia/kernel/xobject.cc @@ -153,7 +153,7 @@ X_STATUS XObject::WaitMultiple(uint32_t count, XObject** objects, } void XObject::SetNativePointer(uint32_t native_ptr, bool uninitialized) { - std::lock_guard lock(kernel_state_->object_mutex()); + std::lock_guard lock(kernel_state_->object_mutex()); auto header = kernel_state_->memory()->TranslateVirtual(native_ptr); @@ -181,7 +181,7 @@ XObject* XObject::GetObject(KernelState* kernel_state, void* native_ptr, // We identify this by checking the low bit of wait_list_blink - if it's 1, // we have already put our pointer in there. - std::lock_guard lock(kernel_state->object_mutex()); + std::lock_guard lock(kernel_state->object_mutex()); auto header = reinterpret_cast(native_ptr);