[CPU] Add XEXP support to XexModule, if XEXP is in same folder as XEX
This was a headache to work out, big thanks to the lack of documentation on .xexp files... a ton of guesswork was involved here but luckily it turned out well. I did have to make some pretty major changes to the way XEX files are loaded though. Previously it'd just load everything in one go: XEX headers -> decrypt/decompress data -> load imports/symbols -> set loader data table entries, etc... Now it's changed to something like this: - Load base XEX headers + decrypted/decompressed image data, return X_STATUS_PENDING - In the LoadFromFile call used to load the XEX, search for XEXP patch file (only .xexp in same folder atm) - If patch exists: load XEXP, decrypt headers/data, apply patch to base XEX, dispose of XEXP - Finish XEX load via LoadXexContinue() (handles imports/symbols/loader data...) This saves us from needing to reset the imports/function/symbol stuff after patching (since all the XEX code will be a lot different), but I'm not really sure if I went about it the best way.
This commit is contained in:
@@ -95,7 +95,36 @@ X_STATUS UserModule::LoadFromFile(std::string path) {
|
||||
file->Destroy();
|
||||
}
|
||||
|
||||
return result;
|
||||
// Only XEX returns X_STATUS_PENDING
|
||||
if (result != X_STATUS_PENDING) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Search for xexp patch file
|
||||
auto patch_entry = kernel_state()->file_system()->ResolvePath(path_ + "p");
|
||||
|
||||
if (patch_entry) {
|
||||
auto patch_path = patch_entry->absolute_path();
|
||||
|
||||
XELOGI("Loading XEX patch from %s", patch_path.c_str());
|
||||
|
||||
auto patch_module = object_ref<UserModule>(new UserModule(kernel_state_));
|
||||
result = patch_module->LoadFromFile(patch_path);
|
||||
if (!result) {
|
||||
result = patch_module->xex_module()->ApplyPatch(xex_module());
|
||||
if (result) {
|
||||
XELOGE("Failed to apply XEX patch, code: %d", result);
|
||||
}
|
||||
} else {
|
||||
XELOGE("Failed to load XEX patch, code: %d", result);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
}
|
||||
|
||||
return LoadXexContinue();
|
||||
}
|
||||
|
||||
X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||
@@ -130,29 +159,13 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Copy the xex2 header into guest memory.
|
||||
auto header = this->xex_module()->xex_header();
|
||||
auto security_header = this->xex_module()->xex_security_info();
|
||||
guest_xex_header_ = memory()->SystemHeapAlloc(header->header_size);
|
||||
// Only XEX headers + image are loaded right now
|
||||
// Caller will have to call LoadXexContinue after they've loaded in a patch
|
||||
// (or after patch isn't found anywhere)
|
||||
// or if this is an XEXP being loaded return success since there's nothing
|
||||
// else to load
|
||||
return this->xex_module()->is_patch() ? X_STATUS_SUCCESS : X_STATUS_PENDING;
|
||||
|
||||
uint8_t* xex_header_ptr = memory()->TranslateVirtual(guest_xex_header_);
|
||||
std::memcpy(xex_header_ptr, header, header->header_size);
|
||||
|
||||
// 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_);
|
||||
is_dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE);
|
||||
|
||||
// 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_;
|
||||
ldr_data->full_image_size = security_header->image_size;
|
||||
ldr_data->image_base = this->xex_module()->base_address();
|
||||
ldr_data->entry_point = entry_point_;
|
||||
} else if (module_format_ == kModuleFormatElf) {
|
||||
auto elf_module =
|
||||
std::make_unique<cpu::ElfModule>(processor, kernel_state());
|
||||
@@ -175,6 +188,52 @@ X_STATUS UserModule::LoadFromMemory(const void* addr, const size_t length) {
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
X_STATUS UserModule::LoadXexContinue() {
|
||||
// LoadXexContinue: finishes loading XEX after a patch has been applied (or
|
||||
// patch wasn't found)
|
||||
|
||||
if (!this->xex_module()) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// If guest_xex_header is set we must have already loaded the XEX
|
||||
if (guest_xex_header_) {
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Finish XexModule load (PE sections/imports/symbols...)
|
||||
if (!xex_module()->LoadContinue()) {
|
||||
return X_STATUS_UNSUCCESSFUL;
|
||||
}
|
||||
|
||||
// Copy the xex2 header into guest memory.
|
||||
auto header = this->xex_module()->xex_header();
|
||||
auto security_header = this->xex_module()->xex_security_info();
|
||||
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);
|
||||
|
||||
// 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_);
|
||||
is_dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE);
|
||||
|
||||
// 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_;
|
||||
ldr_data->full_image_size = security_header->image_size;
|
||||
ldr_data->image_base = this->xex_module()->base_address();
|
||||
ldr_data->entry_point = entry_point_;
|
||||
|
||||
OnLoad();
|
||||
|
||||
return X_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
X_STATUS UserModule::Unload() {
|
||||
if (module_format_ == kModuleFormatXex &&
|
||||
(!processor_module_ || !xex_module()->loaded())) {
|
||||
|
||||
Reference in New Issue
Block a user