Real modules and threads (mostly).

Need events/waiting to get proper launch thread behavior.
This commit is contained in:
Ben Vanik
2013-01-30 22:44:32 -08:00
parent 2ecacedaa6
commit 49af0dbc85
51 changed files with 1335 additions and 657 deletions

View File

@@ -19,17 +19,16 @@ using namespace xe::kernel;
using namespace xe::kernel::xam;
XamModule::XamModule(xe_pal_ref pal, xe_memory_ref memory,
shared_ptr<ExportResolver> resolver) :
KernelModule(pal, memory, resolver) {
resolver->RegisterTable(
XamModule::XamModule(Runtime* runtime) :
KernelModule(runtime) {
export_resolver_->RegisterTable(
"xam.xex", xam_export_table, XECOUNT(xam_export_table));
// Setup the xam state instance.
xam_state = auto_ptr<XamState>(new XamState(pal, memory, resolver));
xam_state = auto_ptr<XamState>(new XamState(pal_, memory_, export_resolver_));
// Register all exported functions.
RegisterInfoExports(resolver.get(), xam_state.get());
RegisterInfoExports(export_resolver_.get(), xam_state.get());
}
XamModule::~XamModule() {

View File

@@ -26,8 +26,7 @@ class XamState;
class XamModule : public KernelModule {
public:
XamModule(xe_pal_ref pal, xe_memory_ref memory,
shared_ptr<ExportResolver> resolver);
XamModule(Runtime* runtime);
virtual ~XamModule();
private:

View File

@@ -9,6 +9,8 @@
#include "kernel/modules/xboxkrnl/kernel_state.h"
#include "kernel/modules/xboxkrnl/xobject.h"
using namespace xe;
using namespace xe::kernel;
@@ -20,14 +22,88 @@ namespace {
}
KernelState::KernelState(xe_pal_ref pal, xe_memory_ref memory,
shared_ptr<ExportResolver> export_resolver) {
this->pal = xe_pal_retain(pal);
this->memory = xe_memory_retain(memory);
export_resolver_ = export_resolver;
KernelState::KernelState(Runtime* runtime) :
runtime_(runtime),
next_handle_(0) {
pal_ = runtime->pal();
memory_ = runtime->memory();
processor_ = runtime->processor();
objects_mutex_ = xe_mutex_alloc(0);
XEASSERTNOTNULL(objects_mutex_);
}
KernelState::~KernelState() {
xe_memory_release(memory);
xe_pal_release(pal);
// Delete all objects.
// We first copy the list to another list so that the deletion of the objects
// doesn't mess up iteration.
std::vector<XObject*> all_objects;
xe_mutex_lock(objects_mutex_);
for (std::tr1::unordered_map<X_HANDLE, XObject*>::iterator it =
objects_.begin(); it != objects_.end(); ++it) {
all_objects.push_back(it->second);
}
objects_.clear();
xe_mutex_unlock(objects_mutex_);
for (std::vector<XObject*>::iterator it = all_objects.begin();
it != all_objects.end(); ++it) {
// Perhaps call a special ForceRelease method or something?
XObject* obj = *it;
delete obj;
}
xe_mutex_free(objects_mutex_);
objects_mutex_ = NULL;
processor_.reset();
xe_memory_release(memory_);
xe_pal_release(pal_);
}
Runtime* KernelState::runtime() {
return runtime_;
}
xe_pal_ref KernelState::pal() {
return pal_;
}
xe_memory_ref KernelState::memory() {
return memory_;
}
cpu::Processor* KernelState::processor() {
return processor_.get();
}
// TODO(benvanik): invesitgate better handle storage/structure.
// A much better way of doing handles, if performance becomes an issue, would
// be to try to make the pointers 32bit. Then we could round-trip them through
// PPC code without needing to keep a map.
// To achieve this we could try doing allocs in the 32-bit address space via
// the OS alloc calls, or maybe create a section with a reserved size at load
// time (65k handles * 4 is more than enough?).
// We could then use a free list of handle IDs and allocate/release lock-free.
XObject* KernelState::GetObject(X_HANDLE handle) {
xe_mutex_lock(objects_mutex_);
std::tr1::unordered_map<X_HANDLE, XObject*>::iterator it =
objects_.find(handle);
XObject* value = it != objects_.end() ? it->second : NULL;
xe_mutex_unlock(objects_mutex_);
return value;
}
X_HANDLE KernelState::InsertObject(XObject* obj) {
xe_mutex_lock(objects_mutex_);
X_HANDLE handle = 0x00001000 + (++next_handle_);
objects_.insert(std::pair<X_HANDLE, XObject*>(handle, obj));
xe_mutex_unlock(objects_mutex_);
return handle;
}
void KernelState::RemoveObject(XObject* obj) {
xe_mutex_lock(objects_mutex_);
objects_.erase(obj->handle());
xe_mutex_unlock(objects_mutex_);
}

View File

@@ -15,6 +15,7 @@
#include <xenia/kernel/export.h>
#include <xenia/kernel/kernel_module.h>
#include <xenia/kernel/xbox.h>
namespace xe {
@@ -22,17 +23,35 @@ namespace kernel {
namespace xboxkrnl {
class XObject;
class KernelState {
public:
KernelState(xe_pal_ref pal, xe_memory_ref memory,
shared_ptr<ExportResolver> export_resolver);
KernelState(Runtime* runtime);
~KernelState();
xe_pal_ref pal;
xe_memory_ref memory;
XObject* GetObject(X_HANDLE handle);
Runtime* runtime();
xe_pal_ref pal();
xe_memory_ref memory();
cpu::Processor* processor();
private:
shared_ptr<ExportResolver> export_resolver_;
X_HANDLE InsertObject(XObject* obj);
void RemoveObject(XObject* obj);
Runtime* runtime_;
xe_pal_ref pal_;
xe_memory_ref memory_;
shared_ptr<cpu::Processor> processor_;
xe_mutex_t* objects_mutex_;
X_HANDLE next_handle_;
std::tr1::unordered_map<X_HANDLE, XObject*> objects_;
friend class XObject;
};

View File

@@ -10,6 +10,8 @@
#include "kernel/modules/xboxkrnl/module.h"
#include "kernel/modules/xboxkrnl/kernel_state.h"
#include "kernel/modules/xboxkrnl/objects/xmodule.h"
#include "kernel/modules/xboxkrnl/xboxkrnl_hal.h"
#include "kernel/modules/xboxkrnl/xboxkrnl_memory.h"
#include "kernel/modules/xboxkrnl/xboxkrnl_module.h"
@@ -29,27 +31,28 @@ namespace {
}
XboxkrnlModule::XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory,
shared_ptr<ExportResolver> resolver) :
KernelModule(pal, memory, resolver) {
XboxkrnlModule::XboxkrnlModule(Runtime* runtime) :
KernelModule(runtime) {
ExportResolver* resolver = export_resolver_.get();
resolver->RegisterTable(
"xboxkrnl.exe", xboxkrnl_export_table, XECOUNT(xboxkrnl_export_table));
// Setup the kernel state instance.
// This is where all kernel objects are kept while running.
kernel_state = auto_ptr<KernelState>(new KernelState(pal, memory, resolver));
kernel_state_ = auto_ptr<KernelState>(new KernelState(runtime));
// Register all exported functions.
RegisterHalExports(resolver.get(), kernel_state.get());
RegisterMemoryExports(resolver.get(), kernel_state.get());
RegisterModuleExports(resolver.get(), kernel_state.get());
RegisterRtlExports(resolver.get(), kernel_state.get());
RegisterThreadingExports(resolver.get(), kernel_state.get());
RegisterHalExports(resolver, kernel_state_.get());
RegisterMemoryExports(resolver, kernel_state_.get());
RegisterModuleExports(resolver, kernel_state_.get());
RegisterRtlExports(resolver, kernel_state_.get());
RegisterThreadingExports(resolver, kernel_state_.get());
// TODO(benvanik): alloc heap memory somewhere in user space
// TODO(benvanik): tools for reading/writing to heap memory
uint8_t* mem = xe_memory_addr(memory, 0);
uint8_t* mem = xe_memory_addr(memory_, 0);
// KeDebugMonitorData (?*)
// I'm not sure what this is for, but games make sure it's not null and
@@ -100,3 +103,42 @@ XboxkrnlModule::XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory,
XboxkrnlModule::~XboxkrnlModule() {
}
int XboxkrnlModule::LaunchModule(const xechar_t* path) {
// TODO(benvanik): setup the virtual filesystem map and use LoadFromFile.
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
if (!mmap) {
return NULL;
}
void* addr = xe_mmap_get_addr(mmap);
size_t length = xe_mmap_get_length(mmap);
char path_a[2048];
XEIGNORE(xestrnarrow(path_a, XECOUNT(path_a), path));
// Create and load the module.
XModule* module = new XModule(kernel_state_.get(), path_a);
X_STATUS status = module->LoadFromMemory(addr, length);
// TODO(benvanik): retain memory somehow? is it needed?
xe_mmap_release(mmap);
if (XFAILED(status)) {
XELOGE(XT("Failed to load module"));
module->Release();
return 1;
}
// Launch the module.
status = module->Launch(0);
if (XFAILED(status)) {
XELOGE(XT("Failed to launch module"));
module->Release();
return 2;
}
module->Release();
return 0;
}

View File

@@ -26,12 +26,13 @@ class KernelState;
class XboxkrnlModule : public KernelModule {
public:
XboxkrnlModule(xe_pal_ref pal, xe_memory_ref memory,
shared_ptr<ExportResolver> resolver);
XboxkrnlModule(Runtime* runtime);
virtual ~XboxkrnlModule();
int LaunchModule(const xechar_t* path);
private:
auto_ptr<KernelState> kernel_state;
auto_ptr<KernelState> kernel_state_;
};

View File

@@ -0,0 +1,7 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'xmodule.cc',
'xthread.cc',
],
}

View File

@@ -7,244 +7,124 @@
******************************************************************************
*/
#include <xenia/kernel/user_module.h>
#include "kernel/modules/xboxkrnl/objects/xmodule.h"
#include <third_party/pe/pe_image.h>
#include "kernel/modules/xboxkrnl/objects/xthread.h"
using namespace xe;
using namespace kernel;
using namespace xe::kernel;
using namespace xe::kernel::xboxkrnl;
UserModule::UserModule(xe_memory_ref memory) {
memory_ = xe_memory_retain(memory);
xex_ = NULL;
namespace {
}
UserModule::~UserModule() {
for (std::vector<PESection*>::iterator it = sections_.begin();
it != sections_.end(); ++it) {
delete *it;
}
xe_xex2_release(xex_);
xe_memory_release(memory_);
}
int UserModule::Load(const void* addr, const size_t length,
const xechar_t* path) {
XModule::XModule(KernelState* kernel_state, const char* path) :
XObject(kernel_state, kTypeModule),
xex_(NULL) {
XEIGNORE(xestrcpy(path_, XECOUNT(path_), path));
const xechar_t *slash = xestrrchr(path, '/');
if (slash) {
XEIGNORE(xestrcpy(name_, XECOUNT(name_), slash + 1));
}
xe_xex2_options_t xex_options;
xex_ = xe_xex2_load(memory_, addr, length, xex_options);
XEEXPECTNOTNULL(xex_);
XEEXPECTZERO(LoadPE());
return 0;
XECLEANUP:
return 1;
}
const xechar_t* UserModule::path() {
XModule::~XModule() {
xe_xex2_release(xex_);
}
const char* XModule::path() {
return path_;
}
const xechar_t* UserModule::name() {
const char* XModule::name() {
return name_;
}
uint32_t UserModule::handle() {
return handle_;
}
xe_xex2_ref UserModule::xex() {
xe_xex2_ref XModule::xex() {
return xe_xex2_retain(xex_);
}
const xe_xex2_header_t* UserModule::xex_header() {
const xe_xex2_header_t* XModule::xex_header() {
return xe_xex2_get_header(xex_);
}
void* UserModule::GetProcAddress(const uint32_t ordinal) {
X_STATUS XModule::LoadFromFile(const char* path) {
// TODO(benvanik): load from virtual filesystem.
XEASSERTALWAYS();
return NULL;
return X_STATUS_UNSUCCESSFUL;
}
// IMAGE_CE_RUNTIME_FUNCTION_ENTRY
// http://msdn.microsoft.com/en-us/library/ms879748.aspx
typedef struct IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY_t {
uint32_t FuncStart; // Virtual address
union {
struct {
uint32_t PrologLen : 8; // # of prolog instructions (size = x4)
uint32_t FuncLen : 22; // # of instructions total (size = x4)
uint32_t ThirtyTwoBit : 1; // Always 1
uint32_t ExceptionFlag : 1; // 1 if PDATA_EH in .text -- unknown if used
} Flags;
uint32_t FlagsValue; // To make byte swapping easier
};
} IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY;
X_STATUS XModule::LoadFromMemory(const void* addr, const size_t length) {
// Load the XEX into memory and decrypt.
xe_xex2_options_t xex_options;
xex_ = xe_xex2_load(kernel_state()->memory(), addr, length, xex_options);
XEEXPECTNOTNULL(xex_);
int UserModule::LoadPE() {
const xe_xex2_header_t* xex_header = xe_xex2_get_header(xex_);
uint8_t* mem = xe_memory_addr(memory_, 0);
const uint8_t* p = mem + xex_header->exe_address;
// Prepare the module for execution.
XEEXPECTZERO(kernel_state()->processor()->PrepareModule(
name_, path_, xex_, runtime()->export_resolver()));
// Verify DOS signature (MZ).
const IMAGE_DOS_HEADER* doshdr = (const IMAGE_DOS_HEADER*)p;
if (doshdr->e_magic != IMAGE_DOS_SIGNATURE) {
return 1;
}
// Move to the NT header offset from the DOS header.
p += doshdr->e_lfanew;
// Verify NT signature (PE\0\0).
const IMAGE_NT_HEADERS32* nthdr = (const IMAGE_NT_HEADERS32*)(p);
if (nthdr->Signature != IMAGE_NT_SIGNATURE) {
return 1;
}
// Verify matches an Xbox PE.
const IMAGE_FILE_HEADER* filehdr = &nthdr->FileHeader;
if ((filehdr->Machine != IMAGE_FILE_MACHINE_POWERPCBE) ||
!(filehdr->Characteristics & IMAGE_FILE_32BIT_MACHINE)) {
return 1;
}
// Verify the expected size.
if (filehdr->SizeOfOptionalHeader != IMAGE_SIZEOF_NT_OPTIONAL_HEADER) {
return 1;
}
// Verify optional header is 32bit.
const IMAGE_OPTIONAL_HEADER32* opthdr = &nthdr->OptionalHeader;
if (opthdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
return 1;
}
// Verify subsystem.
if (opthdr->Subsystem != IMAGE_SUBSYSTEM_XBOX) {
return 1;
}
// Linker version - likely 8+
// Could be useful for recognizing certain patterns
//opthdr->MajorLinkerVersion; opthdr->MinorLinkerVersion;
// Data directories of interest:
// EXPORT IMAGE_EXPORT_DIRECTORY
// IMPORT IMAGE_IMPORT_DESCRIPTOR[]
// EXCEPTION IMAGE_CE_RUNTIME_FUNCTION_ENTRY[]
// BASERELOC
// DEBUG IMAGE_DEBUG_DIRECTORY[]
// ARCHITECTURE /IMAGE_ARCHITECTURE_HEADER/ ----- import thunks!
// TLS IMAGE_TLS_DIRECTORY
// IAT Import Address Table ptr
//opthdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_X].VirtualAddress / .Size
// Quick scan to determine bounds of sections.
size_t upper_address = 0;
const IMAGE_SECTION_HEADER* sechdr = IMAGE_FIRST_SECTION(nthdr);
for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) {
const size_t physical_address = opthdr->ImageBase + sechdr->VirtualAddress;
upper_address =
MAX(upper_address, physical_address + sechdr->Misc.VirtualSize);
}
// Setup/load sections.
sechdr = IMAGE_FIRST_SECTION(nthdr);
for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) {
PESection* section = (PESection*)xe_calloc(sizeof(PESection));
xe_copy_memory(section->name, sizeof(section->name),
sechdr->Name, sizeof(sechdr->Name));
section->name[8] = 0;
section->raw_address = sechdr->PointerToRawData;
section->raw_size = sechdr->SizeOfRawData;
section->address = xex_header->exe_address + sechdr->VirtualAddress;
section->size = sechdr->Misc.VirtualSize;
section->flags = sechdr->Characteristics;
sections_.push_back(section);
}
//DumpTLSDirectory(pImageBase, pNTHeader, (PIMAGE_TLS_DIRECTORY32)0);
//DumpExportsSection(pImageBase, pNTHeader);
return 0;
}
PESection* UserModule::GetSection(const char *name) {
for (std::vector<PESection*>::iterator it = sections_.begin();
it != sections_.end(); ++it) {
if (!xestrcmpa((*it)->name, name)) {
return *it;
}
}
return NULL;
}
int UserModule::GetMethodHints(PEMethodInfo** out_method_infos,
size_t* out_method_info_count) {
uint8_t* mem = xe_memory_addr(memory_, 0);
*out_method_infos = NULL;
*out_method_info_count = 0;
const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY* entry = NULL;
// Find pdata, which contains the exception handling entries.
PESection* pdata = GetSection(".pdata");
if (!pdata) {
// No exception data to go on.
return 0;
}
// Resolve.
const uint8_t* p = mem + pdata->address;
// Entry count = pdata size / sizeof(entry).
size_t entry_count = pdata->size / sizeof(IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY);
if (!entry_count) {
// Empty?
return 0;
}
// Allocate output.
PEMethodInfo* method_infos = (PEMethodInfo*)xe_calloc(
entry_count * sizeof(PEMethodInfo));
XEEXPECTNOTNULL(method_infos);
// Parse entries.
// NOTE: entries are in memory as big endian, so pull them out and swap the
// values before using them.
entry = (const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY*)p;
IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY temp_entry;
for (size_t n = 0; n < entry_count; n++, entry++) {
PEMethodInfo* method_info = &method_infos[n];
method_info->address = XESWAP32BE(entry->FuncStart);
// The bitfield needs to be swapped by hand.
temp_entry.FlagsValue = XESWAP32BE(entry->FlagsValue);
method_info->total_length = temp_entry.Flags.FuncLen * 4;
method_info->prolog_length = temp_entry.Flags.PrologLen * 4;
}
*out_method_infos = method_infos;
*out_method_info_count = entry_count;
return 0;
return X_STATUS_SUCCESS;
XECLEANUP:
if (method_infos) {
xe_free(method_infos);
}
return 1;
return X_STATUS_UNSUCCESSFUL;
}
void UserModule::Dump(ExportResolver* export_resolver) {
//const uint8_t *mem = (const uint8_t*)xe_memory_addr(memory_, 0);
X_STATUS XModule::GetSection(const char* name,
uint32_t* out_data, uint32_t* out_size) {
const PESection* section = xe_xex2_get_pe_section(xex_, name);
if (!section) {
return X_STATUS_UNSUCCESSFUL;
}
*out_data = section->address;
*out_size = section->size;
return X_STATUS_SUCCESS;
}
void* XModule::GetProcAddressByOrdinal(uint16_t ordinal) {
// TODO(benvanik): check export tables.
XELOGE(XT("GetProcAddressByOrdinal not implemented"));
return NULL;
}
X_STATUS XModule::Launch(uint32_t flags) {
const xe_xex2_header_t* header = xex_header();
// TODO(benvanik): set as main module/etc
// xekXexExecutableModuleHandle = xe_module_get_handle(module);
// Create a thread to run in.
XThread* thread = new XThread(
kernel_state(),
header->exe_stack_size,
0,
header->exe_entry_point, NULL,
0);
X_STATUS result = thread->Create();
if (XFAILED(result)) {
XELOGE(XT("Could not create launch thread: %.8X"), result);
return result;
}
// Wait until thread completes.
// XLARGE_INTEGER timeout = XINFINITE;
// xekNtWaitForSingleObjectEx(thread_handle, TRUE, &timeout);
while (true) {
sleep(1);
}
thread->Release();
return X_STATUS_SUCCESS;
}
void XModule::Dump() {
ExportResolver* export_resolver = runtime()->export_resolver().get();
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
// XEX info.

View File

@@ -0,0 +1,62 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_XMODULE_H_
#define XENIA_KERNEL_MODULES_XBOXKRNL_XMODULE_H_
#include "kernel/modules/xboxkrnl/xobject.h"
#include <vector>
#include <xenia/kernel/export.h>
#include <xenia/kernel/xbox.h>
#include <xenia/kernel/xex2.h>
namespace xe {
namespace kernel {
namespace xboxkrnl {
class XModule : public XObject {
public:
XModule(KernelState* kernel_state, const char* path);
virtual ~XModule();
const char* path();
const char* name();
xe_xex2_ref xex();
const xe_xex2_header_t* xex_header();
X_STATUS LoadFromFile(const char* path);
X_STATUS LoadFromMemory(const void* addr, const size_t length);
X_STATUS GetSection(const char* name, uint32_t* out_data, uint32_t* out_size);
void* GetProcAddressByOrdinal(uint16_t ordinal);
X_STATUS Launch(uint32_t flags);
void Dump();
private:
int LoadPE();
char name_[256];
char path_[2048];
xe_xex2_ref xex_;
};
} // namespace xboxkrnl
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_XMODULE_H_

View File

@@ -0,0 +1,232 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "kernel/modules/xboxkrnl/objects/xthread.h"
using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::xboxkrnl;
namespace {
static uint32_t next_xthread_id = 0;
}
XThread::XThread(KernelState* kernel_state,
uint32_t stack_size,
uint32_t xapi_thread_startup,
uint32_t start_address, uint32_t start_context,
uint32_t creation_flags) :
XObject(kernel_state, kTypeThread),
thread_id_(++next_xthread_id),
thread_handle_(0),
thread_state_address_(0),
processor_state_(0) {
creation_params_.stack_size = stack_size;
creation_params_.xapi_thread_startup = xapi_thread_startup;
creation_params_.start_address = start_address;
creation_params_.start_context = start_context;
creation_params_.creation_flags = creation_flags;
// Adjust stack size - min of 16k.
if (creation_params_.stack_size < 16 * 1024 * 1024) {
creation_params_.stack_size = 16 * 1024 * 1024;
}
}
XThread::~XThread() {
// TODO(benvanik): if executing, kill it?
if (processor_state_) {
kernel_state()->processor()->DeallocThread(processor_state_);
}
if (thread_state_address_) {
xe_memory_heap_free(kernel_state()->memory(), thread_state_address_, 0);
}
if (thread_handle_) {
// TODO(benvanik): platform kill
XELOGE(XT("Thread disposed without exiting"));
}
}
uint32_t XThread::thread_id() {
return thread_id_;
}
uint32_t XThread::last_error() {
uint8_t *p = xe_memory_addr(memory(), thread_state_address_);
return XEGETUINT32BE(p + 0x160);
}
void XThread::set_last_error(uint32_t error_code) {
uint8_t *p = xe_memory_addr(memory(), thread_state_address_);
XESETUINT32BE(p + 0x160, error_code);
}
X_STATUS XThread::Create() {
// Allocate thread state block from heap.
// This is set as r13 for user code and some special inlined Win32 calls
// (like GetLastError/etc) will poke it directly.
// We try to use it as our primary store of data just to keep things all
// consistent.
thread_state_address_ = xe_memory_heap_alloc(memory(), 0, 2048, 0);
if (!thread_state_address_) {
XELOGW(XT("Unable to allocate thread state block"));
return X_STATUS_NO_MEMORY;
}
// Setup the thread state block (last error/etc).
// 0x100: pointer to self?
// 0x14C: thread id
// 0x150: if >0 then error states don't get set
// 0x160: last error
// So, at offset 0x100 we have a 4b pointer to offset 200, then have the
// structure.
uint8_t *p = xe_memory_addr(memory(), thread_state_address_);
XESETUINT32BE(p + 0x100, thread_state_address_);
XESETUINT32BE(p + 0x14C, thread_id_);
XESETUINT32BE(p + 0x150, 0); // ?
XESETUINT32BE(p + 0x160, 0); // last error
// Allocate processor thread state.
// This is thread safe.
processor_state_ = kernel_state()->processor()->AllocThread(
creation_params_.stack_size, thread_state_address_);
if (!processor_state_) {
XELOGW(XT("Unable to allocate processor thread state"));
return X_STATUS_NO_MEMORY;
}
X_STATUS return_code = PlatformCreate();
if (XFAILED(return_code)) {
XELOGW(XT("Unable to create platform thread (%.8X)"), return_code);
return return_code;
}
return X_STATUS_SUCCESS;
}
X_STATUS XThread::Exit(int exit_code) {
// TODO(benvanik): set exit code in thread state block
// TODO(benvanik); dispatch events? waiters? etc?
// NOTE: unless PlatformExit fails, expect it to never return!
X_STATUS return_code = PlatformExit(exit_code);
if (XFAILED(return_code)) {
return return_code;
}
return X_STATUS_SUCCESS;
}
#if XE_PLATFORM(WIN32)
static uint32_t __stdcall XThreadStartCallbackWin32(void* param) {
XThread* thread = reinterpret_cast<XThread*>(param);
thread->Execute();
thread->Release();
return 0;
}
X_STATUS XThread::PlatformCreate() {
XEASSERTALWAYS();
thread_handle_ = CreateThread(
NULL,
creation_params_.stack_size,
(LPTHREAD_START_ROUTINE)XThreadStartCallbackWin32,
this,
creation_params.creation_flags,
NULL);
if (!handle) {
uint32_t last_error = GetLastError();
// TODO(benvanik): translate?
XELOGE(XT("CreateThread failed with %d"), last_error);
return last_error;
}
return X_STATUS_SUCCESS;
}
X_STATUS XThread::PlatformExit(int exit_code) {
// NOTE: does not return.
ExitThread(exit_code);
return X_STATUS_SUCCESS;
}
#else
static void* XThreadStartCallbackPthreads(void* param) {
XThread* thread = reinterpret_cast<XThread*>(param);
thread->Execute();
thread->Release();
return 0;
}
X_STATUS XThread::PlatformCreate() {
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, creation_params_.stack_size);
int result_code;
if (creation_params_.creation_flags & X_CREATE_SUSPENDED) {
result_code = pthread_create_suspended_np(
reinterpret_cast<pthread_t*>(&thread_handle_),
&attr,
&XThreadStartCallbackPthreads,
this);
} else {
result_code = pthread_create(
reinterpret_cast<pthread_t*>(&thread_handle_),
&attr,
&XThreadStartCallbackPthreads,
this);
}
pthread_attr_destroy(&attr);
switch (result_code) {
case 0:
// Succeeded!
return X_STATUS_SUCCESS;
default:
case EAGAIN:
return X_STATUS_NO_MEMORY;
case EINVAL:
case EPERM:
return X_STATUS_INVALID_PARAMETER;
}
}
X_STATUS XThread::PlatformExit(int exit_code) {
// NOTE: does not return.
pthread_exit((void*)exit_code);
return X_STATUS_SUCCESS;
}
#endif // WIN32
void XThread::Execute() {
// Run XapiThreadStartup first, if present.
if (creation_params_.xapi_thread_startup) {
XELOGE(XT("xapi_thread_startup not implemented"));
}
// Run user code.
int exit_code = kernel_state()->processor()->Execute(
processor_state_,
creation_params_.start_address, creation_params_.start_context);
// If we got here it means the execute completed without an exit being called.
// Treat the return code as an implicit exit code.
Exit(exit_code);
}

View File

@@ -0,0 +1,65 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_XTHREAD_H_
#define XENIA_KERNEL_MODULES_XBOXKRNL_XTHREAD_H_
#include "kernel/modules/xboxkrnl/xobject.h"
#include <xenia/kernel/xbox.h>
namespace xe {
namespace kernel {
namespace xboxkrnl {
class XThread : public XObject {
public:
XThread(KernelState* kernel_state,
uint32_t stack_size,
uint32_t xapi_thread_startup,
uint32_t start_address, uint32_t start_context,
uint32_t creation_flags);
virtual ~XThread();
uint32_t thread_id();
uint32_t last_error();
void set_last_error(uint32_t error_code);
X_STATUS Create();
X_STATUS Exit(int exit_code);
void Execute();
private:
X_STATUS PlatformCreate();
X_STATUS PlatformExit(int exit_code);
struct {
uint32_t stack_size;
uint32_t xapi_thread_startup;
uint32_t start_address;
uint32_t start_context;
uint32_t creation_flags;
} creation_params_;
uint32_t thread_id_;
void* thread_handle_;
uint32_t thread_state_address_;
cpu::ThreadState* processor_state_;
};
} // namespace xboxkrnl
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_XTHREAD_H_

View File

@@ -8,5 +8,10 @@
'xboxkrnl_module.cc',
'xboxkrnl_rtl.cc',
'xboxkrnl_threading.cc',
'xobject.cc',
],
'includes': [
'objects/sources.gypi',
],
}

View File

@@ -1,87 +0,0 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_H_
#define XENIA_KERNEL_MODULES_XBOXKRNL_H_
#include <xenia/common.h>
#include <xenia/core.h>
namespace xe {
namespace kernel {
namespace xboxkrnl {
typedef uint32_t X_HANDLE;
#define X_INVALID_HANDLE_VALUE ((X_HANDLE)-1)
// NT_STATUS (STATUS_*)
// http://msdn.microsoft.com/en-us/library/cc704588.aspx
// Adding as needed.
#define X_STATUS_SUCCESS ((uint32_t)0x00000000L)
#define X_STATUS_UNSUCCESSFUL ((uint32_t)0xC0000001L)
#define X_STATUS_NOT_IMPLEMENTED ((uint32_t)0xC0000002L)
#define X_STATUS_ACCESS_VIOLATION ((uint32_t)0xC0000005L)
#define X_STATUS_INVALID_HANDLE ((uint32_t)0xC0000008L)
#define X_STATUS_INVALID_PARAMETER ((uint32_t)0xC000000DL)
#define X_STATUS_NO_MEMORY ((uint32_t)0xC0000017L)
#define X_STATUS_ALREADY_COMMITTED ((uint32_t)0xC0000021L)
#define X_STATUS_ACCESS_DENIED ((uint32_t)0xC0000022L)
#define X_STATUS_BUFFER_TOO_SMALL ((uint32_t)0xC0000023L)
#define X_STATUS_OBJECT_TYPE_MISMATCH ((uint32_t)0xC0000024L)
#define X_STATUS_INVALID_PAGE_PROTECTION ((uint32_t)0xC0000045L)
// MEM_*, used by NtAllocateVirtualMemory
#define X_MEM_COMMIT 0x00001000
#define X_MEM_RESERVE 0x00002000
#define X_MEM_DECOMMIT 0x00004000
#define X_MEM_RELEASE 0x00008000
#define X_MEM_FREE 0x00010000
#define X_MEM_PRIVATE 0x00020000
#define X_MEM_RESET 0x00080000
#define X_MEM_TOP_DOWN 0x00100000
#define X_MEM_NOZERO 0x00800000
#define X_MEM_LARGE_PAGES 0x20000000
#define X_MEM_HEAP 0x40000000
#define X_MEM_16MB_PAGES 0x80000000 // from Valve SDK
// PAGE_*, used by NtAllocateVirtualMemory
#define X_PAGE_NOACCESS 0x00000001
#define X_PAGE_READONLY 0x00000002
#define X_PAGE_READWRITE 0x00000004
#define X_PAGE_WRITECOPY 0x00000008
#define X_PAGE_EXECUTE 0x00000010
#define X_PAGE_EXECUTE_READ 0x00000020
#define X_PAGE_EXECUTE_READWRITE 0x00000040
#define X_PAGE_EXECUTE_WRITECOPY 0x00000080
#define X_PAGE_GUARD 0x00000100
#define X_PAGE_NOCACHE 0x00000200
#define X_PAGE_WRITECOMBINE 0x00000400
// (?), used by KeGetCurrentProcessType
#define X_PROCTYPE_IDLE 0
#define X_PROCTYPE_USER 1
#define X_PROCTYPE_SYSTEM 2
// TLS specials.
#define X_TLS_OUT_OF_INDEXES UINT32_MAX // (-1)
} // namespace xboxkrnl
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_H_

View File

@@ -9,8 +9,9 @@
#include "kernel/modules/xboxkrnl/xboxkrnl_hal.h"
#include <xenia/kernel/xbox.h>
#include "kernel/shim_utils.h"
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
using namespace xe;

View File

@@ -9,8 +9,9 @@
#include "kernel/modules/xboxkrnl/xboxkrnl_memory.h"
#include <xenia/kernel/xbox.h>
#include "kernel/shim_utils.h"
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
using namespace xe;
@@ -82,7 +83,7 @@ void NtAllocateVirtualMemory_shim(
// Allocate.
uint32_t flags = 0;
uint32_t addr = xe_memory_heap_alloc(
state->memory, base_addr_value, adjusted_size, flags);
state->memory(), base_addr_value, adjusted_size, flags);
if (!addr) {
// Failed - assume no memory available.
SHIM_SET_RETURN(X_STATUS_NO_MEMORY);
@@ -123,7 +124,7 @@ void NtFreeVirtualMemory_shim(
// Free.
uint32_t flags = 0;
uint32_t freed_size = xe_memory_heap_free(state->memory, base_addr_value,
uint32_t freed_size = xe_memory_heap_free(state->memory(), base_addr_value,
flags);
if (!freed_size) {
SHIM_SET_RETURN(X_STATUS_UNSUCCESSFUL);

View File

@@ -9,10 +9,10 @@
#include "kernel/modules/xboxkrnl/xboxkrnl_module.h"
#include <xenia/kernel/xbox.h>
#include <xenia/kernel/xex2.h>
#include "kernel/shim_utils.h"
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
using namespace xe;

View File

@@ -9,10 +9,10 @@
#include "kernel/modules/xboxkrnl/xboxkrnl_rtl.h"
#include <xenia/kernel/xbox.h>
#include <xenia/kernel/xex2.h>
#include "kernel/shim_utils.h"
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
using namespace xe;

View File

@@ -9,8 +9,10 @@
#include "kernel/modules/xboxkrnl/xboxkrnl_threading.h"
#include <xenia/kernel/xbox.h>
#include "kernel/shim_utils.h"
#include "kernel/modules/xboxkrnl/xboxkrnl.h"
#include "kernel/modules/xboxkrnl/objects/xthread.h"
using namespace xe;
@@ -54,19 +56,55 @@ namespace {
// }
void ExCreateThread() {
// launch native thread
//
}
void ExCreateThread_shim(
xe_ppc_state_t* ppc_state, KernelState* state) {
// DWORD
// LPHANDLE Handle,
// DWORD StackSize,
// LPDWORD ThreadId,
// LPVOID XapiThreadStartup, ?? often 0
// LPVOID StartAddress,
// LPVOID StartContext,
// DWORD CreationFlags // 0x80?
uint32_t handle_ptr = SHIM_GET_ARG_32(0);
uint32_t stack_size = SHIM_GET_ARG_32(1);
uint32_t thread_id_ptr = SHIM_GET_ARG_32(2);
uint32_t xapi_thread_startup = SHIM_GET_ARG_32(3);
uint32_t start_address = SHIM_GET_ARG_32(4);
uint32_t start_context = SHIM_GET_ARG_32(5);
uint32_t creation_flags = SHIM_GET_ARG_32(6);
XELOGD(
XT("ExCreateThread()"));
XT("ExCreateThread(%.8X, %d, %.8X, %.8X, %.8X, %.8X, %.8X)"),
handle_ptr,
stack_size,
thread_id_ptr,
xapi_thread_startup,
start_address,
start_context,
creation_flags);
SHIM_SET_RETURN(0);
XThread* thread = new XThread(
state, stack_size, xapi_thread_startup, start_address, start_context,
creation_flags);
X_STATUS result_code = thread->Create();
if (XFAILED(result_code)) {
// Failed!
thread->Release();
XELOGE(XT("Thread creation failed: %.8X"), result_code);
SHIM_SET_RETURN(result_code);
return;
}
if (handle_ptr) {
SHIM_SET_MEM_32(handle_ptr, thread->handle());
}
if (thread_id_ptr) {
SHIM_SET_MEM_32(thread_id_ptr, thread->thread_id());
}
SHIM_SET_RETURN(result_code);
}
@@ -205,7 +243,7 @@ void xe::kernel::xboxkrnl::RegisterThreadingExports(
export_resolver->SetFunctionMapping("xboxkrnl.exe", ordinal, \
state, (xe_kernel_export_shim_fn)shim, (xe_kernel_export_impl_fn)impl)
SHIM_SET_MAPPING(0x0000000D, ExCreateThread_shim, ExCreateThread);
SHIM_SET_MAPPING(0x0000000D, ExCreateThread_shim, NULL);
SHIM_SET_MAPPING(0x00000066, KeGetCurrentProcessType_shim, NULL);

View File

@@ -0,0 +1,62 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include "kernel/modules/xboxkrnl/xobject.h"
using namespace xe;
using namespace xe::kernel;
using namespace xe::kernel::xboxkrnl;
XObject::XObject(KernelState* kernel_state, Type type) :
kernel_state_(kernel_state),
ref_count_(1),
type_(type), handle_(X_INVALID_HANDLE_VALUE) {
handle_ = kernel_state->InsertObject(this);
}
XObject::~XObject() {
XEASSERTZERO(ref_count_);
if (handle_ != X_INVALID_HANDLE_VALUE) {
// Remove from state table.
kernel_state_->RemoveObject(this);
}
}
Runtime* XObject::runtime() {
return kernel_state_->runtime_;
}
KernelState* XObject::kernel_state() {
return kernel_state_;
}
xe_memory_ref XObject::memory() {
return kernel_state_->memory();
}
XObject::Type XObject::type() {
return type_;
}
X_HANDLE XObject::handle() {
return handle_;
}
void XObject::Retain() {
xe_atomic_inc_32(&ref_count_);
}
void XObject::Release() {
if (!xe_atomic_dec_32(&ref_count_)) {
delete this;
}
}

View File

@@ -0,0 +1,67 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_XOBJECT_H_
#define XENIA_KERNEL_MODULES_XBOXKRNL_XOBJECT_H_
#include "kernel/modules/xboxkrnl/kernel_state.h"
#include <xenia/kernel/xbox.h>
namespace xe {
namespace kernel {
class Runtime;
}
}
namespace xe {
namespace kernel {
namespace xboxkrnl {
class XObject {
public:
enum Type {
kTypeModule = 0x00000001,
kTypeThread = 0x00000002,
};
XObject(KernelState* kernel_state, Type type);
virtual ~XObject();
KernelState* kernel_state();
Type type();
X_HANDLE handle();
void Retain();
void Release();
protected:
Runtime* runtime();
xe_memory_ref memory(); // unretained
private:
KernelState* kernel_state_;
volatile int32_t ref_count_;
Type type_;
X_HANDLE handle_;
};
} // namespace xboxkrnl
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_XOBJECT_H_

View File

@@ -25,22 +25,13 @@ Runtime::Runtime(xe_pal_ref pal, shared_ptr<cpu::Processor> processor,
XEIGNORE(xestrcpy(command_line_, XECOUNT(command_line_), command_line));
export_resolver_ = shared_ptr<ExportResolver>(new ExportResolver());
kernel_modules_.push_back(
new xboxkrnl::XboxkrnlModule(pal_, memory_, export_resolver_));
kernel_modules_.push_back(
new xam::XamModule(pal_, memory_, export_resolver_));
xboxkrnl_ = auto_ptr<xboxkrnl::XboxkrnlModule>(
new xboxkrnl::XboxkrnlModule(this));
xam_ = auto_ptr<xam::XamModule>(
new xam::XamModule(this));
}
Runtime::~Runtime() {
for (std::map<const xechar_t*, UserModule*>::iterator it =
user_modules_.begin(); it != user_modules_.end(); ++it) {
delete it->second;
}
for (std::vector<KernelModule*>::iterator it = kernel_modules_.begin();
it != kernel_modules_.end(); ++it) {
delete *it;
}
xe_memory_release(memory_);
xe_pal_release(pal_);
}
@@ -65,117 +56,6 @@ const xechar_t* Runtime::command_line() {
return command_line_;
}
int Runtime::LoadBinaryModule(const xechar_t* path, uint32_t start_address) {
const xechar_t* name = xestrrchr(path, '/') + 1;
// TODO(benvanik): map file from filesystem
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
if (!mmap) {
return NULL;
}
void* addr = xe_mmap_get_addr(mmap);
size_t length = xe_mmap_get_length(mmap);
int result_code = 1;
XEEXPECTZERO(xe_copy_memory(xe_memory_addr(memory_, start_address),
xe_memory_get_length(memory_),
addr, length));
// Prepare the module.
char name_a[2048];
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
char path_a[2048];
XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), path));
XEEXPECTZERO(processor_->PrepareModule(
name_a, path_a, start_address, start_address + length,
export_resolver_));
result_code = 0;
XECLEANUP:
xe_mmap_release(mmap);
return result_code;
}
int Runtime::LoadModule(const xechar_t* path) {
if (GetModule(path)) {
return 0;
}
// TODO(benvanik): map file from filesystem
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
if (!mmap) {
return NULL;
}
void* addr = xe_mmap_get_addr(mmap);
size_t length = xe_mmap_get_length(mmap);
UserModule* module = new UserModule(memory_);
int result_code = module->Load(addr, length, path);
// TODO(benvanik): retain memory somehow? is it needed?
xe_mmap_release(mmap);
if (result_code) {
delete module;
return 1;
}
// Prepare the module.
XEEXPECTZERO(processor_->PrepareModule(module, export_resolver_));
// Stash in modules list (takes reference).
user_modules_.insert(std::pair<const xechar_t*, UserModule*>(path, module));
return 0;
XECLEANUP:
delete module;
return 1;
}
void Runtime::LaunchModule(UserModule* user_module) {
const xe_xex2_header_t *xex_header = user_module->xex_header();
// TODO(benvanik): set as main module/etc
// xekXexExecutableModuleHandle = xe_module_get_handle(module);
// Setup the heap (and TLS?).
// xex_header->exe_heap_size;
// Launch thread.
// XHANDLE thread_handle;
// XDWORD thread_id;
// XBOOL result = xekExCreateThread(&thread_handle, xex_header->exe_stack_size, &thread_id, NULL, (void*)xex_header->exe_entry_point, NULL, 0);
// Wait until thread completes.
// XLARGE_INTEGER timeout = XINFINITE;
// xekNtWaitForSingleObjectEx(thread_handle, TRUE, &timeout);
// Simulate a thread.
uint32_t stack_size = xex_header->exe_stack_size;
if (stack_size < 16 * 1024 * 1024) {
stack_size = 16 * 1024 * 1024;
}
ThreadState* thread_state = processor_->AllocThread(
0x80000000, stack_size);
// Execute test.
processor_->Execute(thread_state, xex_header->exe_entry_point);
processor_->DeallocThread(thread_state);
}
UserModule* Runtime::GetModule(const xechar_t* path) {
std::map<const xechar_t*, UserModule*>::iterator it =
user_modules_.find(path);
if (it != user_modules_.end()) {
return it->second;
}
return NULL;
}
void Runtime::UnloadModule(UserModule* user_module) {
// TODO(benvanik): unload module
XEASSERTALWAYS();
int Runtime::LaunchModule(const xechar_t* path) {
return xboxkrnl_->LaunchModule(path);
}

View File

@@ -3,7 +3,6 @@
'sources': [
'export.cc',
'runtime.cc',
'user_module.cc',
'xex2.cc',
],

View File

@@ -9,11 +9,14 @@
#include <xenia/kernel/xex2.h>
#include <vector>
#include <third_party/crypto/rijndael-alg-fst.h>
#include <third_party/crypto/rijndael-alg-fst.c>
#include <third_party/mspack/lzx.h>
#include <third_party/mspack/lzxd.c>
#include <third_party/mspack/mspack.h>
#include <third_party/pe/pe_image.h>
typedef struct xe_xex2 {
@@ -22,6 +25,8 @@ typedef struct xe_xex2 {
xe_memory_ref memory;
xe_xex2_header_t header;
std::vector<PESection*>* sections;
} xe_xex2_t;
@@ -31,6 +36,7 @@ int xe_xex2_decrypt_key(xe_xex2_header_t *header);
int xe_xex2_read_image(xe_xex2_ref xex,
const uint8_t *xex_addr, const size_t xex_length,
xe_memory_ref memory);
int xe_xex2_load_pe(xe_xex2_ref xex);
xe_xex2_ref xe_xex2_load(xe_memory_ref memory,
@@ -40,6 +46,7 @@ xe_xex2_ref xe_xex2_load(xe_memory_ref memory,
xe_ref_init((xe_ref)xex);
xex->memory = xe_memory_retain(memory);
xex->sections = new std::vector<PESection*>();
XEEXPECTZERO(xe_xex2_read_header((const uint8_t*)addr, length, &xex->header));
@@ -47,6 +54,8 @@ xe_xex2_ref xe_xex2_load(xe_memory_ref memory,
XEEXPECTZERO(xe_xex2_read_image(xex, (const uint8_t*)addr, length, memory));
XEEXPECTZERO(xe_xex2_load_pe(xex));
return xex;
XECLEANUP:
@@ -55,6 +64,11 @@ XECLEANUP:
}
void xe_xex2_dealloc(xe_xex2_ref xex) {
for (std::vector<PESection*>::iterator it = xex->sections->begin();
it != xex->sections->end(); ++it) {
delete *it;
}
xe_xex2_header_t *header = &xex->header;
xe_free(header->sections);
if (header->file_format_info.compression_type == XEX_COMPRESSION_BASIC) {
@@ -743,6 +757,100 @@ int xe_xex2_read_image(xe_xex2_ref xex, const uint8_t *xex_addr,
}
}
int xe_xex2_load_pe(xe_xex2_ref xex) {
const xe_xex2_header_t* header = &xex->header;
const uint8_t* p = xe_memory_addr(xex->memory, header->exe_address);
// Verify DOS signature (MZ).
const IMAGE_DOS_HEADER* doshdr = (const IMAGE_DOS_HEADER*)p;
if (doshdr->e_magic != IMAGE_DOS_SIGNATURE) {
return 1;
}
// Move to the NT header offset from the DOS header.
p += doshdr->e_lfanew;
// Verify NT signature (PE\0\0).
const IMAGE_NT_HEADERS32* nthdr = (const IMAGE_NT_HEADERS32*)(p);
if (nthdr->Signature != IMAGE_NT_SIGNATURE) {
return 1;
}
// Verify matches an Xbox PE.
const IMAGE_FILE_HEADER* filehdr = &nthdr->FileHeader;
if ((filehdr->Machine != IMAGE_FILE_MACHINE_POWERPCBE) ||
!(filehdr->Characteristics & IMAGE_FILE_32BIT_MACHINE)) {
return 1;
}
// Verify the expected size.
if (filehdr->SizeOfOptionalHeader != IMAGE_SIZEOF_NT_OPTIONAL_HEADER) {
return 1;
}
// Verify optional header is 32bit.
const IMAGE_OPTIONAL_HEADER32* opthdr = &nthdr->OptionalHeader;
if (opthdr->Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC) {
return 1;
}
// Verify subsystem.
if (opthdr->Subsystem != IMAGE_SUBSYSTEM_XBOX) {
return 1;
}
// Linker version - likely 8+
// Could be useful for recognizing certain patterns
//opthdr->MajorLinkerVersion; opthdr->MinorLinkerVersion;
// Data directories of interest:
// EXPORT IMAGE_EXPORT_DIRECTORY
// IMPORT IMAGE_IMPORT_DESCRIPTOR[]
// EXCEPTION IMAGE_CE_RUNTIME_FUNCTION_ENTRY[]
// BASERELOC
// DEBUG IMAGE_DEBUG_DIRECTORY[]
// ARCHITECTURE /IMAGE_ARCHITECTURE_HEADER/ ----- import thunks!
// TLS IMAGE_TLS_DIRECTORY
// IAT Import Address Table ptr
//opthdr->DataDirectory[IMAGE_DIRECTORY_ENTRY_X].VirtualAddress / .Size
// Quick scan to determine bounds of sections.
size_t upper_address = 0;
const IMAGE_SECTION_HEADER* sechdr = IMAGE_FIRST_SECTION(nthdr);
for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) {
const size_t physical_address = opthdr->ImageBase + sechdr->VirtualAddress;
upper_address =
MAX(upper_address, physical_address + sechdr->Misc.VirtualSize);
}
// Setup/load sections.
sechdr = IMAGE_FIRST_SECTION(nthdr);
for (size_t n = 0; n < filehdr->NumberOfSections; n++, sechdr++) {
PESection* section = (PESection*)xe_calloc(sizeof(PESection));
xe_copy_memory(section->name, sizeof(section->name),
sechdr->Name, sizeof(sechdr->Name));
section->name[8] = 0;
section->raw_address = sechdr->PointerToRawData;
section->raw_size = sechdr->SizeOfRawData;
section->address = header->exe_address + sechdr->VirtualAddress;
section->size = sechdr->Misc.VirtualSize;
section->flags = sechdr->Characteristics;
xex->sections->push_back(section);
}
//DumpTLSDirectory(pImageBase, pNTHeader, (PIMAGE_TLS_DIRECTORY32)0);
//DumpExportsSection(pImageBase, pNTHeader);
return 0;
}
const PESection* xe_xex2_get_pe_section(xe_xex2_ref xex, const char* name) {
for (std::vector<PESection*>::iterator it = xex->sections->begin();
it != xex->sections->end(); ++it) {
if (!xestrcmpa((*it)->name, name)) {
return *it;
}
}
return NULL;
}
int xe_xex2_get_import_infos(xe_xex2_ref xex,
const xe_xex2_import_library_t *library,
xe_xex2_import_info_t **out_import_infos,