Files
Xenia-Canary/src/xenia/kernel/objects/xuser_module.cc

405 lines
14 KiB
C++

/**
******************************************************************************
* 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/kernel/objects/xuser_module.h"
#include "xenia/base/logging.h"
#include "xenia/cpu/cpu.h"
#include "xenia/emulator.h"
#include "xenia/kernel/objects/xfile.h"
#include "xenia/kernel/objects/xthread.h"
namespace xe {
namespace kernel {
using namespace xe::cpu;
XUserModule::XUserModule(KernelState* kernel_state, const char* path)
: XModule(kernel_state, path), xex_(nullptr), execution_info_ptr_(0) {}
XUserModule::~XUserModule() {
kernel_state()->memory()->SystemHeapFree(execution_info_ptr_);
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_);
}
X_STATUS XUserModule::LoadFromFile(std::string path) {
X_STATUS result = X_STATUS_UNSUCCESSFUL;
XFile* file = NULL;
// Resolve the file to open.
// TODO(benvanik): make this code shared?
auto fs_entry = kernel_state()->file_system()->ResolvePath(path);
if (!fs_entry) {
XELOGE("File not found: %s", path.c_str());
return X_STATUS_NO_SUCH_FILE;
}
// If the FS supports mapping, map the file in and load from that.
if (fs_entry->can_map()) {
// Map.
auto mmap = fs_entry->CreateMemoryMapping(fs::Mode::READ, 0, 0);
if (!mmap) {
if (file) {
file->Release();
}
return result;
}
// Load the module.
result = LoadFromMemory(mmap->address(), mmap->length());
} else {
XFileInfo file_info;
result = fs_entry->QueryInfo(&file_info);
if (result) {
if (file) {
file->Release();
}
return result;
}
std::vector<uint8_t> buffer(file_info.file_length);
// Open file for reading.
result = kernel_state()->file_system()->Open(
std::move(fs_entry), kernel_state(), fs::Mode::READ, false, &file);
if (result) {
if (file) {
file->Release();
}
return result;
}
// Read entire file into memory.
// Ugh.
size_t bytes_read = 0;
result = file->Read(buffer.data(), buffer.size(), 0, &bytes_read);
if (result) {
if (file) {
file->Release();
}
return result;
}
// Load the module.
result = LoadFromMemory(buffer.data(), bytes_read);
}
if (file) {
file->Release();
}
return result;
}
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;
}
// Store execution info for later use.
// TODO(benvanik): just put entire xex header in memory somewhere?
execution_info_ptr_ = memory()->SystemHeapAlloc(24);
auto eip = memory()->TranslateVirtual(execution_info_ptr_);
const auto& ex = xe_xex2_get_header(xex_)->execution_info;
xe::store_and_swap<uint32_t>(eip + 0x00, ex.media_id);
xe::store_and_swap<uint32_t>(eip + 0x04, ex.version.value);
xe::store_and_swap<uint32_t>(eip + 0x08, ex.base_version.value);
xe::store_and_swap<uint32_t>(eip + 0x0C, ex.title_id);
xe::store_and_swap<uint8_t>(eip + 0x10, ex.platform);
xe::store_and_swap<uint8_t>(eip + 0x11, ex.executable_table);
xe::store_and_swap<uint8_t>(eip + 0x12, ex.disc_number);
xe::store_and_swap<uint8_t>(eip + 0x13, ex.disc_count);
xe::store_and_swap<uint32_t>(eip + 0x14, ex.savegame_id);
// Prepare the module for execution.
// Runtime takes ownership.
auto xex_module = std::make_unique<XexModule>(processor);
if (!xex_module->Load(name_, path_, xex_)) {
return X_STATUS_UNSUCCESSFUL;
}
if (!processor->AddModule(std::move(xex_module))) {
return X_STATUS_UNSUCCESSFUL;
}
OnLoad();
return X_STATUS_SUCCESS;
}
uint32_t XUserModule::GetProcAddressByOrdinal(uint16_t ordinal) {
return xe_xex2_lookup_export(xex_, ordinal);
}
uint32_t XUserModule::GetProcAddressByName(const char* name) {
return xe_xex2_lookup_export(xex_, 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];
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::Launch(uint32_t flags) {
const xe_xex2_header_t* header = xex_header();
XELOGI("Launching module...");
Dump();
// 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("Could not create launch thread: %.8X", result);
return result;
}
// Wait until thread completes.
thread->Wait(0, 0, 0, NULL);
thread->Release();
return X_STATUS_SUCCESS;
}
void XUserModule::Dump() {
xe::cpu::ExportResolver* export_resolver =
kernel_state_->emulator()->export_resolver();
const xe_xex2_header_t* header = xe_xex2_get_header(xex_);
// 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);
}
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];
const char* type = "UNKNOWN";
switch (section->info.type) {
case XEX_SECTION_CODE:
type = "CODE ";
break;
case XEX_SECTION_DATA:
type = "RWDATA ";
break;
case XEX_SECTION_READONLY_DATA:
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");
// 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);
}
}
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.
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];
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)) {
printf(" %s - %d imports\n", library->name, (int)import_info_count);
printf(" Version: %d.%d.%d.%d\n", library->version.major,
library->version.minor, library->version.build,
library->version.qfe);
printf(" Min Version: %d.%d.%d.%d\n", library->min_version.major,
library->min_version.minor, library->min_version.build,
library->min_version.qfe);
printf("\n");
// Counts.
int known_count = 0;
int unknown_count = 0;
int impl_count = 0;
int unimpl_count = 0;
for (size_t m = 0; m < import_info_count; m++) {
const xe_xex2_import_info_t* info = &import_infos[m];
KernelExport* kernel_export =
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
if (kernel_export) {
known_count++;
if (kernel_export->is_implemented) {
impl_count++;
} else {
unimpl_count++;
}
} else {
unknown_count++;
unimpl_count++;
}
}
printf(" Total: %4u\n", uint32_t(import_info_count));
printf(" Known: %3d%% (%d known, %d unknown)\n",
(int)(known_count / (float)import_info_count * 100.0f),
known_count, unknown_count);
printf(" Implemented: %3d%% (%d implemented, %d unimplemented)\n",
(int)(impl_count / (float)import_info_count * 100.0f), impl_count,
unimpl_count);
printf("\n");
// Listing.
for (size_t m = 0; m < import_info_count; m++) {
const xe_xex2_import_info_t* info = &import_infos[m];
KernelExport* kernel_export =
export_resolver->GetExportByOrdinal(library->name, info->ordinal);
const char* name = "UNKNOWN";
bool implemented = false;
if (kernel_export) {
name = kernel_export->name;
implemented = kernel_export->is_implemented;
}
if (kernel_export && kernel_export->type == KernelExport::Variable) {
printf(" V %.8X %.3X (%3d) %s %s\n", info->value_address,
info->ordinal, info->ordinal, implemented ? " " : "!!", name);
} else if (info->thunk_address) {
printf(" F %.8X %.8X %.3X (%3d) %s %s\n", info->value_address,
info->thunk_address, info->ordinal, info->ordinal,
implemented ? " " : "!!", name);
}
}
}
printf("\n");
}
}
} // namespace kernel
} // namespace xe