From 90e489527ab7cf0e5ddc9cf23e946fd3615a0ba5 Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Wed, 11 Feb 2015 09:42:12 -0800 Subject: [PATCH] Enough of ObOpenObjectByName to handle basic usage + asserts for others. Fixes #143. --- src/xenia/kernel/object_table.cc | 78 ++++++++++++++++++++------ src/xenia/kernel/object_table.h | 10 +++- src/xenia/kernel/xboxkrnl_ob.cc | 30 ++++++++++ src/xenia/kernel/xboxkrnl_threading.cc | 47 ++++++++++++++-- src/xenia/kernel/xobject.cc | 16 ++++++ src/xenia/kernel/xobject.h | 5 ++ src/xenia/xbox.h | 17 +++++- 7 files changed, 175 insertions(+), 28 deletions(-) diff --git a/src/xenia/kernel/object_table.cc b/src/xenia/kernel/object_table.cc index 203c3deb7..5b3418b59 100644 --- a/src/xenia/kernel/object_table.cc +++ b/src/xenia/kernel/object_table.cc @@ -151,7 +151,8 @@ X_STATUS ObjectTable::RemoveHandle(X_HANDLE handle) { return result; } -X_STATUS ObjectTable::GetObject(X_HANDLE handle, XObject** out_object) { +X_STATUS ObjectTable::GetObject(X_HANDLE handle, XObject** out_object, + bool already_locked) { assert_not_null(out_object); X_STATUS result = X_STATUS_SUCCESS; @@ -162,28 +163,32 @@ X_STATUS ObjectTable::GetObject(X_HANDLE handle, XObject** out_object) { } XObject* object = NULL; - { - std::lock_guard lock(table_mutex_); + if (!already_locked) { + table_mutex_.lock(); + } - // Lower 2 bits are ignored. - uint32_t slot = handle >> 2; + // Lower 2 bits are ignored. + uint32_t slot = handle >> 2; - // Verify slot. - if (slot > table_capacity_) { - result = X_STATUS_INVALID_HANDLE; + // Verify slot. + if (slot > table_capacity_) { + result = X_STATUS_INVALID_HANDLE; + } else { + ObjectTableEntry& entry = table_[slot]; + if (entry.object) { + object = entry.object; } else { - ObjectTableEntry& entry = table_[slot]; - if (entry.object) { - object = entry.object; - } else { - result = X_STATUS_INVALID_HANDLE; - } + result = X_STATUS_INVALID_HANDLE; } + } - // Retain the object pointer. - if (object) { - object->Retain(); - } + // Retain the object pointer. + if (object) { + object->Retain(); + } + + if (!already_locked) { + table_mutex_.unlock(); } *out_object = object; @@ -203,5 +208,42 @@ X_HANDLE ObjectTable::TranslateHandle(X_HANDLE handle) { } } +X_STATUS ObjectTable::AddNameMapping(const std::string& name, X_HANDLE handle) { + std::lock_guard lock(table_mutex_); + if (name_table_.count(name)) { + return X_STATUS_OBJECT_NAME_COLLISION; + } + name_table_.insert({ name, handle }); + return X_STATUS_SUCCESS; +} + +void ObjectTable::RemoveNameMapping(const std::string& name) { + std::lock_guard lock(table_mutex_); + auto it = name_table_.find(name); + if (it != name_table_.end()) { + name_table_.erase(it); + } +} + +X_STATUS ObjectTable::GetObjectByName(const std::string& name, + X_HANDLE* out_handle) { + std::lock_guard lock(table_mutex_); + auto it = name_table_.find(name); + if (it == name_table_.end()) { + *out_handle = X_INVALID_HANDLE_VALUE; + return X_STATUS_OBJECT_NAME_NOT_FOUND; + } + *out_handle = it->second; + + // We need to ref the handle. I think. + XObject* obj = nullptr; + if (XSUCCEEDED(GetObject(it->second, &obj, true))) { + obj->RetainHandle(); + obj->Release(); + } + + return X_STATUS_SUCCESS; +} + } // namespace kernel } // namespace xe diff --git a/src/xenia/kernel/object_table.h b/src/xenia/kernel/object_table.h index 75da74c86..7637b3ad5 100644 --- a/src/xenia/kernel/object_table.h +++ b/src/xenia/kernel/object_table.h @@ -11,6 +11,8 @@ #define XENIA_KERNEL_XBOXKRNL_OBJECT_TABLE_H_ #include +#include +#include #include "xenia/common.h" #include "xenia/xbox.h" @@ -27,7 +29,12 @@ class ObjectTable { X_STATUS AddHandle(XObject* object, X_HANDLE* out_handle); X_STATUS RemoveHandle(X_HANDLE handle); - X_STATUS GetObject(X_HANDLE handle, XObject** out_object); + X_STATUS GetObject(X_HANDLE handle, XObject** out_object, + bool already_locked = false); + + X_STATUS AddNameMapping(const std::string& name, X_HANDLE handle); + void RemoveNameMapping(const std::string& name); + X_STATUS GetObjectByName(const std::string& name, X_HANDLE* out_handle); private: X_HANDLE TranslateHandle(X_HANDLE handle); @@ -39,6 +46,7 @@ class ObjectTable { uint32_t table_capacity_; ObjectTableEntry* table_; uint32_t last_free_entry_; + std::unordered_map name_table_; }; } // namespace kernel diff --git a/src/xenia/kernel/xboxkrnl_ob.cc b/src/xenia/kernel/xboxkrnl_ob.cc index 23b717165..65cfa2239 100644 --- a/src/xenia/kernel/xboxkrnl_ob.cc +++ b/src/xenia/kernel/xboxkrnl_ob.cc @@ -18,6 +18,35 @@ namespace xe { namespace kernel { +SHIM_CALL ObOpenObjectByName_shim(PPCContext* ppc_state, KernelState* state) { + // r3 = ptr to info? + // +0 = -4 + // +4 = name ptr + // +8 = 0 + // r4 = ExEventObjectType | ExSemaphoreObjectType | ExTimerObjectType + // r5 = 0 + // r6 = out_ptr (handle?) + uint32_t obj_attributes_ptr = SHIM_GET_ARG_32(0); + uint32_t object_type_ptr = SHIM_GET_ARG_32(1); + uint32_t unk = SHIM_GET_ARG_32(2); + uint32_t handle_ptr = SHIM_GET_ARG_32(3); + + uint32_t name_str_ptr = SHIM_MEM_32(obj_attributes_ptr + 4); + X_ANSI_STRING name_str(SHIM_MEM_BASE, name_str_ptr); + auto name = name_str.to_string(); + + XELOGD("ObOpenObjectByName(%.8X(name=%s), %.8X, %.8X, %.8X)", + obj_attributes_ptr, name.c_str(), object_type_ptr, unk, handle_ptr); + + X_HANDLE handle = X_INVALID_HANDLE_VALUE; + X_STATUS result = state->object_table()->GetObjectByName(name, &handle); + if (XSUCCEEDED(result)) { + SHIM_SET_MEM_32(handle_ptr, handle); + } + + SHIM_SET_RETURN_32(result); +} + SHIM_CALL ObReferenceObjectByHandle_shim(PPCContext* ppc_state, KernelState* state) { uint32_t handle = SHIM_GET_ARG_32(0); @@ -130,6 +159,7 @@ SHIM_CALL NtClose_shim(PPCContext* ppc_state, KernelState* state) { void xe::kernel::xboxkrnl::RegisterObExports(ExportResolver* export_resolver, KernelState* state) { + SHIM_SET_MAPPING("xboxkrnl.exe", ObOpenObjectByName, state); SHIM_SET_MAPPING("xboxkrnl.exe", ObReferenceObjectByHandle, state); SHIM_SET_MAPPING("xboxkrnl.exe", ObDereferenceObject, state); SHIM_SET_MAPPING("xboxkrnl.exe", NtDuplicateObject, state); diff --git a/src/xenia/kernel/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl_threading.cc index a7babec83..1110e9541 100644 --- a/src/xenia/kernel/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl_threading.cc @@ -57,6 +57,26 @@ namespace kernel { // stw r3, 0x160(r11) // } +void AssertNoNameCollision(KernelState* state, uint32_t obj_attributes_ptr) { + // If the name exists and its type matches, we can return that (ref+1) + // with a success of NAME_EXISTS. + // If the name exists and its type doesn't match, we do NAME_COLLISION. + // Otherwise, we add like normal. + auto membase = state->memory()->membase(); + uint32_t name_str_ptr = + poly::load_and_swap(membase + obj_attributes_ptr + 4); + if (name_str_ptr) { + X_ANSI_STRING name_str(membase, name_str_ptr); + auto name = name_str.to_string(); + X_HANDLE handle = X_INVALID_HANDLE_VALUE; + X_RESULT result = state->object_table()->GetObjectByName(name, &handle); + if (XSUCCEEDED(result)) { + // Found something! + assert_always("Existing names not implemented"); + } + } +} + SHIM_CALL ExCreateThread_shim(PPCContext* ppc_state, KernelState* state) { uint32_t handle_ptr = SHIM_GET_ARG_32(0); uint32_t stack_size = SHIM_GET_ARG_32(1); @@ -425,13 +445,16 @@ SHIM_CALL NtCreateEvent_shim(PPCContext* ppc_state, KernelState* state) { XELOGD("NtCreateEvent(%.8X, %.8X, %d, %d)", handle_ptr, obj_attributes_ptr, event_type, initial_state); + // TODO(benvanik): check for name collision. May return existing object if + // type matches. + AssertNoNameCollision(state, obj_attributes_ptr); + XEvent* ev = new XEvent(state); ev->Initialize(!event_type, !!initial_state); // obj_attributes may have a name inside of it, if != NULL. - auto obj_attributes = SHIM_MEM_ADDR(obj_attributes_ptr); - if (obj_attributes) { - // ev->SetName(...); + if (obj_attributes_ptr) { + ev->SetAttributes(SHIM_MEM_ADDR(obj_attributes_ptr)); } if (handle_ptr) { @@ -566,12 +589,16 @@ SHIM_CALL NtCreateSemaphore_shim(PPCContext* ppc_state, KernelState* state) { XELOGD("NtCreateSemaphore(%.8X, %.8X, %d, %d)", handle_ptr, obj_attributes_ptr, count, limit); + // TODO(benvanik): check for name collision. May return existing object if + // type matches. + AssertNoNameCollision(state, obj_attributes_ptr); + XSemaphore* sem = new XSemaphore(state); sem->Initialize(count, limit); // obj_attributes may have a name inside of it, if != NULL. if (obj_attributes_ptr) { - // sem->SetName(...); + sem->SetAttributes(SHIM_MEM_ADDR(obj_attributes_ptr)); } if (handle_ptr) { @@ -656,12 +683,16 @@ SHIM_CALL NtCreateMutant_shim(PPCContext* ppc_state, KernelState* state) { XELOGD("NtCreateMutant(%.8X, %.8X, %.1X)", handle_ptr, obj_attributes_ptr, initial_owner); + // TODO(benvanik): check for name collision. May return existing object if + // type matches. + AssertNoNameCollision(state, obj_attributes_ptr); + XMutant* mutant = new XMutant(state); mutant->Initialize(initial_owner ? true : false); // obj_attributes may have a name inside of it, if != NULL. if (obj_attributes_ptr) { - // mutant->SetName(...); + mutant->SetAttributes(SHIM_MEM_ADDR(obj_attributes_ptr)); } if (handle_ptr) { @@ -709,12 +740,16 @@ SHIM_CALL NtCreateTimer_shim(PPCContext* ppc_state, KernelState* state) { XELOGD("NtCreateTimer(%.8X, %.8X, %.1X)", handle_ptr, obj_attributes_ptr, timer_type); + // TODO(benvanik): check for name collision. May return existing object if + // type matches. + AssertNoNameCollision(state, obj_attributes_ptr); + XTimer* timer = new XTimer(state); timer->Initialize(timer_type); // obj_attributes may have a name inside of it, if != NULL. if (obj_attributes_ptr) { - // timer->SetName(...); + timer->SetAttributes(SHIM_MEM_ADDR(obj_attributes_ptr)); } if (handle_ptr) { diff --git a/src/xenia/kernel/xobject.cc b/src/xenia/kernel/xobject.cc index 736760bf8..f2cac6ae1 100644 --- a/src/xenia/kernel/xobject.cc +++ b/src/xenia/kernel/xobject.cc @@ -56,9 +56,25 @@ void XObject::Release() { } X_STATUS XObject::Delete() { + if (!name_.empty()) { + kernel_state_->object_table()->RemoveNameMapping(name_); + } return kernel_state_->object_table()->RemoveHandle(handle_); } +void XObject::SetAttributes(const uint8_t* obj_attrs_ptr) { + if (!obj_attrs_ptr) { + return; + } + + uint32_t name_str_ptr = poly::load_and_swap(obj_attrs_ptr + 4); + if (name_str_ptr) { + X_ANSI_STRING name_str(membase(), name_str_ptr); + name_ = name_str.to_string(); + kernel_state_->object_table()->AddNameMapping(name_, handle_); + } +} + uint32_t XObject::TimeoutTicksToMs(int64_t timeout_ticks) { if (timeout_ticks > 0) { // Absolute time, based on January 1, 1601. diff --git a/src/xenia/kernel/xobject.h b/src/xenia/kernel/xobject.h index 86e43a423..32d39103c 100644 --- a/src/xenia/kernel/xobject.h +++ b/src/xenia/kernel/xobject.h @@ -46,9 +46,11 @@ class XObject { Emulator* emulator() const { return kernel_state_->emulator_; } KernelState* kernel_state() const { return kernel_state_; } + uint8_t* membase() const { return kernel_state_->memory()->membase(); } Type type(); X_HANDLE handle() const; + const std::string& name() const { return name_; } void RetainHandle(); bool ReleaseHandle(); @@ -59,6 +61,8 @@ class XObject { // Reference() // Dereference() + void SetAttributes(const uint8_t* obj_attrs_ptr); + X_STATUS Wait(uint32_t wait_reason, uint32_t processor_mode, uint32_t alertable, uint64_t* opt_timeout); static X_STATUS SignalAndWait(XObject* signal_object, XObject* wait_object, @@ -88,6 +92,7 @@ class XObject { Type type_; X_HANDLE handle_; + std::string name_; // May be zero length. }; } // namespace kernel diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index 0f2397e5b..c91f451c9 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -10,10 +10,11 @@ #ifndef XENIA_XBOX_H_ #define XENIA_XBOX_H_ +#include + #include "poly/memory.h" #include "xenia/common.h" - namespace xe { template @@ -41,6 +42,7 @@ typedef uint32_t X_STATUS; #define X_STATUS_ALERTED ((X_STATUS)0x00000101L) #define X_STATUS_TIMEOUT ((X_STATUS)0x00000102L) #define X_STATUS_PENDING ((X_STATUS)0x00000103L) +#define X_STATUS_OBJECT_NAME_EXISTS ((X_STATUS)0x40000000L) #define X_STATUS_TIMER_RESUME_IGNORED ((X_STATUS)0x40000025L) #define X_STATUS_BUFFER_OVERFLOW ((X_STATUS)0x80000005L) #define X_STATUS_NO_MORE_FILES ((X_STATUS)0x80000006L) @@ -57,6 +59,8 @@ typedef uint32_t X_STATUS; #define X_STATUS_ACCESS_DENIED ((X_STATUS)0xC0000022L) #define X_STATUS_BUFFER_TOO_SMALL ((X_STATUS)0xC0000023L) #define X_STATUS_OBJECT_TYPE_MISMATCH ((X_STATUS)0xC0000024L) +#define X_STATUS_OBJECT_NAME_NOT_FOUND ((X_STATUS)0xC0000034L) +#define X_STATUS_OBJECT_NAME_COLLISION ((X_STATUS)0xC0000035L) #define X_STATUS_INVALID_PAGE_PROTECTION ((X_STATUS)0xC0000045L) #define X_STATUS_MUTANT_NOT_OWNED ((X_STATUS)0xC0000046L) #define X_STATUS_MEMORY_NOT_ALLOCATED ((X_STATUS)0xC00000A0L) @@ -268,13 +272,20 @@ public: buffer = 0; } char* Duplicate() { - if (buffer == NULL || length == 0) { - return NULL; + if (!buffer || !length) { + return nullptr; } auto copy = (char*)calloc(length + 1, sizeof(char)); std::strncpy(copy, buffer, length); return copy; } + std::string to_string() { + if (!buffer || !length) { + return ""; + } + std::string result(buffer, length); + return result; + } }; //static_assert_size(X_ANSI_STRING, 8);