commit
4097fe420e
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <cstdint>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
#if XE_PLATFORM_MAC
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/kernel/objects/xmodule.h"
|
||||
|
||||
#include "third_party/crypto/rijndael-alg-fst.h"
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
@ -45,19 +47,184 @@ XexModule::XexModule(Processor* processor, KernelState* kernel_state)
|
|||
|
||||
XexModule::~XexModule() { xe_xex2_dealloc(xex_); }
|
||||
|
||||
bool XexModule::GetOptHeader(const xex2_header* header, xe_xex2_header_keys key,
|
||||
void** out_ptr) {
|
||||
assert_not_null(header);
|
||||
assert_not_null(out_ptr);
|
||||
|
||||
for (uint32_t i = 0; i < header->header_count; i++) {
|
||||
const xex2_opt_header& opt_header = header->headers[i];
|
||||
if (opt_header.key == key) {
|
||||
// Match!
|
||||
switch (key & 0xFF) {
|
||||
case 0x00: {
|
||||
// We just return the value of the optional header.
|
||||
// Assume that the output pointer points to a uint32_t.
|
||||
*(uint32_t*)out_ptr = (uint32_t)opt_header.value;
|
||||
} break;
|
||||
case 0x01: {
|
||||
// Pointer to the value on the optional header.
|
||||
*out_ptr = (void*)&opt_header.value;
|
||||
} break;
|
||||
default: {
|
||||
// Pointer to the header.
|
||||
*out_ptr = (void*)((uint8_t*)header + opt_header.offset);
|
||||
} break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool XexModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const {
|
||||
return XexModule::GetOptHeader(xex_header_, key, out_ptr);
|
||||
}
|
||||
|
||||
const xex2_security_info* XexModule::GetSecurityInfo(
|
||||
const xex2_header* header) {
|
||||
return reinterpret_cast<const xex2_security_info*>((uint8_t*)header +
|
||||
header->security_offset);
|
||||
}
|
||||
|
||||
uint32_t XexModule::GetProcAddress(uint16_t ordinal) const {
|
||||
// First: Check the xex2 export table.
|
||||
if (xex_security_info()->export_table) {
|
||||
auto export_table = memory()->TranslateVirtual<const xex2_export_table*>(
|
||||
xex_security_info()->export_table);
|
||||
|
||||
if (ordinal > export_table->count) {
|
||||
XELOGE("GetProcAddress(%.3X): ordinal out of bounds", ordinal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t num = ordinal - export_table->base;
|
||||
uint32_t ordinal_offset = export_table->ordOffset[num];
|
||||
ordinal_offset += export_table->imagebaseaddr << 16;
|
||||
return ordinal_offset;
|
||||
}
|
||||
|
||||
// Second: Check the PE exports.
|
||||
xe::be<uint32_t>* exe_address = nullptr;
|
||||
GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address);
|
||||
assert_not_null(exe_address);
|
||||
|
||||
xex2_opt_data_directory* pe_export_directory = 0;
|
||||
if (GetOptHeader(XEX_HEADER_EXPORTS_BY_NAME, &pe_export_directory)) {
|
||||
auto e = memory()->TranslateVirtual<const X_IMAGE_EXPORT_DIRECTORY*>(
|
||||
*exe_address + pe_export_directory->offset);
|
||||
assert_not_null(e);
|
||||
|
||||
uint32_t* function_table = (uint32_t*)((uint8_t*)e + e->AddressOfFunctions);
|
||||
|
||||
if (ordinal < e->NumberOfFunctions) {
|
||||
return xex_security_info()->load_address + function_table[ordinal];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t XexModule::GetProcAddress(const char* name) const {
|
||||
xe::be<uint32_t>* exe_address = nullptr;
|
||||
GetOptHeader(XEX_HEADER_IMAGE_BASE_ADDRESS, &exe_address);
|
||||
assert_not_null(exe_address);
|
||||
|
||||
xex2_opt_data_directory* pe_export_directory = 0;
|
||||
if (!GetOptHeader(XEX_HEADER_EXPORTS_BY_NAME, &pe_export_directory)) {
|
||||
// No exports by name.
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto e = memory()->TranslateVirtual<const X_IMAGE_EXPORT_DIRECTORY*>(
|
||||
*exe_address + pe_export_directory->offset);
|
||||
assert_not_null(e);
|
||||
|
||||
// e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY!
|
||||
uint32_t* function_table = (uint32_t*)((uint64_t)e + e->AddressOfFunctions);
|
||||
|
||||
// Names relative to directory
|
||||
uint32_t* name_table = (uint32_t*)((uint64_t)e + e->AddressOfNames);
|
||||
|
||||
// Table of ordinals (by name)
|
||||
uint16_t* ordinal_table = (uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals);
|
||||
|
||||
const char* mod_name = (const char*)((uint64_t)e + e->Name);
|
||||
|
||||
for (uint32_t i = 0; i < e->NumberOfNames; i++) {
|
||||
const char* fn_name = (const char*)((uint64_t)e + name_table[i]);
|
||||
uint16_t ordinal = ordinal_table[i];
|
||||
uint32_t addr = *exe_address + function_table[ordinal];
|
||||
|
||||
if (!strcmp(name, fn_name)) {
|
||||
// We have a match!
|
||||
return addr;
|
||||
}
|
||||
}
|
||||
|
||||
// No match
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool XexModule::ApplyPatch(XexModule* module) {
|
||||
auto header = reinterpret_cast<const xex2_header*>(module->xex_header());
|
||||
if (!(header->module_flags &
|
||||
(XEX_MODULE_MODULE_PATCH | XEX_MODULE_PATCH_DELTA |
|
||||
XEX_MODULE_PATCH_FULL))) {
|
||||
// This isn't a XEX2 patch.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Grab the delta descriptor and get to work.
|
||||
xex2_opt_delta_patch_descriptor* patch_header = nullptr;
|
||||
GetOptHeader(header, XEX_HEADER_DELTA_PATCH_DESCRIPTOR,
|
||||
(void**)&patch_header);
|
||||
assert_not_null(patch_header);
|
||||
|
||||
// TODO!
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool XexModule::Load(const std::string& name, const std::string& path,
|
||||
const void* xex_addr, size_t xex_length) {
|
||||
// TODO: Move loading code here
|
||||
xex_ = xe_xex2_load(memory(), xex_addr, xex_length, {0});
|
||||
if (!xex_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make a copy of the xex header.
|
||||
auto src_header = reinterpret_cast<const xex2_header*>(xex_addr);
|
||||
xex_header_ = (xex2_header*)new char[src_header->header_size];
|
||||
|
||||
std::memcpy(xex_header_, src_header, src_header->header_size);
|
||||
|
||||
return Load(name, path, xex_);
|
||||
}
|
||||
|
||||
bool XexModule::Load(const std::string& name, const std::string& path,
|
||||
xe_xex2_ref xex) {
|
||||
xex_ = xex;
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex);
|
||||
auto header = xex_header_;
|
||||
auto old_header = xe_xex2_get_header(xex_);
|
||||
|
||||
// Setup debug info.
|
||||
name_ = std::string(name);
|
||||
path_ = std::string(path);
|
||||
// TODO(benvanik): debug info
|
||||
|
||||
// Scan and find the low/high addresses.
|
||||
// All code sections are continuous, so this should be easy.
|
||||
// TODO: Use the new xex header to do this.
|
||||
low_address_ = UINT_MAX;
|
||||
high_address_ = 0;
|
||||
for (uint32_t n = 0, i = 0; n < header->section_count; n++) {
|
||||
const xe_xex2_section_t* section = &header->sections[n];
|
||||
for (uint32_t n = 0, i = 0; n < old_header->section_count; n++) {
|
||||
const xe_xex2_section_t* section = &old_header->sections[n];
|
||||
const uint32_t start_address =
|
||||
header->exe_address + (i * section->page_size);
|
||||
old_header->exe_address + (i * section->page_size);
|
||||
const uint32_t end_address =
|
||||
start_address + (section->info.page_count * section->page_size);
|
||||
if (section->info.type == XEX_SECTION_CODE) {
|
||||
|
@ -71,23 +238,45 @@ bool XexModule::Load(const std::string& name, const std::string& path,
|
|||
processor_->backend()->CommitExecutableRange(low_address_, high_address_);
|
||||
|
||||
// Add all imports (variables/functions).
|
||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||
if (!SetupLibraryImports(&header->import_libraries[n])) {
|
||||
return false;
|
||||
xex2_opt_import_libraries* opt_import_header = nullptr;
|
||||
GetOptHeader(XEX_HEADER_IMPORT_LIBRARIES, &opt_import_header);
|
||||
assert_not_null(opt_import_header);
|
||||
|
||||
// FIXME: Don't know if 32 is the actual limit, but haven't seen more than 2.
|
||||
const char* string_table[32];
|
||||
std::memset(string_table, 0, sizeof(string_table));
|
||||
|
||||
// Parse the string table
|
||||
for (size_t i = 0, j = 0; i < opt_import_header->string_table_size; j++) {
|
||||
assert_true(j < xe::countof(string_table));
|
||||
const char* str = opt_import_header->string_table + i;
|
||||
|
||||
string_table[j] = str;
|
||||
i += std::strlen(str) + 1;
|
||||
|
||||
// Padding
|
||||
if ((i % 4) != 0) {
|
||||
i += 4 - (i % 4);
|
||||
}
|
||||
}
|
||||
|
||||
auto libraries =
|
||||
(uint8_t*)opt_import_header + opt_import_header->string_table_size + 12;
|
||||
uint32_t library_offset = 0;
|
||||
for (uint32_t i = 0; i < opt_import_header->library_count; i++) {
|
||||
auto library = reinterpret_cast<xex2_import_library*>((uint8_t*)libraries +
|
||||
library_offset);
|
||||
SetupLibraryImports(string_table[library->name_index], library);
|
||||
|
||||
library_offset += library->size;
|
||||
}
|
||||
|
||||
// Find __savegprlr_* and __restgprlr_* and the others.
|
||||
// We can flag these for special handling (inlining/etc).
|
||||
if (!FindSaveRest()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Setup debug info.
|
||||
name_ = std::string(name);
|
||||
path_ = std::string(path);
|
||||
// TODO(benvanik): debug info
|
||||
|
||||
// Load a specified module map and diff.
|
||||
if (FLAGS_load_module_map.size()) {
|
||||
if (!ReadMap(FLAGS_load_module_map.c_str())) {
|
||||
|
@ -98,121 +287,106 @@ bool XexModule::Load(const std::string& name, const std::string& path,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) {
|
||||
ExportResolver* export_resolver = processor_->export_resolver();
|
||||
|
||||
xe_xex2_import_info_t* import_infos;
|
||||
size_t import_info_count;
|
||||
if (xe_xex2_get_import_infos(xex_, library, &import_infos,
|
||||
&import_info_count)) {
|
||||
return false;
|
||||
bool XexModule::SetupLibraryImports(const char* name,
|
||||
const xex2_import_library* library) {
|
||||
ExportResolver* kernel_resolver = nullptr;
|
||||
if (kernel_state_->IsKernelModule(name)) {
|
||||
kernel_resolver = processor_->export_resolver();
|
||||
}
|
||||
|
||||
char name[128];
|
||||
for (size_t n = 0; n < import_info_count; n++) {
|
||||
const xe_xex2_import_info_t* info = &import_infos[n];
|
||||
auto user_module = kernel_state_->GetModule(name);
|
||||
|
||||
// Strip off the extension (for the symbol name)
|
||||
std::string libname = library->name;
|
||||
auto dot = libname.find_last_of('.');
|
||||
if (dot != libname.npos) {
|
||||
libname = libname.substr(0, dot);
|
||||
}
|
||||
std::string libbasename = name;
|
||||
auto dot = libbasename.find_last_of('.');
|
||||
if (dot != libbasename.npos) {
|
||||
libbasename = libbasename.substr(0, dot);
|
||||
}
|
||||
|
||||
Export* kernel_export = nullptr; // kernel export info
|
||||
uint32_t user_export_addr = 0; // user export address
|
||||
// Imports are stored as {import descriptor, thunk addr, import desc, ...}
|
||||
// Even thunks have an import descriptor (albeit unused/useless)
|
||||
for (uint32_t i = 0; i < library->count; i++) {
|
||||
uint32_t record_addr = library->import_table[i];
|
||||
assert_not_zero(record_addr);
|
||||
|
||||
if (kernel_state_->IsKernelModule(library->name)) {
|
||||
kernel_export =
|
||||
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
|
||||
auto record_slot =
|
||||
memory()->TranslateVirtual<xe::be<uint32_t>*>(record_addr);
|
||||
uint32_t record_value = *record_slot;
|
||||
|
||||
uint16_t record_type = (record_value & 0xFF000000) >> 24;
|
||||
uint16_t ordinal = record_value & 0xFFFF;
|
||||
|
||||
Export* kernel_export = nullptr;
|
||||
uint32_t user_export_addr = 0;
|
||||
|
||||
if (kernel_resolver) {
|
||||
kernel_export = kernel_resolver->GetExportByOrdinal(name, ordinal);
|
||||
} else {
|
||||
auto module = kernel_state_->GetModule(library->name);
|
||||
if (module) {
|
||||
user_export_addr = module->GetProcAddressByOrdinal(info->ordinal);
|
||||
}
|
||||
user_export_addr = user_module->GetProcAddressByOrdinal(ordinal);
|
||||
}
|
||||
|
||||
if (kernel_export) {
|
||||
if (info->thunk_address) {
|
||||
snprintf(name, xe::countof(name), "__imp_%s", kernel_export->name);
|
||||
} else {
|
||||
snprintf(name, xe::countof(name), "%s", kernel_export->name);
|
||||
}
|
||||
} else {
|
||||
snprintf(name, xe::countof(name), "__imp_%s_%.3X", libname.c_str(),
|
||||
info->ordinal);
|
||||
// Import not resolved?
|
||||
assert_not_zero(kernel_export || user_export_addr);
|
||||
if (!kernel_export && !user_export_addr) {
|
||||
XELOGW(
|
||||
"WARNING: an import variable was not resolved! (library: %s, import "
|
||||
"lib: %s, ordinal: %.3X)",
|
||||
name_.c_str(), name, ordinal);
|
||||
}
|
||||
|
||||
VariableInfo* var_info;
|
||||
DeclareVariable(info->value_address, &var_info);
|
||||
// var->set_name(name);
|
||||
var_info->set_status(SymbolStatus::kDeclared);
|
||||
DefineVariable(var_info);
|
||||
// var->kernel_export = kernel_export;
|
||||
var_info->set_status(SymbolStatus::kDefined);
|
||||
|
||||
// Grab, if available.
|
||||
auto slot = memory_->TranslateVirtual<uint32_t*>(info->value_address);
|
||||
if (kernel_export) {
|
||||
if (kernel_export->type == Export::Type::kFunction) {
|
||||
// Not exactly sure what this should be...
|
||||
if (info->thunk_address) {
|
||||
*slot = xe::byte_swap(info->thunk_address);
|
||||
} else {
|
||||
// TODO(benvanik): find out what import variables are.
|
||||
XELOGW("kernel import variable not defined %.8X %s",
|
||||
info->value_address, kernel_export->name);
|
||||
*slot = xe::byte_swap(0xF00DF00D);
|
||||
}
|
||||
} else {
|
||||
if (kernel_export->is_implemented()) {
|
||||
// Implemented - replace with pointer.
|
||||
xe::store_and_swap<uint32_t>(slot, kernel_export->variable_ptr);
|
||||
} else {
|
||||
// Not implemented - write with a dummy value.
|
||||
xe::store_and_swap<uint32_t>(
|
||||
slot, 0xD000BEEF | (kernel_export->ordinal & 0xFFF) << 16);
|
||||
XELOGCPU("WARNING: imported a variable with no value: %s",
|
||||
kernel_export->name);
|
||||
}
|
||||
}
|
||||
} else if (user_export_addr) {
|
||||
xe::store_and_swap<uint32_t>(slot, user_export_addr);
|
||||
} else {
|
||||
// No module found.
|
||||
XELOGE("kernel import not found: %s", name);
|
||||
if (info->thunk_address) {
|
||||
*slot = xe::byte_swap(info->thunk_address);
|
||||
} else {
|
||||
*slot = xe::byte_swap(0xF00DF00D);
|
||||
}
|
||||
}
|
||||
|
||||
if (info->thunk_address) {
|
||||
StringBuffer import_name;
|
||||
if (record_type == 0) {
|
||||
// Variable.
|
||||
import_name.AppendFormat("__imp__");
|
||||
if (kernel_export) {
|
||||
snprintf(name, xe::countof(name), "%s", kernel_export->name);
|
||||
import_name.AppendFormat("%s", kernel_export->name);
|
||||
} else if (user_export_addr) {
|
||||
snprintf(name, xe::countof(name), "__%s_%.3X", libname.c_str(),
|
||||
info->ordinal);
|
||||
} else {
|
||||
snprintf(name, xe::countof(name), "__kernel_%s_%.3X", libname.c_str(),
|
||||
info->ordinal);
|
||||
import_name.AppendFormat("%s_%.3X", libbasename.c_str(), ordinal);
|
||||
}
|
||||
|
||||
if (user_export_addr) {
|
||||
// Rewrite PPC code to set r11 to the target address
|
||||
// So we'll have:
|
||||
// lis r11, user_export_addr
|
||||
// ori r11, r11, user_export_addr
|
||||
// mtspr CTR, r11
|
||||
// bctr
|
||||
uint16_t hi_addr = (user_export_addr >> 16) & 0xFFFF;
|
||||
uint16_t low_addr = user_export_addr & 0xFFFF;
|
||||
|
||||
uint8_t* p = memory()->TranslateVirtual(info->thunk_address);
|
||||
xe::store_and_swap<uint32_t>(p + 0x0, 0x3D600000 | hi_addr);
|
||||
xe::store_and_swap<uint32_t>(p + 0x4, 0x616B0000 | low_addr);
|
||||
if (kernel_export) {
|
||||
if (kernel_export->type == Export::Type::kFunction) {
|
||||
// Not exactly sure what this should be...
|
||||
// Appears to be ignored.
|
||||
*record_slot = 0xDEADC0DE;
|
||||
} else if (kernel_export->type == Export::Type::kVariable) {
|
||||
// Kernel import variable
|
||||
if (kernel_export->is_implemented()) {
|
||||
// Implemented - replace with pointer.
|
||||
*record_slot = kernel_export->variable_ptr;
|
||||
} else {
|
||||
// Not implemented - write with a dummy value.
|
||||
*record_slot = 0xD000BEEF | (kernel_export->ordinal & 0xFFF) << 16;
|
||||
XELOGCPU("WARNING: imported a variable with no value: %s",
|
||||
kernel_export->name);
|
||||
}
|
||||
}
|
||||
} else if (user_export_addr) {
|
||||
*record_slot = user_export_addr;
|
||||
} else {
|
||||
*record_slot = 0xF00DF00D;
|
||||
}
|
||||
|
||||
// Setup a variable and define it.
|
||||
VariableInfo* var_info;
|
||||
DeclareVariable(record_addr, &var_info);
|
||||
var_info->set_name(import_name.GetString());
|
||||
var_info->set_status(SymbolStatus::kDeclared);
|
||||
DefineVariable(var_info);
|
||||
var_info->set_status(SymbolStatus::kDefined);
|
||||
} else if (record_type == 1) {
|
||||
// Thunk.
|
||||
if (kernel_export) {
|
||||
import_name.AppendFormat("%s", kernel_export->name);
|
||||
} else if (user_export_addr) {
|
||||
import_name.AppendFormat("__%s_%.3X", libbasename.c_str(), ordinal);
|
||||
}
|
||||
|
||||
FunctionInfo* fn_info;
|
||||
DeclareFunction(record_addr, &fn_info);
|
||||
fn_info->set_end_address(record_addr + 16 - 4);
|
||||
fn_info->set_name(import_name.GetString());
|
||||
|
||||
if (kernel_export) {
|
||||
// On load we have something like this in memory:
|
||||
// li r3, 0
|
||||
// li r4, 0x1F5
|
||||
|
@ -227,7 +401,7 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) {
|
|||
// blr
|
||||
// nop
|
||||
// nop
|
||||
uint8_t* p = memory()->TranslateVirtual(info->thunk_address);
|
||||
uint8_t* p = memory()->TranslateVirtual(record_addr);
|
||||
xe::store_and_swap<uint32_t>(p + 0x0, 0x44000002);
|
||||
xe::store_and_swap<uint32_t>(p + 0x4, 0x4E800020);
|
||||
xe::store_and_swap<uint32_t>(p + 0x8, 0x60000000);
|
||||
|
@ -243,16 +417,43 @@ bool XexModule::SetupLibraryImports(const xe_xex2_import_library_t* library) {
|
|||
(FunctionInfo::ExternHandler)kernel_export->function_data.shim;
|
||||
}
|
||||
} else {
|
||||
XELOGW("WARNING: Imported kernel function %s is unimplemented!",
|
||||
import_name.GetString());
|
||||
handler = UndefinedImport;
|
||||
}
|
||||
|
||||
FunctionInfo* fn_info;
|
||||
DeclareFunction(info->thunk_address, &fn_info);
|
||||
fn_info->set_end_address(info->thunk_address + 16 - 4);
|
||||
fn_info->set_name(name);
|
||||
fn_info->SetupExtern(handler);
|
||||
fn_info->set_status(SymbolStatus::kDeclared);
|
||||
} else if (user_export_addr) {
|
||||
// Rewrite PPC code to set r11 to the target address
|
||||
// So we'll have:
|
||||
// lis r11, user_export_addr
|
||||
// ori r11, r11, user_export_addr
|
||||
// mtspr CTR, r11
|
||||
// bctr
|
||||
uint16_t hi_addr = (user_export_addr >> 16) & 0xFFFF;
|
||||
uint16_t low_addr = user_export_addr & 0xFFFF;
|
||||
|
||||
uint8_t* p = memory()->TranslateVirtual(record_addr);
|
||||
xe::store_and_swap<uint32_t>(p + 0x0, 0x3D600000 | hi_addr);
|
||||
xe::store_and_swap<uint32_t>(p + 0x4, 0x616B0000 | low_addr);
|
||||
} else {
|
||||
// Import not resolved.
|
||||
// We're gonna rewrite the PPC to trigger a debug trap:
|
||||
// trap
|
||||
// blr
|
||||
// nop
|
||||
// nop
|
||||
uint8_t* p = memory()->TranslateVirtual(record_addr);
|
||||
xe::store_and_swap<uint32_t>(p + 0x0, 0x7FE00008);
|
||||
xe::store_and_swap<uint32_t>(p + 0x4, 0x4E800020);
|
||||
xe::store_and_swap<uint32_t>(p + 0x8, 0x60000000);
|
||||
xe::store_and_swap<uint32_t>(p + 0xC, 0x60000000);
|
||||
}
|
||||
|
||||
fn_info->set_status(SymbolStatus::kDeclared);
|
||||
} else {
|
||||
// Bad.
|
||||
assert_always();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
#include "xenia/cpu/module.h"
|
||||
#include "xenia/kernel/util/xex2.h"
|
||||
#include "xenia/kernel/util/xex2_info.h"
|
||||
|
||||
namespace xe {
|
||||
|
||||
|
@ -32,7 +33,40 @@ class XexModule : public xe::cpu::Module {
|
|||
virtual ~XexModule();
|
||||
|
||||
xe_xex2_ref xex() const { return xex_; }
|
||||
const xex2_header* xex_header() const { return xex_header_; }
|
||||
const xex2_security_info* xex_security_info() const {
|
||||
return GetSecurityInfo(xex_header_);
|
||||
}
|
||||
|
||||
// Gets an optional header. Returns NULL if not found.
|
||||
// Special case: if key & 0xFF == 0x00, this function will return the value,
|
||||
// not a pointer! This assumes out_ptr points to uint32_t.
|
||||
static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key,
|
||||
void** out_ptr);
|
||||
bool GetOptHeader(xe_xex2_header_keys key, void** out_ptr) const;
|
||||
|
||||
// Ultra-cool templated version
|
||||
// Special case: if key & 0xFF == 0x00, this function will return the value,
|
||||
// not a pointer! This assumes out_ptr points to uint32_t.
|
||||
template <typename T>
|
||||
static bool GetOptHeader(const xex2_header* header, xe_xex2_header_keys key,
|
||||
T* out_ptr) {
|
||||
return GetOptHeader(header, key, reinterpret_cast<void**>(out_ptr));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool GetOptHeader(xe_xex2_header_keys key, T* out_ptr) const {
|
||||
return GetOptHeader(key, reinterpret_cast<void**>(out_ptr));
|
||||
}
|
||||
|
||||
static const xex2_security_info* GetSecurityInfo(const xex2_header* header);
|
||||
|
||||
uint32_t GetProcAddress(uint16_t ordinal) const;
|
||||
uint32_t GetProcAddress(const char* name) const;
|
||||
|
||||
bool ApplyPatch(XexModule* module);
|
||||
bool Load(const std::string& name, const std::string& path,
|
||||
const void* xex_addr, size_t xex_length);
|
||||
bool Load(const std::string& name, const std::string& path, xe_xex2_ref xex);
|
||||
|
||||
const std::string& name() const override { return name_; }
|
||||
|
@ -40,8 +74,8 @@ class XexModule : public xe::cpu::Module {
|
|||
bool ContainsAddress(uint32_t address) override;
|
||||
|
||||
private:
|
||||
bool SetupImports(xe_xex2_ref xex);
|
||||
bool SetupLibraryImports(const xe_xex2_import_library_t* library);
|
||||
bool SetupLibraryImports(const char* name,
|
||||
const xex2_import_library* library);
|
||||
bool FindSaveRest();
|
||||
|
||||
private:
|
||||
|
@ -50,6 +84,7 @@ class XexModule : public xe::cpu::Module {
|
|||
std::string name_;
|
||||
std::string path_;
|
||||
xe_xex2_ref xex_;
|
||||
xex2_header* xex_header_;
|
||||
|
||||
uint32_t base_address_;
|
||||
uint32_t low_address_;
|
||||
|
|
|
@ -120,7 +120,15 @@ KernelState* KernelState::shared() { return shared_kernel_state_; }
|
|||
|
||||
uint32_t KernelState::title_id() const {
|
||||
assert_not_null(executable_module_);
|
||||
return executable_module_->xex_header()->execution_info.title_id;
|
||||
|
||||
xex2_opt_execution_info* exec_info = 0;
|
||||
executable_module_->GetOptHeader(XEX_HEADER_EXECUTION_INFO, &exec_info);
|
||||
|
||||
if (exec_info) {
|
||||
return exec_info->title_id;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t KernelState::process_type() const {
|
||||
|
@ -192,13 +200,14 @@ void KernelState::SetExecutableModule(object_ref<XUserModule> module) {
|
|||
return;
|
||||
}
|
||||
|
||||
auto header = executable_module_->xex_header();
|
||||
if (header) {
|
||||
xex2_opt_tls_info* tls_header = nullptr;
|
||||
executable_module_->GetOptHeader(XEX_HEADER_TLS_INFO, &tls_header);
|
||||
if (tls_header) {
|
||||
auto pib = memory_->TranslateVirtual<ProcessInfoBlock*>(
|
||||
process_info_block_address_);
|
||||
pib->tls_data_size = header->tls_info.data_size;
|
||||
pib->tls_raw_data_size = header->tls_info.raw_data_size;
|
||||
pib->tls_slot_size = header->tls_info.slot_count * 4;
|
||||
pib->tls_data_size = tls_header->data_size;
|
||||
pib->tls_raw_data_size = tls_header->raw_data_size;
|
||||
pib->tls_slot_size = tls_header->slot_count * 4;
|
||||
}
|
||||
|
||||
// Setup the kernel's XexExecutableModuleHandle field.
|
||||
|
@ -275,8 +284,7 @@ object_ref<XUserModule> KernelState::LoadUserModule(const char* raw_name) {
|
|||
|
||||
module->Dump();
|
||||
|
||||
auto xex_header = module->xex_header();
|
||||
if (xex_header->exe_entry_point) {
|
||||
if (module->entry_point()) {
|
||||
// Call DllMain(DLL_PROCESS_ATTACH):
|
||||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx
|
||||
uint64_t args[] = {
|
||||
|
@ -285,7 +293,7 @@ object_ref<XUserModule> KernelState::LoadUserModule(const char* raw_name) {
|
|||
0, // 0 because always dynamic
|
||||
};
|
||||
auto thread_state = XThread::GetCurrentThread()->thread_state();
|
||||
processor()->Execute(thread_state, xex_header->exe_entry_point, args,
|
||||
processor()->Execute(thread_state, module->entry_point(), args,
|
||||
xe::countof(args));
|
||||
}
|
||||
|
||||
|
@ -323,14 +331,13 @@ void KernelState::OnThreadExecute(XThread* thread) {
|
|||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx
|
||||
auto thread_state = thread->thread_state();
|
||||
for (auto user_module : user_modules_) {
|
||||
auto xex_header = user_module->xex_header();
|
||||
if (xex_header->exe_entry_point) {
|
||||
if (user_module->entry_point()) {
|
||||
uint64_t args[] = {
|
||||
user_module->handle(),
|
||||
2, // DLL_THREAD_ATTACH
|
||||
0, // 0 because always dynamic
|
||||
};
|
||||
processor()->Execute(thread_state, xex_header->exe_entry_point, args,
|
||||
processor()->Execute(thread_state, user_module->entry_point(), args,
|
||||
xe::countof(args));
|
||||
}
|
||||
}
|
||||
|
@ -346,14 +353,13 @@ void KernelState::OnThreadExit(XThread* thread) {
|
|||
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms682583%28v=vs.85%29.aspx
|
||||
auto thread_state = thread->thread_state();
|
||||
for (auto user_module : user_modules_) {
|
||||
auto xex_header = user_module->xex_header();
|
||||
if (xex_header->exe_entry_point) {
|
||||
if (user_module->entry_point()) {
|
||||
uint64_t args[] = {
|
||||
user_module->handle(),
|
||||
3, // DLL_THREAD_DETACH
|
||||
0, // 0 because always dynamic
|
||||
};
|
||||
processor()->Execute(thread_state, xex_header->exe_entry_point, args,
|
||||
processor()->Execute(thread_state, user_module->entry_point(), args,
|
||||
xe::countof(args));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -164,14 +164,18 @@ X_STATUS XThread::Create() {
|
|||
|
||||
// Allocate TLS block.
|
||||
// Games will specify a certain number of 4b slots that each thread will get.
|
||||
xex2_opt_tls_info* tls_header = nullptr;
|
||||
if (module) {
|
||||
module->GetOptHeader(XEX_HEADER_TLS_INFO, &tls_header);
|
||||
}
|
||||
|
||||
const uint32_t kDefaultTlsSlotCount = 32;
|
||||
uint32_t tls_slots = 0;
|
||||
uint32_t tls_extended_size = 0;
|
||||
if (module && module->xex_header()) {
|
||||
const xe_xex2_header_t* header = module->xex_header();
|
||||
tls_slots = header->tls_info.slot_count ? header->tls_info.slot_count
|
||||
: kDefaultTlsSlotCount;
|
||||
tls_extended_size = header->tls_info.data_size;
|
||||
if (tls_header) {
|
||||
tls_slots =
|
||||
tls_header->slot_count ? tls_header->slot_count : kDefaultTlsSlotCount;
|
||||
tls_extended_size = tls_header->data_size;
|
||||
} else {
|
||||
tls_slots = kDefaultTlsSlotCount;
|
||||
}
|
||||
|
@ -192,10 +196,9 @@ X_STATUS XThread::Create() {
|
|||
memory()->Fill(tls_address_, tls_total_size, 0);
|
||||
if (tls_extended_size) {
|
||||
// If game has extended data, copy in the default values.
|
||||
const xe_xex2_header_t* header = module->xex_header();
|
||||
assert_not_zero(header->tls_info.raw_data_address);
|
||||
memory()->Copy(tls_address_, header->tls_info.raw_data_address,
|
||||
header->tls_info.raw_data_size);
|
||||
assert_not_zero(tls_header->raw_data_address);
|
||||
memory()->Copy(tls_address_, tls_header->raw_data_address,
|
||||
tls_header->raw_data_size);
|
||||
}
|
||||
|
||||
// Allocate thread state block from heap.
|
||||
|
|
|
@ -22,15 +22,9 @@ namespace kernel {
|
|||
using namespace xe::cpu;
|
||||
|
||||
XUserModule::XUserModule(KernelState* kernel_state, const char* path)
|
||||
: XModule(kernel_state, ModuleType::kUserModule, path), xex_(nullptr) {}
|
||||
: XModule(kernel_state, ModuleType::kUserModule, path) {}
|
||||
|
||||
XUserModule::~XUserModule() { xe_xex2_dealloc(xex_); }
|
||||
|
||||
xe_xex2_ref XUserModule::xex() { return xex_; }
|
||||
|
||||
const xe_xex2_header_t* XUserModule::xex_header() {
|
||||
return xe_xex2_get_header(xex_);
|
||||
}
|
||||
XUserModule::~XUserModule() {}
|
||||
|
||||
X_STATUS XUserModule::LoadFromFile(std::string path) {
|
||||
X_STATUS result = X_STATUS_UNSUCCESSFUL;
|
||||
|
@ -82,33 +76,10 @@ X_STATUS XUserModule::LoadFromFile(std::string path) {
|
|||
X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||
Processor* processor = kernel_state()->processor();
|
||||
|
||||
// Load the XEX into memory and decrypt.
|
||||
xe_xex2_options_t xex_options = {0};
|
||||
xex_ = xe_xex2_load(memory(), addr, length, xex_options);
|
||||
if (!xex_) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Copy the xex2 header into guest memory
|
||||
const xex2_header* header = reinterpret_cast<const xex2_header*>(addr);
|
||||
uint32_t header_size = xex2_get_header_size(header);
|
||||
|
||||
xex_header_ = memory()->SystemHeapAlloc(header_size);
|
||||
|
||||
uint8_t* xex_header_ptr = memory()->TranslateVirtual(xex_header_);
|
||||
std::memcpy(xex_header_ptr, header, header_size);
|
||||
|
||||
// Setup the loader data entry
|
||||
auto ldr_data =
|
||||
memory()->TranslateVirtual<X_LDR_DATA_TABLE_ENTRY*>(hmodule_ptr_);
|
||||
|
||||
ldr_data->dll_base = 0; // GetProcAddress will read this.
|
||||
ldr_data->xex_header_base = xex_header_;
|
||||
|
||||
// Prepare the module for execution.
|
||||
// Runtime takes ownership.
|
||||
auto xex_module = std::make_unique<XexModule>(processor, kernel_state());
|
||||
if (!xex_module->Load(name_, path_, xex_)) {
|
||||
if (!xex_module->Load(name_, path_, addr, length)) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
processor_module_ = xex_module.get();
|
||||
|
@ -116,37 +87,76 @@ X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) {
|
|||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Copy the xex2 header into guest memory.
|
||||
const xex2_header* header = this->xex_module()->xex_header();
|
||||
guest_xex_header_ = memory()->SystemHeapAlloc(header->header_size);
|
||||
|
||||
uint8_t* xex_header_ptr = memory()->TranslateVirtual(guest_xex_header_);
|
||||
std::memcpy(xex_header_ptr, header, header->header_size);
|
||||
|
||||
// Setup the loader data entry
|
||||
auto ldr_data =
|
||||
memory()->TranslateVirtual<X_LDR_DATA_TABLE_ENTRY*>(hmodule_ptr_);
|
||||
|
||||
ldr_data->dll_base = 0; // GetProcAddress will read this.
|
||||
ldr_data->xex_header_base = guest_xex_header_;
|
||||
|
||||
// Cache some commonly used headers...
|
||||
this->xex_module()->GetOptHeader(XEX_HEADER_ENTRY_POINT, &entry_point_);
|
||||
this->xex_module()->GetOptHeader(XEX_HEADER_DEFAULT_STACK_SIZE, &stack_size_);
|
||||
|
||||
OnLoad();
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
uint32_t XUserModule::GetProcAddressByOrdinal(uint16_t ordinal) {
|
||||
return xe_xex2_lookup_export(xex_, ordinal);
|
||||
return xex_module()->GetProcAddress(ordinal);
|
||||
}
|
||||
|
||||
uint32_t XUserModule::GetProcAddressByName(const char* name) {
|
||||
return xe_xex2_lookup_export(xex_, name);
|
||||
return xex_module()->GetProcAddress(name);
|
||||
}
|
||||
|
||||
X_STATUS XUserModule::GetSection(const char* name, uint32_t* out_section_data,
|
||||
uint32_t* out_section_size) {
|
||||
auto header = xe_xex2_get_header(xex_);
|
||||
for (size_t n = 0; n < header->resource_info_count; n++) {
|
||||
auto& res = header->resource_infos[n];
|
||||
xex2_opt_resource_info* resource_header = nullptr;
|
||||
if (!XexModule::GetOptHeader(xex_header(), XEX_HEADER_RESOURCE_INFO,
|
||||
&resource_header)) {
|
||||
// No resources.
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
uint32_t count = (resource_header->size - 4) / 16;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
auto& res = resource_header->resources[i];
|
||||
if (strcmp(name, res.name) == 0) {
|
||||
// Found!
|
||||
*out_section_data = res.address;
|
||||
*out_section_size = res.size;
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) {
|
||||
assert_not_null(out_ptr);
|
||||
|
||||
bool ret = xex_module()->GetOptHeader(key, out_ptr);
|
||||
if (!ret) {
|
||||
return X_STATUS_NOT_FOUND;
|
||||
}
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key,
|
||||
uint32_t* out_header_guest_ptr) {
|
||||
auto header = memory()->TranslateVirtual<xex2_header*>(xex_header_);
|
||||
auto header =
|
||||
memory()->TranslateVirtual<const xex2_header*>(guest_xex_header_);
|
||||
if (!header) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
@ -190,16 +200,12 @@ X_STATUS XUserModule::GetOptHeader(uint8_t* membase, const xex2_header* header,
|
|||
}
|
||||
|
||||
X_STATUS XUserModule::Launch(uint32_t flags) {
|
||||
const xe_xex2_header_t* header = xex_header();
|
||||
|
||||
XELOGI("Launching module...");
|
||||
|
||||
Dump();
|
||||
|
||||
// Create a thread to run in.
|
||||
auto thread =
|
||||
object_ref<XThread>(new XThread(kernel_state(), header->exe_stack_size, 0,
|
||||
header->exe_entry_point, 0, 0));
|
||||
auto thread = object_ref<XThread>(
|
||||
new XThread(kernel_state(), stack_size_, 0, entry_point_, 0, 0));
|
||||
|
||||
X_STATUS result = thread->Create();
|
||||
if (XFAILED(result)) {
|
||||
|
@ -216,71 +222,249 @@ X_STATUS XUserModule::Launch(uint32_t flags) {
|
|||
void XUserModule::Dump() {
|
||||
xe::cpu::ExportResolver* export_resolver =
|
||||
kernel_state_->emulator()->export_resolver();
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
|
||||
auto header = xex_header();
|
||||
|
||||
// XEX info.
|
||||
printf("Module %s:\n\n", path_.c_str());
|
||||
printf(" Module Flags: %.8X\n", header->module_flags);
|
||||
printf(" System Flags: %.8X\n", header->system_flags);
|
||||
printf("\n");
|
||||
printf(" Address: %.8X\n", header->exe_address);
|
||||
printf(" Entry Point: %.8X\n", header->exe_entry_point);
|
||||
printf(" Stack Size: %.8X\n", header->exe_stack_size);
|
||||
printf(" Heap Size: %.8X\n", header->exe_heap_size);
|
||||
printf("\n");
|
||||
printf(" Execution Info:\n");
|
||||
printf(" Media ID: %.8X\n", header->execution_info.media_id);
|
||||
printf(
|
||||
" Version: %d.%d.%d.%d\n", header->execution_info.version.major,
|
||||
header->execution_info.version.minor,
|
||||
header->execution_info.version.build, header->execution_info.version.qfe);
|
||||
printf(" Base Version: %d.%d.%d.%d\n",
|
||||
header->execution_info.base_version.major,
|
||||
header->execution_info.base_version.minor,
|
||||
header->execution_info.base_version.build,
|
||||
header->execution_info.base_version.qfe);
|
||||
printf(" Title ID: %.8X\n", header->execution_info.title_id);
|
||||
printf(" Platform: %.8X\n", header->execution_info.platform);
|
||||
printf(" Exec Table: %.8X\n", header->execution_info.executable_table);
|
||||
printf(" Disc Number: %d\n", header->execution_info.disc_number);
|
||||
printf(" Disc Count: %d\n", header->execution_info.disc_count);
|
||||
printf(" Savegame ID: %.8X\n", header->execution_info.savegame_id);
|
||||
printf("\n");
|
||||
printf(" Loader Info:\n");
|
||||
printf(" Image Flags: %.8X\n", header->loader_info.image_flags);
|
||||
printf(" Game Regions: %.8X\n", header->loader_info.game_regions);
|
||||
printf(" Media Flags: %.8X\n", header->loader_info.media_flags);
|
||||
printf("\n");
|
||||
printf(" TLS Info:\n");
|
||||
printf(" Slot Count: %d\n", header->tls_info.slot_count);
|
||||
printf(" Data Size: %db\n", header->tls_info.data_size);
|
||||
printf(" Address: %.8X, %db\n", header->tls_info.raw_data_address,
|
||||
header->tls_info.raw_data_size);
|
||||
printf("\n");
|
||||
printf(" Headers:\n");
|
||||
for (size_t n = 0; n < header->header_count; n++) {
|
||||
const xe_xex2_opt_header_t* opt_header = &header->headers[n];
|
||||
printf(" %.8X (%.8X, %4db) %.8X = %11d\n", opt_header->key,
|
||||
opt_header->offset, opt_header->length, opt_header->value,
|
||||
opt_header->value);
|
||||
// XEX header.
|
||||
printf("Module %s:\n", path_.c_str());
|
||||
printf(" Module Flags: %.8X\n", (uint32_t)header->module_flags);
|
||||
|
||||
// Security header
|
||||
auto security_info = xex_module()->xex_security_info();
|
||||
printf("Security Header:\n");
|
||||
printf(" Image Flags: %.8X\n", (uint32_t)security_info->image_flags);
|
||||
printf(" Load Address: %.8X\n", (uint32_t)security_info->load_address);
|
||||
printf(" Image Size: %.8X\n", (uint32_t)security_info->image_size);
|
||||
printf(" Export Table: %.8X\n", (uint32_t)security_info->export_table);
|
||||
|
||||
// Optional headers
|
||||
printf("Optional Header Count: %d\n", (uint32_t)header->header_count);
|
||||
|
||||
for (uint32_t i = 0; i < header->header_count; i++) {
|
||||
auto& opt_header = header->headers[i];
|
||||
|
||||
// Stash a pointer (although this isn't used in every case)
|
||||
void* opt_header_ptr = (uint8_t*)header + opt_header.offset;
|
||||
switch (opt_header.key) {
|
||||
case XEX_HEADER_RESOURCE_INFO: {
|
||||
printf(" XEX_HEADER_RESOURCE_INFO:\n");
|
||||
auto opt_resource_info =
|
||||
reinterpret_cast<xex2_opt_resource_info*>(opt_header_ptr);
|
||||
|
||||
uint32_t count = (opt_resource_info->size - 4) / 16;
|
||||
for (uint32_t j = 0; j < count; j++) {
|
||||
auto& res = opt_resource_info->resources[j];
|
||||
|
||||
// Manually NULL-terminate the name.
|
||||
char name[9];
|
||||
std::memcpy(name, res.name, sizeof(res.name));
|
||||
name[8] = 0;
|
||||
|
||||
printf(" %-8s %.8X-%.8X, %db\n", name, res.address,
|
||||
res.address + res.size, res.size);
|
||||
}
|
||||
} break;
|
||||
case XEX_HEADER_FILE_FORMAT_INFO: {
|
||||
printf(" XEX_HEADER_FILE_FORMAT_INFO (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_DELTA_PATCH_DESCRIPTOR: {
|
||||
printf(" XEX_HEADER_DELTA_PATCH_DESCRIPTOR (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_BOUNDING_PATH: {
|
||||
auto opt_bound_path =
|
||||
reinterpret_cast<xex2_opt_bound_path*>(opt_header_ptr);
|
||||
printf(" XEX_HEADER_BOUNDING_PATH: %s\n", opt_bound_path->path);
|
||||
} break;
|
||||
case XEX_HEADER_ORIGINAL_BASE_ADDRESS: {
|
||||
printf(" XEX_HEADER_ORIGINAL_BASE_ADDRESS: %.8X\n",
|
||||
(uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_ENTRY_POINT: {
|
||||
printf(" XEX_HEADER_ENTRY_POINT: %.8X\n", (uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_IMAGE_BASE_ADDRESS: {
|
||||
printf(" XEX_HEADER_IMAGE_BASE_ADDRESS: %.8X\n",
|
||||
(uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_IMPORT_LIBRARIES: {
|
||||
printf(" XEX_HEADER_IMPORT_LIBRARIES:\n");
|
||||
auto opt_import_libraries =
|
||||
reinterpret_cast<const xex2_opt_import_libraries*>(opt_header_ptr);
|
||||
|
||||
// FIXME: Don't know if 32 is the actual limit, but haven't seen more
|
||||
// than 2.
|
||||
const char* string_table[32];
|
||||
std::memset(string_table, 0, sizeof(string_table));
|
||||
|
||||
// Parse the string table
|
||||
for (size_t i = 0, j = 0; i < opt_import_libraries->string_table_size;
|
||||
j++) {
|
||||
assert_true(j < xe::countof(string_table));
|
||||
const char* str = opt_import_libraries->string_table + i;
|
||||
|
||||
string_table[j] = str;
|
||||
i += std::strlen(str) + 1;
|
||||
|
||||
// Padding
|
||||
if ((i % 4) != 0) {
|
||||
i += 4 - (i % 4);
|
||||
}
|
||||
}
|
||||
|
||||
auto libraries = (uint8_t*)opt_import_libraries +
|
||||
opt_import_libraries->string_table_size + 12;
|
||||
uint32_t library_offset = 0;
|
||||
for (uint32_t i = 0; i < opt_import_libraries->library_count; i++) {
|
||||
auto library = reinterpret_cast<xex2_import_library*>(
|
||||
(uint8_t*)libraries + library_offset);
|
||||
auto name = string_table[library->name_index];
|
||||
|
||||
// Okay. Dump it.
|
||||
printf(" %s - %d imports\n", name, library->count);
|
||||
|
||||
// Manually byteswap these because of the bitfields.
|
||||
xex2_version version, version_min;
|
||||
version.value = xe::byte_swap<uint32_t>(library->version.value);
|
||||
version_min.value =
|
||||
xe::byte_swap<uint32_t>(library->version_min.value);
|
||||
printf(" Version: %d.%d.%d.%d\n", version.major, version.minor,
|
||||
version.build, version.qfe);
|
||||
printf(" Min Version: %d.%d.%d.%d\n", version_min.major,
|
||||
version_min.minor, version_min.build, version_min.qfe);
|
||||
|
||||
library_offset += library->size;
|
||||
}
|
||||
} break;
|
||||
case XEX_HEADER_CHECKSUM_TIMESTAMP: {
|
||||
printf(" XEX_HEADER_CHECKSUM_TIMESTAMP (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_ORIGINAL_PE_NAME: {
|
||||
auto opt_pe_name =
|
||||
reinterpret_cast<xex2_opt_original_pe_name*>(opt_header_ptr);
|
||||
printf(" XEX_HEADER_ORIGINAL_PE_NAME: %s\n", opt_pe_name->name);
|
||||
} break;
|
||||
case XEX_HEADER_STATIC_LIBRARIES: {
|
||||
printf(" XEX_HEADER_STATIC_LIBRARIES:\n");
|
||||
auto opt_static_libraries =
|
||||
reinterpret_cast<const xex2_opt_static_libraries*>(opt_header_ptr);
|
||||
|
||||
uint32_t count = (opt_static_libraries->size - 4) / 0x10;
|
||||
for (uint32_t i = 0; i < count; i++) {
|
||||
auto& library = opt_static_libraries->libraries[i];
|
||||
printf(
|
||||
" %-8s : %d.%d.%d.%d\n", library.name,
|
||||
(uint16_t)library.version_major, (uint16_t)library.version_minor,
|
||||
(uint16_t)library.version_build, (uint16_t)library.version_qfe);
|
||||
}
|
||||
} break;
|
||||
case XEX_HEADER_TLS_INFO: {
|
||||
printf(" XEX_HEADER_TLS_INFO:\n");
|
||||
auto opt_tls_info =
|
||||
reinterpret_cast<const xex2_opt_tls_info*>(opt_header_ptr);
|
||||
|
||||
printf(" Slot Count: %d\n",
|
||||
(uint32_t)opt_tls_info->slot_count);
|
||||
printf(" Raw Data Address: %.8X\n",
|
||||
(uint32_t)opt_tls_info->raw_data_address);
|
||||
printf(" Data Size: %d\n", (uint32_t)opt_tls_info->data_size);
|
||||
printf(" Raw Data Size: %d\n",
|
||||
(uint32_t)opt_tls_info->raw_data_size);
|
||||
} break;
|
||||
case XEX_HEADER_DEFAULT_STACK_SIZE: {
|
||||
printf(" XEX_HEADER_DEFAULT_STACK_SIZE: %d\n",
|
||||
(uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: {
|
||||
printf(" XEX_HEADER_DEFAULT_FILESYSTEM_CACHE_SIZE: %d\n",
|
||||
(uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_DEFAULT_HEAP_SIZE: {
|
||||
printf(" XEX_HEADER_DEFAULT_HEAP_SIZE: %d\n",
|
||||
(uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS: {
|
||||
printf(" XEX_HEADER_PAGE_HEAP_SIZE_AND_FLAGS (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_SYSTEM_FLAGS: {
|
||||
printf(" XEX_HEADER_SYSTEM_FLAGS: %.8X\n", (uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_EXECUTION_INFO: {
|
||||
printf(" XEX_HEADER_EXECUTION_INFO:\n");
|
||||
auto opt_exec_info =
|
||||
reinterpret_cast<const xex2_opt_execution_info*>(opt_header_ptr);
|
||||
|
||||
printf(" Media ID: %.8X\n", (uint32_t)opt_exec_info->media_id);
|
||||
printf(" Title ID: %.8X\n", (uint32_t)opt_exec_info->title_id);
|
||||
printf(" Savegame ID: %.8X\n", (uint32_t)opt_exec_info->title_id);
|
||||
printf(" Disc Number / Total: %d / %d\n",
|
||||
(uint8_t)opt_exec_info->disc_number,
|
||||
(uint8_t)opt_exec_info->disc_count);
|
||||
} break;
|
||||
case XEX_HEADER_TITLE_WORKSPACE_SIZE: {
|
||||
printf(" XEX_HEADER_TITLE_WORKSPACE_SIZE: %d\n",
|
||||
(uint32_t)opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_GAME_RATINGS: {
|
||||
printf(" XEX_HEADER_GAME_RATINGS (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_LAN_KEY: {
|
||||
printf(" XEX_HEADER_LAN_KEY (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_XBOX360_LOGO: {
|
||||
printf(" XEX_HEADER_XBOX360_LOGO (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_MULTIDISC_MEDIA_IDS: {
|
||||
printf(" XEX_HEADER_MULTIDISC_MEDIA_IDS (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_ALTERNATE_TITLE_IDS: {
|
||||
printf(" XEX_HEADER_ALTERNATE_TITLE_IDS (TODO):\n");
|
||||
} break;
|
||||
case XEX_HEADER_ADDITIONAL_TITLE_MEMORY: {
|
||||
printf(" XEX_HEADER_ADDITIONAL_TITLE_MEMORY: %d\n", opt_header.value);
|
||||
} break;
|
||||
case XEX_HEADER_EXPORTS_BY_NAME: {
|
||||
printf(" XEX_HEADER_EXPORTS_BY_NAME:\n");
|
||||
auto dir =
|
||||
reinterpret_cast<const xex2_opt_data_directory*>(opt_header_ptr);
|
||||
|
||||
auto exe_address = xex_module()->xex_security_info()->load_address;
|
||||
auto e = memory()->TranslateVirtual<const X_IMAGE_EXPORT_DIRECTORY*>(
|
||||
exe_address + dir->offset);
|
||||
|
||||
// e->AddressOfX RVAs are relative to the IMAGE_EXPORT_DIRECTORY!
|
||||
uint32_t* function_table =
|
||||
(uint32_t*)((uint64_t)e + e->AddressOfFunctions);
|
||||
|
||||
// Names relative to directory
|
||||
uint32_t* name_table = (uint32_t*)((uint64_t)e + e->AddressOfNames);
|
||||
|
||||
// Table of ordinals (by name)
|
||||
uint16_t* ordinal_table =
|
||||
(uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals);
|
||||
|
||||
for (uint32_t i = 0; i < e->NumberOfNames; i++) {
|
||||
const char* name = (const char*)((uint8_t*)e + name_table[i]);
|
||||
uint16_t ordinal = ordinal_table[i];
|
||||
uint32_t addr = exe_address + function_table[ordinal];
|
||||
|
||||
printf(" %-28s - %.3X - %.8X\n", name, ordinal, addr);
|
||||
}
|
||||
} break;
|
||||
default: {
|
||||
printf(" Unknown Header %.8X\n", (uint32_t)opt_header.key);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Resources.
|
||||
printf("Resources:\n");
|
||||
for (size_t n = 0; n < header->resource_info_count; n++) {
|
||||
auto& res = header->resource_infos[n];
|
||||
printf(" %-8s %.8X-%.8X, %db\n", res.name, res.address,
|
||||
res.address + res.size, res.size);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Section info.
|
||||
printf("Sections:\n");
|
||||
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
||||
const xe_xex2_section_t* section = &header->sections[n];
|
||||
for (uint32_t i = 0, page = 0; i < security_info->page_descriptor_count;
|
||||
i++) {
|
||||
// Manually byteswap the bitfield data.
|
||||
xex2_page_descriptor page_descriptor;
|
||||
page_descriptor.value =
|
||||
xe::byte_swap(security_info->page_descriptors[i].value);
|
||||
|
||||
const char* type = "UNKNOWN";
|
||||
switch (section->info.type) {
|
||||
switch (page_descriptor.info) {
|
||||
case XEX_SECTION_CODE:
|
||||
type = "CODE ";
|
||||
break;
|
||||
|
@ -291,72 +475,29 @@ void XUserModule::Dump() {
|
|||
type = "RODATA ";
|
||||
break;
|
||||
}
|
||||
const size_t start_address = header->exe_address + (i * section->page_size);
|
||||
const size_t end_address =
|
||||
start_address + (section->info.page_count * section->page_size);
|
||||
printf(" %3d %s %3d pages %.8X - %.8X (%d bytes)\n", (int)n, type,
|
||||
section->info.page_count, (int)start_address, (int)end_address,
|
||||
section->info.page_count * section->page_size);
|
||||
i += section->info.page_count;
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Static libraries.
|
||||
printf("Static Libraries:\n");
|
||||
for (size_t n = 0; n < header->static_library_count; n++) {
|
||||
const xe_xex2_static_library_t* library = &header->static_libraries[n];
|
||||
printf(" %-8s : %d.%d.%d.%d\n", library->name, library->major,
|
||||
library->minor, library->build, library->qfe);
|
||||
}
|
||||
printf("\n");
|
||||
const uint32_t page_size =
|
||||
security_info->load_address < 0x90000000 ? 64 * 1024 : 4 * 1024;
|
||||
uint32_t start_address = security_info->load_address + (page * page_size);
|
||||
uint32_t end_address = start_address + (page_descriptor.size * page_size);
|
||||
|
||||
// Exports.
|
||||
printf("Exports:\n");
|
||||
if (header->loader_info.export_table) {
|
||||
printf(" XEX-style Ordinal Exports:\n");
|
||||
auto export_table = reinterpret_cast<const xe_xex2_export_table*>(
|
||||
memory()->TranslateVirtual(header->loader_info.export_table));
|
||||
uint32_t ordinal_count = xe::byte_swap(export_table->count);
|
||||
uint32_t ordinal_base = xe::byte_swap(export_table->base);
|
||||
for (uint32_t i = 0, ordinal = ordinal_base; i < ordinal_count;
|
||||
++i, ++ordinal) {
|
||||
uint32_t ordinal_offset = xe::byte_swap(export_table->ordOffset[i]);
|
||||
ordinal_offset += xe::byte_swap(export_table->imagebaseaddr) << 16;
|
||||
printf(" %.8X %.3X (%3d)\n", ordinal_offset, ordinal,
|
||||
ordinal);
|
||||
}
|
||||
printf(" %3d %s %3d pages %.8X - %.8X (%d bytes)\n", (int)page, type,
|
||||
page_descriptor.size, (int)start_address, (int)end_address,
|
||||
page_descriptor.size * page_size);
|
||||
page += page_descriptor.size;
|
||||
}
|
||||
if (header->pe_export_table_offset) {
|
||||
auto e = reinterpret_cast<const IMAGE_EXPORT_DIRECTORY*>(
|
||||
memory()->TranslateVirtual(header->exe_address +
|
||||
header->pe_export_table_offset));
|
||||
uint32_t* function_table = (uint32_t*)((uint64_t)e + e->AddressOfFunctions);
|
||||
uint32_t* name_table = (uint32_t*)((uint64_t)e + e->AddressOfNames);
|
||||
uint16_t* ordinal_table =
|
||||
(uint16_t*)((uint64_t)e + e->AddressOfNameOrdinals);
|
||||
const char* mod_name = (const char*)((uint64_t)e + e->Name);
|
||||
printf(" PE %s:\n", mod_name);
|
||||
for (uint32_t i = 0; i < e->NumberOfNames; i++) {
|
||||
const char* fn_name = (const char*)((uint64_t)e + name_table[i]);
|
||||
uint16_t ordinal = ordinal_table[i];
|
||||
uint32_t addr = header->exe_address + function_table[ordinal];
|
||||
printf(" %.8X %.3X (%3d) %s\n", addr, ordinal, ordinal,
|
||||
fn_name);
|
||||
}
|
||||
}
|
||||
if (!header->loader_info.export_table && !header->pe_export_table_offset) {
|
||||
printf(" No exports\n");
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
// Imports.
|
||||
// Print out imports.
|
||||
// TODO: Figure out a way to remove dependency on old xex header.
|
||||
auto old_header = xe_xex2_get_header(xex_module()->xex());
|
||||
|
||||
printf("Imports:\n");
|
||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||
const xe_xex2_import_library_t* library = &header->import_libraries[n];
|
||||
for (size_t n = 0; n < old_header->import_library_count; n++) {
|
||||
const xe_xex2_import_library_t* library = &old_header->import_libraries[n];
|
||||
|
||||
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_module()->xex(), library, &import_infos,
|
||||
&import_info_count)) {
|
||||
printf(" %s - %d imports\n", library->name, (int)import_info_count);
|
||||
printf(" Version: %d.%d.%d.%d\n", library->version.major,
|
||||
|
|
|
@ -13,8 +13,9 @@
|
|||
#include <string>
|
||||
|
||||
#include "xenia/cpu/export_resolver.h"
|
||||
#include "xenia/cpu/xex_module.h"
|
||||
#include "xenia/kernel/objects/xmodule.h"
|
||||
#include "xenia/kernel/util/xex2.h"
|
||||
#include "xenia/kernel/util/xex2_info.h"
|
||||
#include "xenia/xbox.h"
|
||||
|
||||
namespace xe {
|
||||
|
@ -25,8 +26,15 @@ class XUserModule : public XModule {
|
|||
XUserModule(KernelState* kernel_state, const char* path);
|
||||
~XUserModule() override;
|
||||
|
||||
xe_xex2_ref xex();
|
||||
const xe_xex2_header_t* xex_header();
|
||||
const xe::cpu::XexModule* xex_module() const {
|
||||
return reinterpret_cast<xe::cpu::XexModule*>(processor_module_);
|
||||
}
|
||||
|
||||
const xex2_header* xex_header() const { return xex_module()->xex_header(); }
|
||||
uint32_t guest_xex_header() const { return guest_xex_header_; }
|
||||
|
||||
uint32_t entry_point() const { return entry_point_; }
|
||||
uint32_t stack_size() const { return stack_size_; }
|
||||
|
||||
X_STATUS LoadFromFile(std::string path);
|
||||
X_STATUS LoadFromMemory(const void* addr, const size_t length);
|
||||
|
@ -36,6 +44,16 @@ class XUserModule : public XModule {
|
|||
X_STATUS GetSection(const char* name, uint32_t* out_section_data,
|
||||
uint32_t* out_section_size) override;
|
||||
|
||||
// Get optional header - FOR HOST USE ONLY!
|
||||
X_STATUS GetOptHeader(xe_xex2_header_keys key, void** out_ptr);
|
||||
|
||||
// Get optional header - FOR HOST USE ONLY!
|
||||
template <typename T>
|
||||
X_STATUS GetOptHeader(xe_xex2_header_keys key, T* out_ptr) {
|
||||
return GetOptHeader(key, reinterpret_cast<void**>(out_ptr));
|
||||
}
|
||||
|
||||
// Get optional header that can safely be returned to guest code.
|
||||
X_STATUS GetOptHeader(xe_xex2_header_keys key,
|
||||
uint32_t* out_header_guest_ptr);
|
||||
static X_STATUS GetOptHeader(uint8_t* membase, const xex2_header* header,
|
||||
|
@ -47,8 +65,10 @@ class XUserModule : public XModule {
|
|||
void Dump();
|
||||
|
||||
private:
|
||||
xe_xex2_ref xex_;
|
||||
uint32_t xex_header_;
|
||||
uint32_t guest_xex_header_;
|
||||
|
||||
uint32_t entry_point_;
|
||||
uint32_t stack_size_;
|
||||
};
|
||||
|
||||
} // namespace kernel
|
||||
|
|
|
@ -27,13 +27,7 @@
|
|||
#include "xenia/base/memory.h"
|
||||
#include "xenia/base/platform.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
uint32_t xex2_get_header_size(const xex2_header* header) {
|
||||
return header->exe_offset;
|
||||
}
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
namespace xe {} // namespace xe
|
||||
|
||||
// TODO(benvanik): remove.
|
||||
#define XEEXPECTZERO(expr) \
|
||||
|
@ -251,56 +245,59 @@ int xe_xex2_read_header(const uint8_t* addr, const size_t length,
|
|||
// size = xe::load_and_swap<uint32_t>(pp + 0x04);
|
||||
} break;
|
||||
case XEX_HEADER_IMPORT_LIBRARIES: {
|
||||
const size_t max_count = xe::countof(header->import_libraries);
|
||||
size_t count = xe::load_and_swap<uint32_t>(pp + 0x08);
|
||||
auto import_libraries =
|
||||
reinterpret_cast<const xe::xex2_opt_import_libraries*>(pp);
|
||||
|
||||
const uint32_t max_count =
|
||||
(uint32_t)xe::countof(header->import_libraries);
|
||||
uint32_t count = import_libraries->library_count;
|
||||
assert_true(count <= max_count);
|
||||
if (count > max_count) {
|
||||
XELOGW("ignoring %zu extra entries in XEX_HEADER_IMPORT_LIBRARIES",
|
||||
(max_count - count));
|
||||
(max_count - import_libraries->library_count));
|
||||
count = max_count;
|
||||
}
|
||||
header->import_library_count = count;
|
||||
|
||||
uint32_t string_table_size = xe::load_and_swap<uint32_t>(pp + 0x04);
|
||||
const char* string_table = (const char*)(pp + 0x0C);
|
||||
uint32_t string_table_size = import_libraries->string_table_size;
|
||||
const char* string_table[32]; // Pretend 32 is max_count
|
||||
std::memset(string_table, 0, sizeof(string_table));
|
||||
|
||||
pp += 12 + string_table_size;
|
||||
// Parse the string table
|
||||
for (size_t i = 0, j = 0; i < string_table_size; j++) {
|
||||
const char* str = import_libraries->string_table + i;
|
||||
|
||||
string_table[j] = str;
|
||||
i += std::strlen(str) + 1;
|
||||
|
||||
// Padding
|
||||
if ((i % 4) != 0) {
|
||||
i += 4 - (i % 4);
|
||||
}
|
||||
}
|
||||
|
||||
pp += 12 + import_libraries->string_table_size;
|
||||
for (size_t m = 0; m < count; m++) {
|
||||
xe_xex2_import_library_t* library = &header->import_libraries[m];
|
||||
auto src_library = (xe::xex2_import_library*)pp;
|
||||
|
||||
memcpy(library->digest, pp + 0x04, 20);
|
||||
library->import_id = xe::load_and_swap<uint32_t>(pp + 0x18);
|
||||
library->version.value = xe::load_and_swap<uint32_t>(pp + 0x1C);
|
||||
library->min_version.value = xe::load_and_swap<uint32_t>(pp + 0x20);
|
||||
library->import_id = src_library->id;
|
||||
library->version.value = src_library->version.value;
|
||||
library->min_version.value = src_library->version_min.value;
|
||||
|
||||
const uint16_t name_index =
|
||||
xe::load_and_swap<uint16_t>(pp + 0x24) & 0xFF;
|
||||
for (size_t i = 0, j = 0; i < string_table_size;) {
|
||||
assert_true(j <= 0xFF);
|
||||
if (j == name_index) {
|
||||
std::strncpy(library->name, string_table + i,
|
||||
xe::countof(library->name));
|
||||
break;
|
||||
}
|
||||
if (string_table[i] == 0) {
|
||||
i++;
|
||||
if (i % 4) {
|
||||
i += 4 - (i % 4);
|
||||
}
|
||||
j++;
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
std::strncpy(library->name, string_table[src_library->name_index],
|
||||
xe::countof(library->name));
|
||||
|
||||
library->record_count = xe::load_and_swap<uint16_t>(pp + 0x26);
|
||||
library->record_count = src_library->count;
|
||||
library->records =
|
||||
(uint32_t*)calloc(library->record_count, sizeof(uint32_t));
|
||||
XEEXPECTNOTNULL(library->records);
|
||||
pp += 0x28;
|
||||
for (size_t i = 0; i < library->record_count; i++) {
|
||||
library->records[i] = xe::load_and_swap<uint32_t>(pp);
|
||||
pp += 4;
|
||||
library->records[i] = src_library->import_table[i];
|
||||
}
|
||||
|
||||
pp += src_library->size;
|
||||
}
|
||||
} break;
|
||||
case XEX_HEADER_STATIC_LIBRARIES: {
|
||||
|
@ -1058,17 +1055,17 @@ uint32_t xe_xex2_lookup_export(xe_xex2_ref xex, uint16_t ordinal) {
|
|||
|
||||
// XEX-style export table.
|
||||
if (header->loader_info.export_table) {
|
||||
auto export_table = reinterpret_cast<const xe_xex2_export_table*>(
|
||||
auto export_table = reinterpret_cast<const xe::xex2_export_table*>(
|
||||
xex->memory->TranslateVirtual(header->loader_info.export_table));
|
||||
uint32_t ordinal_count = xe::byte_swap(export_table->count);
|
||||
uint32_t ordinal_base = xe::byte_swap(export_table->base);
|
||||
uint32_t ordinal_count = export_table->count;
|
||||
uint32_t ordinal_base = export_table->base;
|
||||
if (ordinal > ordinal_count) {
|
||||
XELOGE("xe_xex2_lookup_export: ordinal out of bounds");
|
||||
return 0;
|
||||
}
|
||||
uint32_t i = ordinal - ordinal_base;
|
||||
uint32_t ordinal_offset = xe::byte_swap(export_table->ordOffset[i]);
|
||||
ordinal_offset += xe::byte_swap(export_table->imagebaseaddr) << 16;
|
||||
uint32_t ordinal_offset = export_table->ordOffset[i];
|
||||
ordinal_offset += export_table->imagebaseaddr << 16;
|
||||
return ordinal_offset;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,11 +13,7 @@
|
|||
#include "xenia/kernel/util/xex2_info.h"
|
||||
#include "xenia/memory.h"
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
uint32_t xex2_get_header_size(const xex2_header* header);
|
||||
} // namespace kernel
|
||||
} // namespace xe
|
||||
namespace xe {} // namespace xe
|
||||
|
||||
typedef struct { int reserved; } xe_xex2_options_t;
|
||||
|
||||
|
|
|
@ -297,18 +297,6 @@ typedef struct {
|
|||
xe_xex2_approval_type approval;
|
||||
} xe_xex2_static_library_t;
|
||||
|
||||
// credits: some obscure pastebin (http://pastebin.com/ZRvr3Sgj)
|
||||
typedef struct {
|
||||
uint32_t magic[3];
|
||||
uint32_t modulenumber[2];
|
||||
uint32_t version[3];
|
||||
uint32_t imagebaseaddr; // must be <<16 to be accurate
|
||||
uint32_t count;
|
||||
uint32_t base;
|
||||
uint32_t ordOffset[1]; // ordOffset[0] + (imagebaseaddr << 16) = function
|
||||
// offset of ordinal 1
|
||||
} xe_xex2_export_table;
|
||||
|
||||
typedef enum {
|
||||
XEX_ENCRYPTION_NONE = 0,
|
||||
XEX_ENCRYPTION_NORMAL = 1,
|
||||
|
@ -470,9 +458,8 @@ typedef struct {
|
|||
} xe_xex2_header_t;
|
||||
|
||||
namespace xe {
|
||||
namespace kernel {
|
||||
union xex2_version {
|
||||
uint32_t value;
|
||||
xe::be<uint32_t> value;
|
||||
struct {
|
||||
uint32_t major : 4;
|
||||
uint32_t minor : 4;
|
||||
|
@ -481,6 +468,73 @@ union xex2_version {
|
|||
};
|
||||
};
|
||||
|
||||
struct xex2_opt_bound_path {
|
||||
xe::be<uint32_t> size;
|
||||
char path[1];
|
||||
};
|
||||
|
||||
struct xex2_opt_static_library {
|
||||
char name[8]; // 0x0
|
||||
xe::be<uint16_t> version_major; // 0x8
|
||||
xe::be<uint16_t> version_minor; // 0xA
|
||||
xe::be<uint16_t> version_build; // 0xC
|
||||
xe::be<uint8_t> approval_type; // 0xE
|
||||
xe::be<uint8_t> version_qfe; // 0xF
|
||||
};
|
||||
static_assert_size(xex2_opt_static_library, 0x10);
|
||||
|
||||
struct xex2_opt_static_libraries {
|
||||
xe::be<uint32_t> size; // 0x0
|
||||
xex2_opt_static_library libraries[1]; // 0x4
|
||||
};
|
||||
|
||||
struct xex2_opt_original_pe_name {
|
||||
xe::be<uint32_t> size;
|
||||
char name[1];
|
||||
};
|
||||
|
||||
struct xex2_opt_data_directory {
|
||||
xe::be<uint32_t> offset; // 0x0
|
||||
xe::be<uint32_t> size; // 0x4
|
||||
};
|
||||
static_assert_size(xex2_opt_data_directory, 0x8);
|
||||
|
||||
struct xex2_opt_tls_info {
|
||||
xe::be<uint32_t> slot_count; // 0x0
|
||||
xe::be<uint32_t> raw_data_address; // 0x4
|
||||
xe::be<uint32_t> data_size; // 0x8
|
||||
xe::be<uint32_t> raw_data_size; // 0xC
|
||||
};
|
||||
static_assert_size(xex2_opt_tls_info, 0x10);
|
||||
|
||||
struct xex2_resource {
|
||||
char name[8]; // 0x0
|
||||
xe::be<uint32_t> address; // 0x8
|
||||
xe::be<uint32_t> size; // 0xC
|
||||
};
|
||||
static_assert_size(xex2_resource, 0x10);
|
||||
|
||||
struct xex2_opt_resource_info {
|
||||
xe::be<uint32_t> size; // 0x0 Resource count is (size - 4) / 16
|
||||
xex2_resource resources[1]; // 0x4
|
||||
};
|
||||
|
||||
struct xex2_opt_delta_patch_descriptor {
|
||||
xe::be<uint32_t> size; // 0x0
|
||||
xex2_version target_version; // 0x4
|
||||
xex2_version source_version; // 0x8
|
||||
char digest_source[0x14]; // 0xC
|
||||
char image_key_source[0x10]; // 0x20
|
||||
xe::be<uint32_t> size_of_target_headers; // 0x30
|
||||
xe::be<uint32_t> delta_headers_source_offset; // 0x34
|
||||
xe::be<uint32_t> delta_headers_source_size; // 0x38
|
||||
xe::be<uint32_t> delta_headers_target_offset; // 0x3C
|
||||
xe::be<uint32_t> delta_image_source_offset; // 0x40
|
||||
xe::be<uint32_t> delta_image_source_size; // 0x44
|
||||
xe::be<uint32_t> delta_image_target_offset; // 0x48
|
||||
};
|
||||
static_assert_size(xex2_opt_delta_patch_descriptor, 0x4C);
|
||||
|
||||
struct xex2_opt_execution_info {
|
||||
xe::be<uint32_t> media_id; // 0x0
|
||||
xe::be<xex2_version> version; // 0x4
|
||||
|
@ -492,32 +546,103 @@ struct xex2_opt_execution_info {
|
|||
uint8_t disc_count; // 0x13
|
||||
xe::be<uint32_t> savegame_id; // 0x14
|
||||
};
|
||||
static_assert_size(xex2_opt_execution_info, 0x18);
|
||||
|
||||
struct xex2_opt_import_libraries {
|
||||
xe::be<uint32_t> section_size; // 0x0
|
||||
xe::be<uint32_t> string_table_size; // 0x4
|
||||
xe::be<uint32_t> library_count; // 0x8
|
||||
char string_table[1]; // 0xC string_table_size bytes
|
||||
};
|
||||
|
||||
struct xex2_import_library {
|
||||
xe::be<uint32_t> size; // 0x0
|
||||
char next_import_digest[0x14]; // 0x4
|
||||
xe::be<uint32_t> id; // 0x18
|
||||
xex2_version version; // 0x1C
|
||||
xex2_version version_min; // 0x20
|
||||
xe::be<uint16_t> name_index; // 0x24
|
||||
xe::be<uint16_t> count; // 0x26
|
||||
xe::be<uint32_t> import_table[1]; // 0x28
|
||||
};
|
||||
|
||||
struct xex2_opt_header {
|
||||
xe::be<uint32_t> key; // 0x0
|
||||
|
||||
union {
|
||||
xe::be<uint32_t> value; // 0x4
|
||||
xe::be<uint32_t> offset; // 0x8
|
||||
xe::be<uint32_t> offset; // 0x4
|
||||
};
|
||||
};
|
||||
|
||||
struct xex2_header {
|
||||
xe::be<uint32_t> magic; // 0x0 'XEX2'
|
||||
xe::be<xe_xex2_module_flags> module_flags; // 0x4
|
||||
xe::be<uint32_t> exe_offset; // 0x8
|
||||
xe::be<uint32_t> header_size; // 0x8
|
||||
xe::be<uint32_t> reserved; // 0xC
|
||||
xe::be<uint32_t> certificate_offset; // 0x10
|
||||
xe::be<uint32_t> security_offset; // 0x10
|
||||
xe::be<uint32_t> header_count; // 0x14
|
||||
|
||||
xex2_opt_header headers[1]; // 0x18
|
||||
};
|
||||
|
||||
struct xex2_loader_info {
|
||||
xe::be<uint32_t> header_size;
|
||||
xe::be<uint32_t> image_size;
|
||||
struct xex2_page_descriptor {
|
||||
union {
|
||||
struct {
|
||||
uint32_t info : 4;
|
||||
uint32_t size : 28;
|
||||
};
|
||||
xe::be<uint32_t> value; // 0x0
|
||||
};
|
||||
char data_digest[0x14]; // 0x4
|
||||
};
|
||||
} // namespace kernel
|
||||
|
||||
struct xex2_security_info {
|
||||
xe::be<uint32_t> header_size; // 0x0
|
||||
xe::be<uint32_t> image_size; // 0x4
|
||||
char rsa_signature[0x100]; // 0x8
|
||||
xe::be<uint32_t> unk_108; // 0x108 unk length
|
||||
xe::be<uint32_t> image_flags; // 0x10C
|
||||
xe::be<uint32_t> load_address; // 0x110
|
||||
char section_digest[0x14]; // 0x114
|
||||
xe::be<uint32_t> import_table_count; // 0x128
|
||||
char import_table_digest[0x14]; // 0x12C
|
||||
char xgd2_media_id[0x10]; // 0x140
|
||||
char aes_key[0x10]; // 0x150
|
||||
xe::be<uint32_t> export_table; // 0x160
|
||||
char header_digest[0x14]; // 0x164
|
||||
xe::be<uint32_t> region; // 0x178
|
||||
xe::be<uint32_t> allowed_media_types; // 0x17C
|
||||
xe::be<uint32_t> page_descriptor_count; // 0x180
|
||||
xex2_page_descriptor page_descriptors[1]; // 0x184
|
||||
};
|
||||
|
||||
struct xex2_export_table {
|
||||
xe::be<uint32_t> magic[3]; // 0x0
|
||||
xe::be<uint32_t> modulenumber[2]; // 0xC
|
||||
xe::be<uint32_t> version[3]; // 0x14
|
||||
xe::be<uint32_t> imagebaseaddr; // 0x20 must be <<16 to be accurate
|
||||
xe::be<uint32_t> count; // 0x24
|
||||
xe::be<uint32_t> base; // 0x28
|
||||
xe::be<uint32_t>
|
||||
ordOffset[1]; // 0x2C ordOffset[0] + (imagebaseaddr << 16) = function
|
||||
};
|
||||
|
||||
// Little endian PE export directory (from winnt.h)
|
||||
struct X_IMAGE_EXPORT_DIRECTORY {
|
||||
uint32_t Characteristics;
|
||||
uint32_t TimeDateStamp;
|
||||
uint16_t MajorVersion;
|
||||
uint16_t MinorVersion;
|
||||
uint32_t Name;
|
||||
uint32_t Base;
|
||||
uint32_t NumberOfFunctions;
|
||||
uint32_t NumberOfNames;
|
||||
uint32_t AddressOfFunctions; // RVA from base of image
|
||||
uint32_t AddressOfNames; // RVA from base of image
|
||||
uint32_t AddressOfNameOrdinals; // RVA from base of image
|
||||
};
|
||||
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_KERNEL_XEX2_INFO_H_
|
||||
|
|
|
@ -141,12 +141,11 @@ SHIM_CALL XexCheckExecutablePrivilege_shim(PPCContext* ppc_context,
|
|||
SHIM_SET_RETURN_32(0);
|
||||
return;
|
||||
}
|
||||
xe_xex2_ref xex = module->xex();
|
||||
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex);
|
||||
uint32_t result = (header->system_flags & mask) > 0;
|
||||
uint32_t flags = 0;
|
||||
module->GetOptHeader<uint32_t>(XEX_HEADER_SYSTEM_FLAGS, &flags);
|
||||
|
||||
SHIM_SET_RETURN_32(result);
|
||||
SHIM_SET_RETURN_32((flags & mask) > 0);
|
||||
}
|
||||
|
||||
SHIM_CALL XexGetModuleHandle_shim(PPCContext* ppc_context,
|
||||
|
|
|
@ -109,8 +109,7 @@ SHIM_CALL ExCreateThread_shim(PPCContext* ppc_context,
|
|||
|
||||
// Inherit default stack size
|
||||
if (stack_size == 0) {
|
||||
stack_size =
|
||||
kernel_state->GetExecutableModule()->xex_header()->exe_stack_size;
|
||||
stack_size = kernel_state->GetExecutableModule()->stack_size();
|
||||
}
|
||||
|
||||
// Stack must be aligned to 16kb pages
|
||||
|
|
Loading…
Reference in New Issue