Calling DllMain, fixing ref count, and fixing module search.
This commit is contained in:
parent
0c646f4bc2
commit
23eb343484
|
@ -68,6 +68,20 @@ std::vector<std::string> 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
|
||||
|
|
|
@ -32,6 +32,8 @@ std::wstring to_absolute_path(const std::wstring& path);
|
|||
std::vector<std::string> 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_
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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<std::mutex> lock(object_mutex_);
|
||||
XThread* thread = nullptr;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -109,10 +109,6 @@ uint32_t XThread::GetCurrentThreadId(const uint8_t* thread_state_block) {
|
|||
return xe::load_and_swap<uint32_t>(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<uint32_t>(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() {
|
||||
|
|
|
@ -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_; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 <string>
|
||||
|
||||
#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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue