Calling DllMain, fixing ref count, and fixing module search.

This commit is contained in:
Ben Vanik 2015-05-09 00:56:42 -07:00
parent 0c646f4bc2
commit 23eb343484
11 changed files with 168 additions and 66 deletions

View File

@ -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

View File

@ -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_

View File

@ -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,

View File

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

View File

@ -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);

View File

@ -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() {

View File

@ -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_; }

View File

@ -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;
}

View 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;

View File

@ -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);
}

View File

@ -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);