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

@ -7,10 +7,10 @@ case "$*" in
(*--debug*) CONFIG=debug;;
esac
EXEC=$DIR/../build/xenia/$CONFIG/xenia-info
EXEC=$DIR/../build/xenia/$CONFIG/xenia-run
if [ ! -f "$EXEC" ]; then
python $DIR/../xenia-build.py build --$CONFIG
fi
$EXEC "$@"
$EXEC --abort_before_entry=true "$@"

View File

@ -12,7 +12,7 @@
# LLVM paths.
# TODO(benvanik): switch based on configuration.
'llvm_path': 'build/llvm/release/',
'llvm_path': 'build/llvm/debug/',
'llvm_config': '<(llvm_path)bin/llvm-config',
'llvm_includedir': '<(llvm_path)/include',
'llvm_cxxflags': [

View File

@ -15,6 +15,7 @@
#include <xenia/core/file.h>
#include <xenia/core/memory.h>
#include <xenia/core/mmap.h>
#include <xenia/core/mutex.h>
#include <xenia/core/pal.h>
#include <xenia/core/ref.h>

View File

@ -0,0 +1,27 @@
/**
******************************************************************************
* 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_CORE_MUTEX_H_
#define XENIA_CORE_MUTEX_H_
#include <xenia/common.h>
typedef struct xe_mutex xe_mutex_t;
xe_mutex_t* xe_mutex_alloc(uint32_t spin_count);
void xe_mutex_free(xe_mutex_t* mutex);
int xe_mutex_lock(xe_mutex_t* mutex);
int xe_mutex_trylock(xe_mutex_t* mutex);
int xe_mutex_unlock(xe_mutex_t* mutex);
#endif // XENIA_CORE_MUTEX_H_

View File

@ -16,7 +16,6 @@
#include <xenia/cpu/sdb.h>
#include <xenia/core/memory.h>
#include <xenia/kernel/export.h>
#include <xenia/kernel/user_module.h>
namespace llvm {

View File

@ -15,7 +15,7 @@
#include <xenia/cpu/sdb.h>
#include <xenia/kernel/export.h>
#include <xenia/kernel/user_module.h>
#include <xenia/kernel/xex2.h>
namespace llvm {
@ -49,7 +49,7 @@ public:
shared_ptr<llvm::ExecutionEngine>& engine);
~ExecModule();
int PrepareUserModule(kernel::UserModule* user_module);
int PrepareXex(xe_xex2_ref xex);
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
void AddFunctionsToMap(FunctionMap& map);

View File

@ -17,7 +17,7 @@
#include <xenia/cpu/exec_module.h>
#include <xenia/cpu/thread_state.h>
#include <xenia/kernel/export.h>
#include <xenia/kernel/user_module.h>
#include <xenia/kernel/xex2.h>
namespace llvm {
@ -40,17 +40,18 @@ public:
int Setup();
int PrepareModule(const char* module_name, const char* module_path,
uint32_t start_address, uint32_t end_address,
int LoadBinary(const xechar_t* path, uint32_t start_address,
shared_ptr<kernel::ExportResolver> export_resolver);
int PrepareModule(kernel::UserModule* user_module,
int PrepareModule(const char* name, const char* path, xe_xex2_ref xex,
shared_ptr<kernel::ExportResolver> export_resolver);
uint32_t CreateCallback(void (*callback)(void* data), void* data);
ThreadState* AllocThread(uint32_t stack_address, uint32_t stack_size);
ThreadState* AllocThread(uint32_t stack_size, uint32_t thread_state_address);
void DeallocThread(ThreadState* thread_state);
int Execute(ThreadState* thread_state, uint32_t address);
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0);
private:
llvm::Function* GetFunction(uint32_t address);

View File

@ -17,7 +17,7 @@
#include <vector>
#include <xenia/kernel/export.h>
#include <xenia/kernel/user_module.h>
#include <xenia/kernel/xex2.h>
namespace xe {
@ -205,7 +205,7 @@ class XexSymbolDatabase : public SymbolDatabase {
public:
XexSymbolDatabase(xe_memory_ref memory,
kernel::ExportResolver* export_resolver,
kernel::UserModule* module);
xe_xex2_ref xex);
virtual ~XexSymbolDatabase();
virtual int Analyze();
@ -218,7 +218,7 @@ private:
virtual uint32_t GetEntryPoint();
virtual bool IsValueInTextRange(uint32_t value);
kernel::UserModule* module_;
xe_xex2_ref xex_;
};

View File

@ -25,16 +25,19 @@ class Processor;
class ThreadState {
public:
ThreadState(Processor* processor,
uint32_t stack_address, uint32_t stack_size);
uint32_t stack_size, uint32_t thread_state_address);
~ThreadState();
xe_ppc_state_t* ppc_state();
private:
uint32_t stack_address_;
uint32_t stack_size_;
uint32_t thread_state_address;
xe_memory_ref memory_;
uint32_t stack_address_;
uint32_t thread_state_address_;
xe_ppc_state_t ppc_state_;
};

View File

@ -14,30 +14,36 @@
#include <xenia/core.h>
#include <xenia/kernel/export.h>
#include <xenia/kernel/runtime.h>
namespace xe {
namespace kernel {
class Runtime;
class KernelModule {
public:
KernelModule(xe_pal_ref pal, xe_memory_ref memory,
shared_ptr<ExportResolver> resolver) {
pal_ = xe_pal_retain(pal);
memory_ = xe_memory_retain(memory);
resolver_ = resolver;
KernelModule(Runtime* runtime) {
runtime_ = runtime;
pal_ = runtime->pal();
memory_ = runtime->memory();
export_resolver_ = runtime->export_resolver();
}
virtual ~KernelModule() {
export_resolver_.reset();
xe_memory_release(memory_);
xe_pal_release(pal_);
}
protected:
Runtime* runtime_;
xe_pal_ref pal_;
xe_memory_ref memory_;
shared_ptr<ExportResolver> resolver_;
shared_ptr<ExportResolver> export_resolver_;
};

View File

@ -15,7 +15,6 @@
#include <xenia/cpu.h>
#include <xenia/kernel/export.h>
#include <xenia/kernel/user_module.h>
#include <xenia/kernel/xex2.h>
@ -23,6 +22,14 @@ namespace xe {
namespace cpu {
class Processor;
}
namespace kernel {
namespace xboxkrnl {
class XboxkrnlModule;
}
namespace xam {
class XamModule;
}
}
}
@ -44,12 +51,7 @@ public:
shared_ptr<ExportResolver> export_resolver();
const xechar_t* command_line();
int LoadBinaryModule(const xechar_t* path, uint32_t start_address);
int LoadModule(const xechar_t* path);
void LaunchModule(UserModule* user_module);
UserModule* GetModule(const xechar_t* path);
void UnloadModule(UserModule* user_module);
int LaunchModule(const xechar_t* path);
private:
xe_pal_ref pal_;
@ -58,8 +60,8 @@ private:
xechar_t command_line_[2048];
shared_ptr<ExportResolver> export_resolver_;
std::vector<KernelModule*> kernel_modules_;
std::map<const xechar_t*, UserModule*> user_modules_;
auto_ptr<xboxkrnl::XboxkrnlModule> xboxkrnl_;
auto_ptr<xam::XamModule> xam_;
};

View File

@ -1,89 +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_USER_MODULE_H_
#define XENIA_KERNEL_USER_MODULE_H_
#include <xenia/core.h>
#include <vector>
#include <xenia/kernel/export.h>
#include <xenia/kernel/xex2.h>
namespace xe {
namespace kernel {
#define kXEPESectionContainsCode 0x00000020
#define kXEPESectionContainsDataInit 0x00000040
#define kXEPESectionContainsDataUninit 0x00000080
#define kXEPESectionMemoryExecute 0x20000000
#define kXEPESectionMemoryRead 0x40000000
#define kXEPESectionMemoryWrite 0x80000000
class PESection {
public:
char name[9]; // 8 + 1 for \0
uint32_t raw_address;
size_t raw_size;
uint32_t address;
size_t size;
uint32_t flags; // kXEPESection*
};
class PEMethodInfo {
public:
uint32_t address;
size_t total_length; // in bytes
size_t prolog_length; // in bytes
};
class UserModule {
public:
UserModule(xe_memory_ref memory);
~UserModule();
int Load(const void* addr, const size_t length, const xechar_t* path);
const xechar_t* path();
const xechar_t* name();
uint32_t handle();
xe_xex2_ref xex();
const xe_xex2_header_t* xex_header();
void* GetProcAddress(const uint32_t ordinal);
PESection* GetSection(const char* name);
int GetMethodHints(PEMethodInfo** out_method_infos,
size_t* out_method_info_count);
void Dump(ExportResolver* export_resolver);
private:
int LoadPE();
xe_memory_ref memory_;
xechar_t path_[2048];
xechar_t name_[256];
uint32_t handle_;
xe_xex2_ref xex_;
std::vector<PESection*> sections_;
};
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_USER_MODULE_H_

View File

@ -7,8 +7,8 @@
******************************************************************************
*/
#ifndef XENIA_KERNEL_MODULES_XBOXKRNL_H_
#define XENIA_KERNEL_MODULES_XBOXKRNL_H_
#ifndef XENIA_KERNEL_XBOX_H_
#define XENIA_KERNEL_XBOX_H_
#include <xenia/common.h>
#include <xenia/core.h>
@ -16,7 +16,6 @@
namespace xe {
namespace kernel {
namespace xboxkrnl {
typedef uint32_t X_HANDLE;
@ -26,6 +25,9 @@ typedef uint32_t X_HANDLE;
// NT_STATUS (STATUS_*)
// http://msdn.microsoft.com/en-us/library/cc704588.aspx
// Adding as needed.
typedef uint32_t X_STATUS;
#define XFAILED(s) (s & X_STATUS_UNSUCCESSFUL)
#define XSUCCEEDED(s) !XFAILED(s)
#define X_STATUS_SUCCESS ((uint32_t)0x00000000L)
#define X_STATUS_UNSUCCESSFUL ((uint32_t)0xC0000001L)
#define X_STATUS_NOT_IMPLEMENTED ((uint32_t)0xC0000002L)
@ -75,13 +77,16 @@ typedef uint32_t X_HANDLE;
#define X_PROCTYPE_SYSTEM 2
// Thread enums.
#define X_CREATE_SUSPENDED 0x00000004
// TLS specials.
#define X_TLS_OUT_OF_INDEXES UINT32_MAX // (-1)
} // namespace xboxkrnl
} // namespace kernel
} // namespace xe
#endif // XENIA_KERNEL_MODULES_XBOXKRNL_H_
#endif // XENIA_KERNEL_XBOX_H_

View File

@ -27,6 +27,24 @@ typedef struct {
uint32_t thunk_address; // NULL or address of thunk
} xe_xex2_import_info_t;
enum xe_pe_section_flags_e {
kXEPESectionContainsCode = 0x00000020,
kXEPESectionContainsDataInit = 0x00000040,
kXEPESectionContainsDataUninit = 0x00000080,
kXEPESectionMemoryExecute = 0x20000000,
kXEPESectionMemoryRead = 0x40000000,
kXEPESectionMemoryWrite = 0x80000000,
};
class PESection {
public:
char name[9]; // 8 + 1 for \0
uint32_t raw_address;
size_t raw_size;
uint32_t address;
size_t size;
uint32_t flags; // kXEPESection*
};
xe_xex2_ref xe_xex2_load(xe_memory_ref memory,
const void* addr, const size_t length,
@ -36,6 +54,7 @@ void xe_xex2_release(xe_xex2_ref xex);
const xechar_t *xe_xex2_get_name(xe_xex2_ref xex);
const xe_xex2_header_t *xe_xex2_get_header(xe_xex2_ref xex);
const PESection* xe_xex2_get_pe_section(xe_xex2_ref xex, const char* name);
int xe_xex2_get_import_infos(xe_xex2_ref xex,
const xe_xex2_import_library_t *library,

View File

@ -9,12 +9,14 @@
#include <xenia/core/memory.h>
#include <xenia/core/mutex.h>
#if !XE_PLATFORM(WIN32)
#include <sys/mman.h>
#endif // WIN32
#define MSPACES 1
#define USE_LOCKS 1
#define USE_LOCKS 0
#define USE_DL_PREFIX 1
#define HAVE_MORECORE 0
#define HAVE_MMAP 0
@ -46,6 +48,7 @@ struct xe_memory {
size_t length;
void* ptr;
xe_mutex_t* heap_mutex;
mspace heap;
};
@ -70,6 +73,9 @@ xe_memory_ref xe_memory_create(xe_pal_ref pal, xe_memory_options_t options) {
#endif // WIN32
XEEXPECTNOTNULL(memory->ptr);
memory->heap_mutex = xe_mutex_alloc(0);
XEEXPECTNOTNULL(memory->heap_mutex);
// Allocate the mspace for our heap.
// We skip the first page to make writes to 0 easier to find.
offset = 64 * 1024;
@ -85,8 +91,15 @@ XECLEANUP:
}
void xe_memory_dealloc(xe_memory_ref memory) {
if (memory->heap) {
if (memory->heap_mutex && memory->heap) {
xe_mutex_lock(memory->heap_mutex);
destroy_mspace(memory->heap);
memory->heap = NULL;
xe_mutex_unlock(memory->heap_mutex);
}
if (memory->heap_mutex) {
xe_mutex_free(memory->heap_mutex);
memory->heap_mutex = NULL;
}
#if XE_PLATFORM(WIN32)
@ -148,7 +161,9 @@ uint32_t xe_memory_heap_alloc(xe_memory_ref memory, uint32_t base_addr,
XEASSERT(base_addr == 0);
XEASSERT(flags == 0);
XEIGNORE(xe_mutex_lock(memory->heap_mutex));
uint8_t* p = (uint8_t*)mspace_malloc(memory->heap, size);
XEIGNORE(xe_mutex_unlock(memory->heap_mutex));
if (!p) {
return 0;
}
@ -166,7 +181,9 @@ uint32_t xe_memory_heap_free(xe_memory_ref memory, uint32_t addr,
return 0;
}
XEIGNORE(xe_mutex_lock(memory->heap_mutex));
mspace_free(memory->heap, p);
XEIGNORE(xe_mutex_unlock(memory->heap_mutex));
return (uint32_t)real_size;
}

60
src/core/mutex_posix.cc Normal file
View File

@ -0,0 +1,60 @@
/**
******************************************************************************
* 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 <xenia/core/mutex.h>
struct xe_mutex {
pthread_mutex_t value;
};
xe_mutex_t* xe_mutex_alloc(uint32_t spin_count) {
xe_mutex_t* mutex = (xe_mutex_t*)xe_calloc(sizeof(xe_mutex_t));
int result = pthread_mutex_init(&mutex->value, NULL);
switch (result) {
case ENOMEM:
case EINVAL:
xe_free(mutex);
return NULL;
}
return mutex;
}
void xe_mutex_free(xe_mutex_t* mutex) {
int result = pthread_mutex_destroy(&mutex->value);
switch (result) {
case EBUSY:
case EINVAL:
break;
default:
break;
}
xe_free(mutex);
}
int xe_mutex_lock(xe_mutex_t* mutex) {
return pthread_mutex_lock(&mutex->value) == EINVAL ? 1 : 0;
}
int xe_mutex_trylock(xe_mutex_t* mutex) {
int result = pthread_mutex_trylock(&mutex->value);
switch (result) {
case EBUSY:
case EINVAL:
return 1;
default:
return 0;
}
}
int xe_mutex_unlock(xe_mutex_t* mutex) {
return pthread_mutex_unlock(&mutex->value) == EINVAL ? 1 : 0;
}

50
src/core/mutex_win.cc Normal file
View File

@ -0,0 +1,50 @@
/**
******************************************************************************
* 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 <xenia/core/mutex.h>
struct xe_mutex {
CRITICAL_SECTION value;
};
xe_mutex_t* xe_mutex_alloc(uint32_t spin_count) {
xe_mutex_t* mutex = (xe_mutex_t*)xe_calloc(sizeof(xe_mutex_t));
if (spin_count) {
InitializeCriticalSectionAndSpinCount(&mutex->value, spin_count);
} else {
InitializeCriticalSection(&mutex->value);
}
return mutex;
}
void xe_mutex_free(xe_mutex_t* mutex) {
DeleteCriticalSection(&mutex->value);
xe_free(mutex);
}
int xe_mutex_lock(xe_mutex_t* mutex) {
EnterCriticalSection(&mutex->value);
return 0;
}
int xe_mutex_trylock(xe_mutex_t* mutex) {
if (TryEnterCriticalSection(&mutex->value) == TRUE) {
return 0;
} else {
return 1;
}
}
int xe_mutex_unlock(xe_mutex_t* mutex) {
LeaveCriticalSection(&mutex->value);
return 0;
}

View File

@ -11,11 +11,13 @@
['OS == "mac" or OS == "linux"', {
'sources': [
'mmap_posix.cc',
'mutex_posix.cc',
],
}],
['OS == "win"', {
'sources': [
'mmap_win.cc',
'mutex_win.cc',
],
}],
],

View File

@ -87,6 +87,7 @@ int XeEmitBranchTo(
// registers?
g.SpillRegisters();
XEASSERTNOTNULL(fn_block->outgoing_function);
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
Function::arg_iterator args = g.gen_fn()->arg_begin();
Value* state_ptr = args;

View File

@ -70,9 +70,9 @@ ExecModule::~ExecModule() {
xe_memory_release(memory_);
}
int ExecModule::PrepareUserModule(kernel::UserModule* user_module) {
int ExecModule::PrepareXex(xe_xex2_ref xex) {
sdb_ = shared_ptr<sdb::SymbolDatabase>(
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), user_module));
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), xex));
int result_code = Prepare();
if (result_code) {
@ -80,8 +80,7 @@ int ExecModule::PrepareUserModule(kernel::UserModule* user_module) {
}
// Import variables.
xe_xex2_ref xex = user_module->xex();
xe_xex2_release(xex);
// TODO??
return 0;
}

View File

@ -91,6 +91,10 @@ void XeTraceInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
type && type->emit ? " " : "X",
type ? type->name : "<unknown>");
if (cia == 0x82014468) {
printf("BREAKBREAKBREAK\n");
}
// TODO(benvanik): better disassembly, printing of current register values/etc
}

View File

@ -113,14 +113,35 @@ int Processor::Setup() {
return 0;
}
int Processor::PrepareModule(
const char* module_name, const char* module_path,
uint32_t start_address, uint32_t end_address,
int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
shared_ptr<ExportResolver> export_resolver) {
ExecModule* exec_module = new ExecModule(
memory_, export_resolver, module_name, module_path, engine_);
ExecModule* exec_module = NULL;
const xechar_t* name = xestrrchr(path, '/') + 1;
if (exec_module->PrepareRawBinary(start_address, end_address)) {
// 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));
exec_module = new ExecModule(
memory_, export_resolver, name_a, path_a, engine_);
if (exec_module->PrepareRawBinary(start_address, start_address + length)) {
delete exec_module;
return 1;
}
@ -130,20 +151,23 @@ int Processor::PrepareModule(
exec_module->Dump();
return 0;
result_code = 0;
XECLEANUP:
if (result_code) {
delete exec_module;
}
xe_mmap_release(mmap);
return result_code;
}
int Processor::PrepareModule(UserModule* user_module,
int Processor::PrepareModule(const char* name, const char* path,
xe_xex2_ref xex,
shared_ptr<ExportResolver> export_resolver) {
char name_a[2048];
char path_a[2048];
XEIGNORE(xestrnarrow(name_a, XECOUNT(name_a), user_module->name()));
XEIGNORE(xestrnarrow(path_a, XECOUNT(path_a), user_module->path()));
ExecModule* exec_module = new ExecModule(
memory_, export_resolver, name_a, path_a,
memory_, export_resolver, name, path,
engine_);
if (exec_module->PrepareUserModule(user_module)) {
if (exec_module->PrepareXex(xex)) {
delete exec_module;
return 1;
}
@ -151,7 +175,6 @@ int Processor::PrepareModule(UserModule* user_module,
exec_module->AddFunctionsToMap(all_fns_);
modules_.push_back(exec_module);
//user_module->Dump(export_resolver.get());
exec_module->Dump();
return 0;
@ -162,10 +185,10 @@ uint32_t Processor::CreateCallback(void (*callback)(void* data), void* data) {
return 0;
}
ThreadState* Processor::AllocThread(uint32_t stack_address,
uint32_t stack_size) {
ThreadState* Processor::AllocThread(uint32_t stack_size,
uint32_t thread_state_address) {
ThreadState* thread_state = new ThreadState(
this, stack_address, stack_size);
this, stack_size, thread_state_address);
return thread_state;
}
@ -210,6 +233,16 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) {
return 0;
}
uint64_t Processor::Execute(ThreadState* thread_state, uint32_t address,
uint64_t arg0) {
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
ppc_state->r[3] = arg0;
if (Execute(thread_state, address)) {
return 0xDEADBABE;
}
return ppc_state->r[3];
}
Function* Processor::GetFunction(uint32_t address) {
FunctionMap::iterator it = all_fns_.find(address);
if (it != all_fns_.end()) {

View File

@ -23,6 +23,36 @@ using namespace xe::cpu::sdb;
using namespace xe::kernel;
namespace {
// 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;
class PEMethodInfo {
public:
uint32_t address;
size_t total_length; // in bytes
size_t prolog_length; // in bytes
};
}
FunctionBlock::FunctionBlock() :
start_address(0), end_address(0),
outgoing_type(kTargetUnknown), outgoing_address(0),
@ -405,7 +435,8 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
block->outgoing_type = FunctionBlock::kTargetLR;
if (furthest_target > addr) {
// Remaining targets within function, not end.
XELOGSDB(XT("ignoring blr %.8X (branch to %.8X)"), addr, furthest_target);
XELOGSDB(XT("ignoring blr %.8X (branch to %.8X)"), addr,
furthest_target);
} else {
// Function end point.
XELOGSDB(XT("function end %.8X"), addr);
@ -458,6 +489,11 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
if (!ends_fn) {
furthest_target = MAX(furthest_target, target);
// TODO(benvanik): perhaps queue up for a speculative check? I think
// we are running over tail-call functions here that branch to
// somewhere else.
//GetOrInsertFunction(target);
}
}
ends_block = true;
@ -467,9 +503,16 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
block->outgoing_address = target;
if (i.B.LK) {
XELOGSDB(XT("bcl %.8X -> %.8X"), addr, target);
// Queue call target if needed.
// TODO(benvanik): see if this is correct - not sure anyone makes
// function calls with bcl.
//GetOrInsertFunction(target);
} else {
XELOGSDB(XT("bc %.8X -> %.8X"), addr, target);
// TODO(benvanik): GetOrInsertFunction? it's likely a BB
furthest_target = MAX(furthest_target, target);
}
ends_block = true;
@ -558,14 +601,16 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
block->outgoing_block = fn->SplitBlock(block->outgoing_address);
}
if (!block->outgoing_block) {
printf("block target not found: %.8X\n", block->outgoing_address);
XELOGE(XT("block target not found: %.8X"), block->outgoing_address);
XEASSERTALWAYS();
}
} else {
// Function call.
block->outgoing_type = FunctionBlock::kTargetFunction;
block->outgoing_function = GetFunction(block->outgoing_address);
if (!block->outgoing_function) {
printf("call target not found: %.8X\n", block->outgoing_address);
XELOGE(XT("call target not found: %.8X"), block->outgoing_address);
XEASSERTALWAYS();
}
}
}
@ -702,16 +747,17 @@ bool RawSymbolDatabase::IsValueInTextRange(uint32_t value) {
}
XexSymbolDatabase::XexSymbolDatabase(
xe_memory_ref memory, ExportResolver* export_resolver, UserModule* module) :
xe_memory_ref memory, ExportResolver* export_resolver, xe_xex2_ref xex) :
SymbolDatabase(memory, export_resolver) {
module_ = module;
xex_ = xe_xex2_retain(xex);
}
XexSymbolDatabase::~XexSymbolDatabase() {
xe_xex2_release(xex_);
}
int XexSymbolDatabase::Analyze() {
const xe_xex2_header_t *header = module_->xex_header();
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
// Find __savegprlr_* and __restgprlr_*.
FindGplr();
@ -785,7 +831,7 @@ int XexSymbolDatabase::FindGplr() {
};
uint32_t gplr_start = 0;
const xe_xex2_header_t* header = module_->xex_header();
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
for (size_t n = 0, i = 0; n < header->section_count; n++) {
const xe_xex2_section_t* section = &header->sections[n];
const size_t start_address =
@ -833,12 +879,10 @@ int XexSymbolDatabase::FindGplr() {
}
int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
xe_xex2_ref xex = module_->xex();
xe_xex2_import_info_t* import_infos;
size_t import_info_count;
if (xe_xex2_get_import_infos(xex, library, &import_infos,
if (xe_xex2_get_import_infos(xex_, library, &import_infos,
&import_info_count)) {
xe_xex2_release(xex);
return 1;
}
@ -878,18 +922,54 @@ int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
}
xe_free(import_infos);
xe_xex2_release(xex);
return 0;
}
int XexSymbolDatabase::AddMethodHints() {
PEMethodInfo* method_infos;
size_t method_info_count;
if (module_->GetMethodHints(&method_infos, &method_info_count)) {
return 1;
uint8_t* mem = xe_memory_addr(memory_, 0);
const IMAGE_XBOX_RUNTIME_FUNCTION_ENTRY* entry = NULL;
// Find pdata, which contains the exception handling entries.
const PESection* pdata = xe_xex2_get_pe_section(xex_, ".pdata");
if (!pdata) {
// No exception data to go on.
return 0;
}
for (size_t n = 0; n < method_info_count; n++) {
// 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));
if (!method_infos) {
return 0;
}
// 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;
}
for (size_t n = 0; n < entry_count; n++) {
PEMethodInfo* method_info = &method_infos[n];
FunctionSymbol* fn = GetOrInsertFunction(method_info->address);
fn->end_address = method_info->address + method_info->total_length - 4;
@ -902,12 +982,12 @@ int XexSymbolDatabase::AddMethodHints() {
}
uint32_t XexSymbolDatabase::GetEntryPoint() {
const xe_xex2_header_t* header = module_->xex_header();
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
return header->exe_entry_point;
};
bool XexSymbolDatabase::IsValueInTextRange(uint32_t value) {
const xe_xex2_header_t* header = module_->xex_header();
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
for (size_t n = 0, i = 0; n < header->section_count; n++) {
const xe_xex2_section_t* section = &header->sections[n];
const size_t start_address =

View File

@ -19,11 +19,12 @@ using namespace xe::cpu;
ThreadState::ThreadState(
Processor* processor,
uint32_t stack_address, uint32_t stack_size) {
stack_address_ = stack_address;
stack_size_ = stack_size;
uint32_t stack_size, uint32_t thread_state_address) :
stack_size_(stack_size), thread_state_address_(thread_state_address) {
memory_ = processor->memory();
stack_address_ = xe_memory_heap_alloc(memory_, 0, stack_size, 0);
xe_zero_struct(&ppc_state_, sizeof(ppc_state_));
// Stash pointers to common structures that callbacks may need.
@ -33,9 +34,11 @@ ThreadState::ThreadState(
// Set initial registers.
ppc_state_.r[1] = stack_address_;
ppc_state_.r[13] = thread_state_address_;
}
ThreadState::~ThreadState() {
xe_memory_heap_free(memory_, stack_address_, 0);
xe_memory_release(memory_);
}

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

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

View File

@ -1,7 +1,6 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'includes': [
'xenia-info/xenia-info.gypi',
'xenia-run/xenia-run.gypi',
'xenia-test/xenia-test.gypi',
],

View File

@ -1,66 +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. *
******************************************************************************
*/
#include <xenia/xenia.h>
#include <gflags/gflags.h>
using namespace xe;
using namespace xe::cpu;
using namespace xe::kernel;
int xenia_info(int argc, xechar_t **argv) {
std::string usage = "usage: ";
usage += "xenia-info some.xex";
google::SetUsageMessage(usage);
google::SetVersionString("1.0");
google::ParseCommandLineFlags(&argc, &argv, true);
int result_code = 1;
xe_pal_ref pal = NULL;
xe_memory_ref memory = NULL;
shared_ptr<Processor> processor;
shared_ptr<Runtime> runtime;
// Grab path.
if (argc < 2) {
google::ShowUsageWithFlags("xenia-info");
return 1;
}
const xechar_t *path = argv[1];
xe_pal_options_t pal_options;
xe_zero_struct(&pal_options, sizeof(pal_options));
pal = xe_pal_create(pal_options);
XEEXPECTNOTNULL(pal);
xe_memory_options_t memory_options;
xe_zero_struct(&memory_options, sizeof(memory_options));
memory = xe_memory_create(pal, memory_options);
XEEXPECTNOTNULL(memory);
processor = shared_ptr<Processor>(new Processor(pal, memory));
XEEXPECTZERO(processor->Setup());
runtime = shared_ptr<Runtime>(new Runtime(pal, processor, XT("")));
XEEXPECTZERO(runtime->LoadModule(path));
result_code = 0;
XECLEANUP:
xe_memory_release(memory);
xe_pal_release(pal);
google::ShutDownCommandLineFlags();
return result_code;
}
XE_MAIN_THUNK(xenia_info);

View File

@ -1,23 +0,0 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'targets': [
{
'target_name': 'xenia-info',
'type': 'executable',
'dependencies': [
'xeniacore',
'xeniacpu',
'xeniakernel',
],
'include_dirs': [
'.',
],
'sources': [
'xenia-info.cc',
],
},
],
}

View File

@ -26,15 +26,14 @@ public:
Run();
~Run();
int Setup(const xechar_t* path);
int Launch();
int Setup();
int Launch(const xechar_t* path);
private:
xe_pal_ref pal_;
xe_memory_ref memory_;
shared_ptr<Processor> processor_;
shared_ptr<Runtime> runtime_;
UserModule* module_;
};
Run::Run() {
@ -45,7 +44,7 @@ Run::~Run() {
xe_pal_release(pal_);
}
int Run::Setup(const xechar_t* path) {
int Run::Setup() {
xe_pal_options_t pal_options;
xe_zero_struct(&pal_options, sizeof(pal_options));
pal_ = xe_pal_create(pal_options);
@ -61,21 +60,19 @@ int Run::Setup(const xechar_t* path) {
runtime_ = shared_ptr<Runtime>(new Runtime(pal_, processor_, XT("")));
XEEXPECTZERO(runtime_->LoadModule(path));
module_ = runtime_->GetModule(path);
return 0;
XECLEANUP:
return 1;
}
int Run::Launch() {
int Run::Launch(const xechar_t* path) {
if (FLAGS_abort_before_entry) {
return 0;
}
// TODO(benvanik): wait until the module thread exits
runtime_->LaunchModule(module_);
runtime_->LaunchModule(path);
return 0;
}
@ -100,12 +97,12 @@ int xenia_run(int argc, xechar_t **argv) {
auto_ptr<Run> run = auto_ptr<Run>(new Run());
result_code = run->Setup(path);
result_code = run->Setup();
XEEXPECTZERO(result_code);
//xe_module_dump(run->module);
run->Launch();
run->Launch(path);
result_code = 0;
XECLEANUP:

View File

@ -126,10 +126,11 @@ int run_test(xe_pal_ref pal, string& src_file_path) {
runtime = shared_ptr<Runtime>(new Runtime(pal, processor, XT("")));
// Load the binary module.
XEEXPECTZERO(runtime->LoadBinaryModule(bin_file_path.c_str(), 0x82010000));
XEEXPECTZERO(processor->LoadBinary(bin_file_path.c_str(), 0x82010000,
runtime->export_resolver()));
// Simulate a thread.
thread_state = processor->AllocThread(0x80000000, 256 * 1024 * 1024);
thread_state = processor->AllocThread(256 * 1024 * 1024, 0);
// Setup test state from annotations.
XEEXPECTZERO(setup_test_state(memory, processor.get(), thread_state,
@ -157,6 +158,10 @@ int discover_tests(string& test_path,
vector<string>& test_files) {
// TODO(benvanik): use PAL instead of this
DIR* d = opendir(test_path.c_str());
if (!d) {
XELOGE(XT("Unable to find test path %s"), test_path.c_str());
return 1;
}
struct dirent* dir;
while ((dir = readdir(d))) {
if (dir->d_type == DT_REG) {