Merge pull request #300 from DrChat/hmodule_fix
Swap to using HMODULE instead of handles for xex modules.
This commit is contained in:
commit
7875a4b0ba
|
@ -83,6 +83,32 @@ struct be {
|
||||||
be(const T &src) : value(xe::byte_swap(src)) {}
|
be(const T &src) : value(xe::byte_swap(src)) {}
|
||||||
be(const be &other) { value = other.value; }
|
be(const be &other) { value = other.value; }
|
||||||
operator T() const { return xe::byte_swap(value); }
|
operator T() const { return xe::byte_swap(value); }
|
||||||
|
|
||||||
|
be<T> &operator+=(int a) {
|
||||||
|
*this = *this + a;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
be<T> &operator-=(int a) {
|
||||||
|
*this = *this - a;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
be<T> &operator++() {
|
||||||
|
*this += 1;
|
||||||
|
return *this;
|
||||||
|
} // ++a
|
||||||
|
be<T> operator++(int) {
|
||||||
|
*this += 1;
|
||||||
|
return (*this - 1);
|
||||||
|
} // a++
|
||||||
|
be<T> &operator--() {
|
||||||
|
*this -= 1;
|
||||||
|
return *this;
|
||||||
|
} // --a
|
||||||
|
be<T> operator--(int) {
|
||||||
|
*this -= 1;
|
||||||
|
return (*this + 1);
|
||||||
|
} // a--
|
||||||
|
|
||||||
T value;
|
T value;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ KernelState::KernelState(Emulator* emulator)
|
||||||
shared_kernel_state_ = this;
|
shared_kernel_state_ = this;
|
||||||
|
|
||||||
process_info_block_address_ = memory_->SystemHeapAlloc(0x60);
|
process_info_block_address_ = memory_->SystemHeapAlloc(0x60);
|
||||||
|
|
||||||
auto pib =
|
auto pib =
|
||||||
memory_->TranslateVirtual<ProcessInfoBlock*>(process_info_block_address_);
|
memory_->TranslateVirtual<ProcessInfoBlock*>(process_info_block_address_);
|
||||||
// TODO(benvanik): figure out what this list is.
|
// TODO(benvanik): figure out what this list is.
|
||||||
|
@ -190,6 +191,17 @@ void KernelState::SetExecutableModule(object_ref<XUserModule> module) {
|
||||||
pib->tls_slot_size = header->tls_info.slot_count * 4;
|
pib->tls_slot_size = header->tls_info.slot_count * 4;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup the kernel's XexExecutableModuleHandle field
|
||||||
|
auto exp = processor()->export_resolver()->GetExportByOrdinal(
|
||||||
|
"xboxkrnl.exe", ordinals::XexExecutableModuleHandle);
|
||||||
|
|
||||||
|
if (exp) {
|
||||||
|
auto variable_ptr =
|
||||||
|
memory()->TranslateVirtual<xe::be<uint32_t>*>(exp->variable_ptr);
|
||||||
|
|
||||||
|
*variable_ptr = module->hmodule_ptr();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void KernelState::LoadKernelModule(object_ref<XKernelModule> kernel_module) {
|
void KernelState::LoadKernelModule(object_ref<XKernelModule> kernel_module) {
|
||||||
|
|
|
@ -20,7 +20,8 @@ XModule::XModule(KernelState* kernel_state, ModuleType module_type,
|
||||||
: XObject(kernel_state, kTypeModule),
|
: XObject(kernel_state, kTypeModule),
|
||||||
module_type_(module_type),
|
module_type_(module_type),
|
||||||
path_(path),
|
path_(path),
|
||||||
processor_module_(nullptr) {
|
processor_module_(nullptr),
|
||||||
|
hmodule_ptr_(0) {
|
||||||
auto last_slash = path.find_last_of('/');
|
auto last_slash = path.find_last_of('/');
|
||||||
if (last_slash == path.npos) {
|
if (last_slash == path.npos) {
|
||||||
last_slash = path.find_last_of('\\');
|
last_slash = path.find_last_of('\\');
|
||||||
|
@ -34,9 +35,22 @@ XModule::XModule(KernelState* kernel_state, ModuleType module_type,
|
||||||
if (dot != name_.npos) {
|
if (dot != name_.npos) {
|
||||||
name_ = name_.substr(0, dot);
|
name_ = name_.substr(0, dot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loader data (HMODULE)
|
||||||
|
hmodule_ptr_ = memory()->SystemHeapAlloc(sizeof(X_LDR_DATA_TABLE_ENTRY));
|
||||||
|
|
||||||
|
// Hijack the checksum field to store our kernel object handle.
|
||||||
|
auto ldr_data =
|
||||||
|
memory()->TranslateVirtual<X_LDR_DATA_TABLE_ENTRY*>(hmodule_ptr_);
|
||||||
|
ldr_data->checksum = handle();
|
||||||
}
|
}
|
||||||
|
|
||||||
XModule::~XModule() { kernel_state_->UnregisterModule(this); }
|
XModule::~XModule() {
|
||||||
|
kernel_state_->UnregisterModule(this);
|
||||||
|
|
||||||
|
// Destroy the loader data.
|
||||||
|
memory()->SystemHeapFree(hmodule_ptr_);
|
||||||
|
}
|
||||||
|
|
||||||
bool XModule::Matches(const std::string& name) const {
|
bool XModule::Matches(const std::string& name) const {
|
||||||
if (strcasecmp(xe::find_name_from_path(path_).c_str(), name.c_str()) == 0) {
|
if (strcasecmp(xe::find_name_from_path(path_).c_str(), name.c_str()) == 0) {
|
||||||
|
@ -58,5 +72,17 @@ X_STATUS XModule::GetSection(const char* name, uint32_t* out_section_data,
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object_ref<XModule> XModule::GetFromHModule(KernelState* kernel_state,
|
||||||
|
void* hmodule) {
|
||||||
|
// Grab the object from our stashed kernel handle
|
||||||
|
return kernel_state->object_table()->LookupObject<XModule>(
|
||||||
|
GetHandleFromHModule(hmodule));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t XModule::GetHandleFromHModule(void* hmodule) {
|
||||||
|
auto ldr_data = reinterpret_cast<X_LDR_DATA_TABLE_ENTRY*>(hmodule);
|
||||||
|
return ldr_data->checksum;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|
|
@ -19,6 +19,42 @@
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace kernel {
|
namespace kernel {
|
||||||
|
|
||||||
|
// http://www.nirsoft.net/kernel_struct/vista/LDR_DATA_TABLE_ENTRY.html
|
||||||
|
// HMODULE points to this struct!
|
||||||
|
struct X_LDR_DATA_TABLE_ENTRY {
|
||||||
|
X_LIST_ENTRY in_load_order_links; // 0x0
|
||||||
|
X_LIST_ENTRY in_memory_order_links; // 0x8
|
||||||
|
X_LIST_ENTRY in_initialization_order_links; // 0x10
|
||||||
|
|
||||||
|
xe::be<uint32_t> dll_base; // 0x18
|
||||||
|
xe::be<uint32_t> image_base; // 0x1C
|
||||||
|
xe::be<uint32_t> image_size; // 0x20
|
||||||
|
|
||||||
|
X_UNICODE_STRING full_dll_name; // 0x24
|
||||||
|
X_UNICODE_STRING base_dll_name; // 0x2C
|
||||||
|
|
||||||
|
xe::be<uint32_t> flags; // 0x34
|
||||||
|
xe::be<uint32_t> full_image_size; // 0x38
|
||||||
|
xe::be<uint32_t> entry_point; // 0x3C
|
||||||
|
xe::be<uint16_t> load_count; // 0x40
|
||||||
|
xe::be<uint16_t> module_index; // 0x42
|
||||||
|
xe::be<uint32_t> dll_base_original; // 0x44
|
||||||
|
xe::be<uint32_t> checksum; // 0x48 hijacked to hold kernel handle
|
||||||
|
xe::be<uint32_t> load_flags; // 0x4C
|
||||||
|
xe::be<uint32_t> time_date_stamp; // 0x50
|
||||||
|
xe::be<uint32_t> loaded_imports; // 0x54
|
||||||
|
xe::be<uint32_t> xex_header_base; // 0x58
|
||||||
|
|
||||||
|
union {
|
||||||
|
X_ANSI_STRING load_file_name; // 0x5C
|
||||||
|
|
||||||
|
struct {
|
||||||
|
xe::be<uint32_t> closure_root; // 0x5C
|
||||||
|
xe::be<uint32_t> traversal_parent; // 0x60
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
class XModule : public XObject {
|
class XModule : public XObject {
|
||||||
public:
|
public:
|
||||||
enum class ModuleType {
|
enum class ModuleType {
|
||||||
|
@ -37,12 +73,17 @@ class XModule : public XObject {
|
||||||
bool Matches(const std::string& name) const;
|
bool Matches(const std::string& name) const;
|
||||||
|
|
||||||
xe::cpu::Module* processor_module() const { return processor_module_; }
|
xe::cpu::Module* processor_module() const { return processor_module_; }
|
||||||
|
uint32_t hmodule_ptr() const { return hmodule_ptr_; }
|
||||||
|
|
||||||
virtual uint32_t GetProcAddressByOrdinal(uint16_t ordinal) = 0;
|
virtual uint32_t GetProcAddressByOrdinal(uint16_t ordinal) = 0;
|
||||||
virtual uint32_t GetProcAddressByName(const char* name) = 0;
|
virtual uint32_t GetProcAddressByName(const char* name) = 0;
|
||||||
virtual X_STATUS GetSection(const char* name, uint32_t* out_section_data,
|
virtual X_STATUS GetSection(const char* name, uint32_t* out_section_data,
|
||||||
uint32_t* out_section_size);
|
uint32_t* out_section_size);
|
||||||
|
|
||||||
|
static object_ref<XModule> GetFromHModule(KernelState* kernel_state,
|
||||||
|
void* hmodule);
|
||||||
|
static uint32_t GetHandleFromHModule(void* hmodule);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void OnLoad();
|
void OnLoad();
|
||||||
|
|
||||||
|
@ -51,6 +92,8 @@ class XModule : public XObject {
|
||||||
std::string path_;
|
std::string path_;
|
||||||
|
|
||||||
xe::cpu::Module* processor_module_;
|
xe::cpu::Module* processor_module_;
|
||||||
|
|
||||||
|
uint32_t hmodule_ptr_; // This points to LDR_DATA_TABLE_ENTRY.
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
|
|
|
@ -22,14 +22,9 @@ namespace kernel {
|
||||||
using namespace xe::cpu;
|
using namespace xe::cpu;
|
||||||
|
|
||||||
XUserModule::XUserModule(KernelState* kernel_state, const char* path)
|
XUserModule::XUserModule(KernelState* kernel_state, const char* path)
|
||||||
: XModule(kernel_state, ModuleType::kUserModule, path),
|
: XModule(kernel_state, ModuleType::kUserModule, path), xex_(nullptr) {}
|
||||||
xex_(nullptr),
|
|
||||||
execution_info_ptr_(0) {}
|
|
||||||
|
|
||||||
XUserModule::~XUserModule() {
|
XUserModule::~XUserModule() { xe_xex2_dealloc(xex_); }
|
||||||
kernel_state()->memory()->SystemHeapFree(execution_info_ptr_);
|
|
||||||
xe_xex2_dealloc(xex_);
|
|
||||||
}
|
|
||||||
|
|
||||||
xe_xex2_ref XUserModule::xex() { return xex_; }
|
xe_xex2_ref XUserModule::xex() { return xex_; }
|
||||||
|
|
||||||
|
@ -94,20 +89,21 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store execution info for later use.
|
// Copy the xex2 header into guest memory
|
||||||
// TODO(benvanik): just put entire xex header in memory somewhere?
|
const xex2_header* header = reinterpret_cast<const xex2_header*>(addr);
|
||||||
execution_info_ptr_ = memory()->SystemHeapAlloc(24);
|
uint32_t header_size = xex2_get_header_size(header);
|
||||||
auto eip = memory()->TranslateVirtual(execution_info_ptr_);
|
|
||||||
const auto& ex = xe_xex2_get_header(xex_)->execution_info;
|
xex_header_ = memory()->SystemHeapAlloc(header_size);
|
||||||
xe::store_and_swap<uint32_t>(eip + 0x00, ex.media_id);
|
|
||||||
xe::store_and_swap<uint32_t>(eip + 0x04, ex.version.value);
|
uint8_t* xex_header_ptr = memory()->TranslateVirtual(xex_header_);
|
||||||
xe::store_and_swap<uint32_t>(eip + 0x08, ex.base_version.value);
|
std::memcpy(xex_header_ptr, header, header_size);
|
||||||
xe::store_and_swap<uint32_t>(eip + 0x0C, ex.title_id);
|
|
||||||
xe::store_and_swap<uint8_t>(eip + 0x10, ex.platform);
|
// Setup the loader data entry
|
||||||
xe::store_and_swap<uint8_t>(eip + 0x11, ex.executable_table);
|
auto ldr_data =
|
||||||
xe::store_and_swap<uint8_t>(eip + 0x12, ex.disc_number);
|
memory()->TranslateVirtual<X_LDR_DATA_TABLE_ENTRY*>(hmodule_ptr_);
|
||||||
xe::store_and_swap<uint8_t>(eip + 0x13, ex.disc_count);
|
|
||||||
xe::store_and_swap<uint32_t>(eip + 0x14, ex.savegame_id);
|
ldr_data->dll_base = 0; // GetProcAddress will read this.
|
||||||
|
ldr_data->xex_header_base = xex_header_;
|
||||||
|
|
||||||
// Prepare the module for execution.
|
// Prepare the module for execution.
|
||||||
// Runtime takes ownership.
|
// Runtime takes ownership.
|
||||||
|
@ -148,6 +144,24 @@ X_STATUS XUserModule::GetSection(const char* name, uint32_t* out_section_data,
|
||||||
return X_STATUS_UNSUCCESSFUL;
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key,
|
||||||
|
uint32_t* out_header_guest_ptr) {
|
||||||
|
assert_not_null(out_header_guest_ptr);
|
||||||
|
|
||||||
|
auto header = memory()->TranslateVirtual<xex2_header*>(xex_header_);
|
||||||
|
if (!header) {
|
||||||
|
return X_STATUS_UNSUCCESSFUL;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ptr = xex2_get_opt_header(header, key);
|
||||||
|
if (!ptr) {
|
||||||
|
return X_STATUS_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_header_guest_ptr = (uint32_t)(ptr - memory()->virtual_membase());
|
||||||
|
return X_STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
X_STATUS XUserModule::Launch(uint32_t flags) {
|
X_STATUS XUserModule::Launch(uint32_t flags) {
|
||||||
const xe_xex2_header_t* header = xex_header();
|
const xe_xex2_header_t* header = xex_header();
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,6 @@ class XUserModule : public XModule {
|
||||||
xe_xex2_ref xex();
|
xe_xex2_ref xex();
|
||||||
const xe_xex2_header_t* xex_header();
|
const xe_xex2_header_t* xex_header();
|
||||||
|
|
||||||
uint32_t execution_info_ptr() const { return execution_info_ptr_; }
|
|
||||||
|
|
||||||
X_STATUS LoadFromFile(std::string path);
|
X_STATUS LoadFromFile(std::string path);
|
||||||
X_STATUS LoadFromMemory(const void* addr, const size_t length);
|
X_STATUS LoadFromMemory(const void* addr, const size_t length);
|
||||||
|
|
||||||
|
@ -37,6 +35,8 @@ class XUserModule : public XModule {
|
||||||
uint32_t GetProcAddressByName(const char* name) override;
|
uint32_t GetProcAddressByName(const char* name) override;
|
||||||
X_STATUS GetSection(const char* name, uint32_t* out_section_data,
|
X_STATUS GetSection(const char* name, uint32_t* out_section_data,
|
||||||
uint32_t* out_section_size) override;
|
uint32_t* out_section_size) override;
|
||||||
|
X_STATUS GetOptHeader(xe_xex2_header_keys key,
|
||||||
|
uint32_t* out_header_guest_ptr);
|
||||||
|
|
||||||
X_STATUS Launch(uint32_t flags);
|
X_STATUS Launch(uint32_t flags);
|
||||||
|
|
||||||
|
@ -44,7 +44,7 @@ class XUserModule : public XModule {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xe_xex2_ref xex_;
|
xe_xex2_ref xex_;
|
||||||
uint32_t execution_info_ptr_;
|
uint32_t xex_header_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace kernel
|
} // namespace kernel
|
||||||
|
|
|
@ -27,6 +27,33 @@
|
||||||
#include "xenia/base/memory.h"
|
#include "xenia/base/memory.h"
|
||||||
#include "xenia/base/platform.h"
|
#include "xenia/base/platform.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
uint8_t *xex2_get_opt_header(const xex2_header *header, uint32_t key) {
|
||||||
|
for (uint32_t i = 0; i < header->header_count; i++) {
|
||||||
|
const xex2_opt_header &opt_header = header->headers[i];
|
||||||
|
if (opt_header.key != key) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((opt_header.key & 0xFF) == 0x01) {
|
||||||
|
// Data is stored in the opt header
|
||||||
|
return (uint8_t *)&opt_header.value;
|
||||||
|
} else {
|
||||||
|
// Data stored at offset.
|
||||||
|
return ((uint8_t *)&header->headers[0] + opt_header.offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t xex2_get_header_size(const xex2_header *header) {
|
||||||
|
return header->exe_offset;
|
||||||
|
}
|
||||||
|
} // namespace kernel
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
// TODO(benvanik): remove.
|
// TODO(benvanik): remove.
|
||||||
#define XEEXPECTZERO(expr) \
|
#define XEEXPECTZERO(expr) \
|
||||||
if ((expr) != 0) { \
|
if ((expr) != 0) { \
|
||||||
|
@ -126,7 +153,7 @@ int xe_xex2_read_header(const uint8_t *addr, const size_t length,
|
||||||
xe_xex2_loader_info_t *ldr;
|
xe_xex2_loader_info_t *ldr;
|
||||||
|
|
||||||
header->xex2 = xe::load_and_swap<uint32_t>(p + 0x00);
|
header->xex2 = xe::load_and_swap<uint32_t>(p + 0x00);
|
||||||
if (header->xex2 != 0x58455832) {
|
if (header->xex2 != 'XEX2') {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -356,8 +383,8 @@ int xe_xex2_read_header(const uint8_t *addr, const size_t length,
|
||||||
uint32_t window_size = xe::load_and_swap<uint32_t>(pp + 0x08);
|
uint32_t window_size = xe::load_and_swap<uint32_t>(pp + 0x08);
|
||||||
uint32_t window_bits = 0;
|
uint32_t window_bits = 0;
|
||||||
for (size_t m = 0; m < 32; m++, window_bits++) {
|
for (size_t m = 0; m < 32; m++, window_bits++) {
|
||||||
window_size <<= 1;
|
window_size >>= 1;
|
||||||
if (window_size == 0x80000000) {
|
if (window_size == 0x00000000) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,14 @@
|
||||||
#include "xenia/kernel/util/xex2_info.h"
|
#include "xenia/kernel/util/xex2_info.h"
|
||||||
#include "xenia/memory.h"
|
#include "xenia/memory.h"
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
uint8_t* xex2_get_opt_header(const xex2_header* header, uint32_t key);
|
||||||
|
|
||||||
|
uint32_t xex2_get_header_size(const xex2_header* header);
|
||||||
|
} // namespace kernel
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
typedef struct { int reserved; } xe_xex2_options_t;
|
typedef struct { int reserved; } xe_xex2_options_t;
|
||||||
|
|
||||||
struct xe_xex2;
|
struct xe_xex2;
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "xenia/base/byte_order.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
XEX_HEADER_RESOURCE_INFO = 0x000002FF,
|
XEX_HEADER_RESOURCE_INFO = 0x000002FF,
|
||||||
XEX_HEADER_FILE_FORMAT_INFO = 0x000003FF,
|
XEX_HEADER_FILE_FORMAT_INFO = 0x000003FF,
|
||||||
|
@ -467,4 +469,55 @@ typedef struct {
|
||||||
xe_xex2_section_t* sections;
|
xe_xex2_section_t* sections;
|
||||||
} xe_xex2_header_t;
|
} xe_xex2_header_t;
|
||||||
|
|
||||||
|
namespace xe {
|
||||||
|
namespace kernel {
|
||||||
|
union xex2_version {
|
||||||
|
uint32_t value;
|
||||||
|
struct {
|
||||||
|
uint32_t major : 4;
|
||||||
|
uint32_t minor : 4;
|
||||||
|
uint32_t build : 16;
|
||||||
|
uint32_t qfe : 8;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xex2_opt_execution_info {
|
||||||
|
xe::be<uint32_t> media_id; // 0x0
|
||||||
|
xe::be<xex2_version> version; // 0x4
|
||||||
|
xe::be<xex2_version> base_version; // 0x8
|
||||||
|
xe::be<uint32_t> title_id; // 0xC
|
||||||
|
uint8_t platform; // 0x10
|
||||||
|
uint8_t executable_table; // 0x11
|
||||||
|
uint8_t disc_number; // 0x12
|
||||||
|
uint8_t disc_count; // 0x13
|
||||||
|
xe::be<uint32_t> savegame_id; // 0x14
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xex2_opt_header {
|
||||||
|
xe::be<uint32_t> key; // 0x0
|
||||||
|
|
||||||
|
union {
|
||||||
|
xe::be<uint32_t> value; // 0x4
|
||||||
|
xe::be<uint32_t> offset; // 0x8
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xex2_header {
|
||||||
|
xe::be<uint32_t> magic; // 0x0 'XEX2'
|
||||||
|
xe::be<xe_xex2_module_flags> module_flags; // 0x4
|
||||||
|
xe::be<uint32_t> exe_offset; // 0x8
|
||||||
|
xe::be<uint32_t> reserved; // 0xC
|
||||||
|
xe::be<uint32_t> certificate_offset; // 0x10
|
||||||
|
xe::be<uint32_t> header_count; // 0x14
|
||||||
|
|
||||||
|
xex2_opt_header headers[1]; // 0x18
|
||||||
|
};
|
||||||
|
|
||||||
|
struct xex2_loader_info {
|
||||||
|
xe::be<uint32_t> header_size;
|
||||||
|
xe::be<uint32_t> image_size;
|
||||||
|
};
|
||||||
|
} // namespace kernel
|
||||||
|
} // namespace xe
|
||||||
|
|
||||||
#endif // XENIA_KERNEL_XEX2_INFO_H_
|
#endif // XENIA_KERNEL_XEX2_INFO_H_
|
||||||
|
|
|
@ -77,9 +77,17 @@ SHIM_CALL XamGetExecutionId_shim(PPCContext* ppc_context,
|
||||||
auto module = kernel_state->GetExecutableModule();
|
auto module = kernel_state->GetExecutableModule();
|
||||||
assert_not_null(module);
|
assert_not_null(module);
|
||||||
|
|
||||||
SHIM_SET_MEM_32(info_ptr, module->execution_info_ptr());
|
uint32_t guest_hdr_ptr;
|
||||||
|
X_STATUS result =
|
||||||
|
module->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &guest_hdr_ptr);
|
||||||
|
|
||||||
SHIM_SET_RETURN_32(0);
|
if (XFAILED(result)) {
|
||||||
|
SHIM_SET_RETURN_32(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHIM_SET_MEM_32(info_ptr, guest_hdr_ptr);
|
||||||
|
SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
SHIM_CALL XamLoaderSetLaunchData_shim(PPCContext* ppc_context,
|
SHIM_CALL XamLoaderSetLaunchData_shim(PPCContext* ppc_context,
|
||||||
|
|
|
@ -89,17 +89,9 @@ XboxkrnlModule::XboxkrnlModule(Emulator* emulator, KernelState* kernel_state)
|
||||||
// 0x80101058 <- pointer to xex header
|
// 0x80101058 <- pointer to xex header
|
||||||
// 0x80101100 <- xex header base
|
// 0x80101100 <- xex header base
|
||||||
uint32_t ppXexExecutableModuleHandle = memory_->SystemHeapAlloc(4);
|
uint32_t ppXexExecutableModuleHandle = memory_->SystemHeapAlloc(4);
|
||||||
auto lppXexExecutableModuleHandle =
|
|
||||||
memory_->TranslateVirtual(ppXexExecutableModuleHandle);
|
|
||||||
export_resolver_->SetVariableMapping("xboxkrnl.exe",
|
export_resolver_->SetVariableMapping("xboxkrnl.exe",
|
||||||
ordinals::XexExecutableModuleHandle,
|
ordinals::XexExecutableModuleHandle,
|
||||||
ppXexExecutableModuleHandle);
|
ppXexExecutableModuleHandle);
|
||||||
uint32_t pXexExecutableModuleHandle = memory_->SystemHeapAlloc(256);
|
|
||||||
auto lpXexExecutableModuleHandle =
|
|
||||||
memory_->TranslateVirtual(pXexExecutableModuleHandle);
|
|
||||||
xe::store_and_swap<uint32_t>(lppXexExecutableModuleHandle,
|
|
||||||
pXexExecutableModuleHandle);
|
|
||||||
xe::store_and_swap<uint32_t>(lpXexExecutableModuleHandle + 0x58, 0x80101100);
|
|
||||||
|
|
||||||
// ExLoadedCommandLine (char*)
|
// ExLoadedCommandLine (char*)
|
||||||
// The name of the xex. Not sure this is ever really used on real devices.
|
// The name of the xex. Not sure this is ever really used on real devices.
|
||||||
|
|
|
@ -153,9 +153,9 @@ SHIM_CALL XexGetModuleHandle_shim(PPCContext* ppc_context,
|
||||||
KernelState* kernel_state) {
|
KernelState* kernel_state) {
|
||||||
uint32_t module_name_ptr = SHIM_GET_ARG_32(0);
|
uint32_t module_name_ptr = SHIM_GET_ARG_32(0);
|
||||||
const char* module_name = (const char*)SHIM_MEM_ADDR(module_name_ptr);
|
const char* module_name = (const char*)SHIM_MEM_ADDR(module_name_ptr);
|
||||||
uint32_t module_handle_ptr = SHIM_GET_ARG_32(1);
|
uint32_t hmodule_ptr = SHIM_GET_ARG_32(1);
|
||||||
|
|
||||||
XELOGD("XexGetModuleHandle(%s, %.8X)", module_name, module_handle_ptr);
|
XELOGD("XexGetModuleHandle(%s, %.8X)", module_name, hmodule_ptr);
|
||||||
|
|
||||||
object_ref<XModule> module;
|
object_ref<XModule> module;
|
||||||
if (!module_name) {
|
if (!module_name) {
|
||||||
|
@ -164,31 +164,31 @@ SHIM_CALL XexGetModuleHandle_shim(PPCContext* ppc_context,
|
||||||
module = kernel_state->GetModule(module_name);
|
module = kernel_state->GetModule(module_name);
|
||||||
}
|
}
|
||||||
if (!module) {
|
if (!module) {
|
||||||
SHIM_SET_MEM_32(module_handle_ptr, 0);
|
SHIM_SET_MEM_32(hmodule_ptr, 0);
|
||||||
SHIM_SET_RETURN_32(X_ERROR_NOT_FOUND);
|
SHIM_SET_RETURN_32(X_ERROR_NOT_FOUND);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: we don't retain the handle for return.
|
// NOTE: we don't retain the handle for return.
|
||||||
SHIM_SET_MEM_32(module_handle_ptr, module->handle());
|
SHIM_SET_MEM_32(hmodule_ptr, module->hmodule_ptr());
|
||||||
|
|
||||||
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
|
SHIM_SET_RETURN_32(X_ERROR_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
SHIM_CALL XexGetModuleSection_shim(PPCContext* ppc_context,
|
SHIM_CALL XexGetModuleSection_shim(PPCContext* ppc_context,
|
||||||
KernelState* kernel_state) {
|
KernelState* kernel_state) {
|
||||||
uint32_t handle = SHIM_GET_ARG_32(0);
|
uint32_t hmodule = SHIM_GET_ARG_32(0);
|
||||||
uint32_t name_ptr = SHIM_GET_ARG_32(1);
|
uint32_t name_ptr = SHIM_GET_ARG_32(1);
|
||||||
const char* name = (const char*)SHIM_MEM_ADDR(name_ptr);
|
const char* name = (const char*)SHIM_MEM_ADDR(name_ptr);
|
||||||
uint32_t data_ptr = SHIM_GET_ARG_32(2);
|
uint32_t data_ptr = SHIM_GET_ARG_32(2);
|
||||||
uint32_t size_ptr = SHIM_GET_ARG_32(3);
|
uint32_t size_ptr = SHIM_GET_ARG_32(3);
|
||||||
|
|
||||||
XELOGD("XexGetModuleSection(%.8X, %s, %.8X, %.8X)", handle, name, data_ptr,
|
XELOGD("XexGetModuleSection(%.8X, %s, %.8X, %.8X)", hmodule, name, data_ptr,
|
||||||
size_ptr);
|
size_ptr);
|
||||||
|
|
||||||
X_STATUS result = X_STATUS_SUCCESS;
|
X_STATUS result = X_STATUS_SUCCESS;
|
||||||
|
|
||||||
auto module = kernel_state->object_table()->LookupObject<XModule>(handle);
|
auto module = XModule::GetFromHModule(kernel_state, SHIM_MEM_ADDR(hmodule));
|
||||||
if (module) {
|
if (module) {
|
||||||
uint32_t section_data = 0;
|
uint32_t section_data = 0;
|
||||||
uint32_t section_size = 0;
|
uint32_t section_size = 0;
|
||||||
|
@ -210,49 +210,64 @@ SHIM_CALL XexLoadImage_shim(PPCContext* ppc_context,
|
||||||
const char* module_name = (const char*)SHIM_MEM_ADDR(module_name_ptr);
|
const char* module_name = (const char*)SHIM_MEM_ADDR(module_name_ptr);
|
||||||
uint32_t module_flags = SHIM_GET_ARG_32(1);
|
uint32_t module_flags = SHIM_GET_ARG_32(1);
|
||||||
uint32_t min_version = SHIM_GET_ARG_32(2);
|
uint32_t min_version = SHIM_GET_ARG_32(2);
|
||||||
uint32_t handle_ptr = SHIM_GET_ARG_32(3);
|
uint32_t hmodule_ptr = SHIM_GET_ARG_32(3);
|
||||||
|
|
||||||
XELOGD("XexLoadImage(%s, %.8X, %.8X, %.8X)", module_name, module_flags,
|
XELOGD("XexLoadImage(%s, %.8X, %.8X, %.8X)", module_name, module_flags,
|
||||||
min_version, handle_ptr);
|
min_version, hmodule_ptr);
|
||||||
|
|
||||||
X_STATUS result = X_STATUS_NO_SUCH_FILE;
|
X_STATUS result = X_STATUS_NO_SUCH_FILE;
|
||||||
|
|
||||||
X_HANDLE module_handle = X_INVALID_HANDLE_VALUE;
|
uint32_t hmodule = 0;
|
||||||
auto module = kernel_state->GetModule(module_name);
|
auto module = kernel_state->GetModule(module_name);
|
||||||
if (module) {
|
if (module) {
|
||||||
// Existing module found, just add a reference and obtain a handle.
|
// Existing module found.
|
||||||
result =
|
hmodule = module->hmodule_ptr();
|
||||||
kernel_state->object_table()->AddHandle(module.get(), &module_handle);
|
|
||||||
} else {
|
} else {
|
||||||
// Not found; attempt to load as a user module.
|
// Not found; attempt to load as a user module.
|
||||||
auto user_module = kernel_state->LoadUserModule(module_name);
|
auto user_module = kernel_state->LoadUserModule(module_name);
|
||||||
if (user_module) {
|
if (user_module) {
|
||||||
user_module->RetainHandle();
|
user_module->RetainHandle();
|
||||||
module_handle = user_module->handle();
|
hmodule = user_module->hmodule_ptr();
|
||||||
result = X_STATUS_SUCCESS;
|
result = X_STATUS_SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SHIM_SET_MEM_32(handle_ptr, module_handle);
|
|
||||||
|
// Increment the module's load count.
|
||||||
|
auto ldr_data =
|
||||||
|
kernel_memory()->TranslateVirtual<X_LDR_DATA_TABLE_ENTRY*>(hmodule);
|
||||||
|
ldr_data->load_count++;
|
||||||
|
|
||||||
|
SHIM_SET_MEM_32(hmodule_ptr, hmodule);
|
||||||
|
|
||||||
SHIM_SET_RETURN_32(result);
|
SHIM_SET_RETURN_32(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
SHIM_CALL XexUnloadImage_shim(PPCContext* ppc_context,
|
SHIM_CALL XexUnloadImage_shim(PPCContext* ppc_context,
|
||||||
KernelState* kernel_state) {
|
KernelState* kernel_state) {
|
||||||
uint32_t module_handle = SHIM_GET_ARG_32(0);
|
uint32_t hmodule = SHIM_GET_ARG_32(0);
|
||||||
|
|
||||||
XELOGD("XexUnloadImage(%.8X)", module_handle);
|
XELOGD("XexUnloadImage(%.8X)", hmodule);
|
||||||
|
|
||||||
X_STATUS result = X_STATUS_INVALID_HANDLE;
|
auto module = XModule::GetFromHModule(kernel_state, SHIM_MEM_ADDR(hmodule));
|
||||||
|
if (!module) {
|
||||||
|
SHIM_SET_RETURN_32(X_STATUS_INVALID_HANDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
result = kernel_state->object_table()->RemoveHandle(module_handle);
|
auto ldr_data =
|
||||||
|
kernel_state->memory()->TranslateVirtual<X_LDR_DATA_TABLE_ENTRY*>(
|
||||||
|
hmodule);
|
||||||
|
if (ldr_data->load_count-- <= 0) {
|
||||||
|
// No more references, free it.
|
||||||
|
kernel_state->object_table()->RemoveHandle(module->handle());
|
||||||
|
}
|
||||||
|
|
||||||
SHIM_SET_RETURN_32(result);
|
SHIM_SET_RETURN_32(X_STATUS_SUCCESS);
|
||||||
}
|
}
|
||||||
|
|
||||||
SHIM_CALL XexGetProcedureAddress_shim(PPCContext* ppc_context,
|
SHIM_CALL XexGetProcedureAddress_shim(PPCContext* ppc_context,
|
||||||
KernelState* kernel_state) {
|
KernelState* kernel_state) {
|
||||||
uint32_t module_handle = SHIM_GET_ARG_32(0);
|
uint32_t hmodule = SHIM_GET_ARG_32(0);
|
||||||
uint32_t ordinal = SHIM_GET_ARG_32(1);
|
uint32_t ordinal = SHIM_GET_ARG_32(1);
|
||||||
uint32_t out_function_ptr = SHIM_GET_ARG_32(2);
|
uint32_t out_function_ptr = SHIM_GET_ARG_32(2);
|
||||||
|
|
||||||
|
@ -263,20 +278,20 @@ SHIM_CALL XexGetProcedureAddress_shim(PPCContext* ppc_context,
|
||||||
auto string_name = reinterpret_cast<const char*>(SHIM_MEM_ADDR(ordinal));
|
auto string_name = reinterpret_cast<const char*>(SHIM_MEM_ADDR(ordinal));
|
||||||
|
|
||||||
if (is_string_name) {
|
if (is_string_name) {
|
||||||
XELOGD("XexGetProcedureAddress(%.8X, %.8X(%s), %.8X)", module_handle,
|
XELOGD("XexGetProcedureAddress(%.8X, %.8X(%s), %.8X)", hmodule, ordinal,
|
||||||
ordinal, string_name, out_function_ptr);
|
string_name, out_function_ptr);
|
||||||
} else {
|
} else {
|
||||||
XELOGD("XexGetProcedureAddress(%.8X, %.8X, %.8X)", module_handle, ordinal,
|
XELOGD("XexGetProcedureAddress(%.8X, %.8X, %.8X)", hmodule, ordinal,
|
||||||
out_function_ptr);
|
out_function_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
X_STATUS result = X_STATUS_INVALID_HANDLE;
|
X_STATUS result = X_STATUS_INVALID_HANDLE;
|
||||||
|
|
||||||
object_ref<XModule> module;
|
object_ref<XModule> module;
|
||||||
if (!module_handle) {
|
if (!hmodule) {
|
||||||
module = kernel_state->GetExecutableModule();
|
module = kernel_state->GetExecutableModule();
|
||||||
} else {
|
} else {
|
||||||
module = kernel_state->object_table()->LookupObject<XModule>(module_handle);
|
module = XModule::GetFromHModule(kernel_state, SHIM_MEM_ADDR(hmodule));
|
||||||
}
|
}
|
||||||
if (module) {
|
if (module) {
|
||||||
uint32_t ptr;
|
uint32_t ptr;
|
||||||
|
|
|
@ -385,55 +385,22 @@ SHIM_CALL RtlImageXexHeaderField_shim(PPCContext* ppc_context,
|
||||||
uint32_t xex_header_base = SHIM_GET_ARG_32(0);
|
uint32_t xex_header_base = SHIM_GET_ARG_32(0);
|
||||||
uint32_t image_field = SHIM_GET_ARG_32(1);
|
uint32_t image_field = SHIM_GET_ARG_32(1);
|
||||||
|
|
||||||
// NOTE: this is totally faked!
|
|
||||||
// We set the XexExecutableModuleHandle pointer to a block that has at offset
|
|
||||||
// 0x58 a pointer to our XexHeaderBase. If the value passed doesn't match
|
|
||||||
// then die.
|
|
||||||
// The only ImageField I've seen in the wild is
|
|
||||||
// 0x20401 (XEX_HEADER_DEFAULT_HEAP_SIZE), so that's all we'll support.
|
|
||||||
|
|
||||||
XELOGD("RtlImageXexHeaderField(%.8X, %.8X)", xex_header_base, image_field);
|
XELOGD("RtlImageXexHeaderField(%.8X, %.8X)", xex_header_base, image_field);
|
||||||
|
|
||||||
// PVOID
|
auto header =
|
||||||
// PVOID XexHeaderBase
|
kernel_memory()->TranslateVirtual<xex2_header*>(xex_header_base);
|
||||||
// DWORD ImageField
|
if (!header) {
|
||||||
|
SHIM_SET_RETURN_32(X_STATUS_UNSUCCESSFUL);
|
||||||
// NOTE: this is totally faked!
|
|
||||||
// We set the XexExecutableModuleHandle pointer to a block that has at offset
|
|
||||||
// 0x58 a pointer to our XexHeaderBase. If the value passed doesn't match
|
|
||||||
// then die.
|
|
||||||
|
|
||||||
// TODO(benvanik): use xex_header_base to dereference this.
|
|
||||||
// Right now we are only concerned with games making this call on their main
|
|
||||||
// module, so this hack is fine.
|
|
||||||
assert_true(xex_header_base == 0x80101100);
|
|
||||||
auto module = kernel_state->GetExecutableModule();
|
|
||||||
|
|
||||||
// Special case.
|
|
||||||
if (image_field == XEX_HEADER_EXECUTION_INFO) {
|
|
||||||
SHIM_SET_RETURN_32(module->execution_info_ptr());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const xe_xex2_header_t* xex_header = module->xex_header();
|
uint8_t* hdr = xex2_get_opt_header(header, image_field);
|
||||||
for (size_t n = 0; n < xex_header->header_count; n++) {
|
if (!hdr) {
|
||||||
if (xex_header->headers[n].key == image_field) {
|
SHIM_SET_RETURN_32(X_STATUS_NOT_FOUND);
|
||||||
uint32_t value = xex_header->headers[n].value;
|
return;
|
||||||
SHIM_SET_RETURN_32(value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some games seem to expect 0xC0000225 for not-found results, while
|
SHIM_SET_RETURN_32((uint32_t)(hdr - kernel_memory()->virtual_membase()));
|
||||||
// others will explode if it's not zero. Maybe there are default headers?
|
|
||||||
switch (image_field) {
|
|
||||||
case 0x20401: // XEX_HEADER_DEFAULT_HEAP_SIZE
|
|
||||||
SHIM_SET_RETURN_32(0);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
SHIM_SET_RETURN_32(X_STATUS_NOT_FOUND);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unfortunately the Windows RTL_CRITICAL_SECTION object is bigger than the one
|
// Unfortunately the Windows RTL_CRITICAL_SECTION object is bigger than the one
|
||||||
|
|
|
@ -288,6 +288,28 @@ struct X_ANSI_STRING {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct X_UNICODE_STRING {
|
||||||
|
xe::be<uint16_t> length;
|
||||||
|
xe::be<uint16_t> maximum_length;
|
||||||
|
xe::be<uint32_t> pointer;
|
||||||
|
|
||||||
|
void reset() {
|
||||||
|
length = 0;
|
||||||
|
maximum_length = 0;
|
||||||
|
pointer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring to_string(uint8_t* membase) const {
|
||||||
|
if (!length) {
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::wstring(reinterpret_cast<const wchar_t*>(membase + pointer),
|
||||||
|
length);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
static_assert_size(X_UNICODE_STRING, 8);
|
||||||
|
|
||||||
// http://pastebin.com/SMypYikG
|
// http://pastebin.com/SMypYikG
|
||||||
typedef uint32_t XNotificationID;
|
typedef uint32_t XNotificationID;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue