From 6cd08384e7534442d6d9a96faf5e7aaa7d3bb64a Mon Sep 17 00:00:00 2001 From: "Dr. Chat" Date: Sun, 16 Aug 2015 02:52:47 -0500 Subject: [PATCH] (Experimental) ELF module support --- src/xenia/cpu/elf_module.cc | 144 +++++++++++++++++++++++ src/xenia/cpu/elf_module.h | 55 +++++++++ src/xenia/emulator.cc | 3 +- src/xenia/kernel/objects/xuser_module.cc | 115 +++++++++++++----- src/xenia/kernel/objects/xuser_module.h | 19 ++- 5 files changed, 302 insertions(+), 34 deletions(-) create mode 100644 src/xenia/cpu/elf_module.cc create mode 100644 src/xenia/cpu/elf_module.h diff --git a/src/xenia/cpu/elf_module.cc b/src/xenia/cpu/elf_module.cc new file mode 100644 index 000000000..7de8fa281 --- /dev/null +++ b/src/xenia/cpu/elf_module.cc @@ -0,0 +1,144 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#include "xenia/cpu/elf_module.h" + +#include + +#include "xenia/base/byte_order.h" +#include "xenia/base/logging.h" + +#include "xenia/cpu/processor.h" + +namespace xe { +namespace cpu { + +ElfModule::ElfModule(Processor* processor, kernel::KernelState* kernel_state) + : Module(processor), kernel_state_(kernel_state) {} + +ElfModule::~ElfModule() {} + +// ELF structures +struct elf32_ehdr { + uint8_t e_ident[16]; + xe::be e_type; + xe::be e_machine; + xe::be e_version; + xe::be e_entry; + xe::be e_phoff; + xe::be e_shoff; + xe::be e_flags; + xe::be e_ehsize; + xe::be e_phentsize; + xe::be e_phnum; + xe::be e_shentsize; + xe::be e_shnum; + xe::be e_shtrndx; +}; + +struct elf32_phdr { + xe::be p_type; + xe::be p_offset; + xe::be p_vaddr; + xe::be p_paddr; + xe::be p_filesz; + xe::be p_memsz; + xe::be p_flags; + xe::be p_align; +}; + +bool ElfModule::Load(const std::string& name, const std::string& path, + const void* elf_addr, size_t elf_length) { + name_ = name; + path_ = path; + + uint8_t* pelf = (uint8_t*)elf_addr; + elf32_ehdr* hdr = (elf32_ehdr*)(pelf + 0x0); + if (hdr->e_ident[0] != 0x7F || hdr->e_ident[1] != 'E' || + hdr->e_ident[2] != 'L' || hdr->e_ident[3] != 'F') { + // Not an ELF file! + return false; + } + + assert_true(hdr->e_ident[4] == 1); // 32bit + + if (hdr->e_type != 2 /* ET_EXEC */) { + // Not executable (shared objects not supported yet) + XELOGE("ELF: Could not load ELF because it isn't executable!"); + return false; + } + + if (hdr->e_machine != 20 /* EM_PPC */) { + // Not a PPC ELF! + XELOGE( + "ELF: Could not load ELF because target machine is not PPC! (target: " + "%d)", + hdr->e_machine); + return false; + } + + // Parse LOAD program headers and load into memory. + if (!hdr->e_phoff) { + XELOGE("ELF: File doesn't have a program header!"); + return false; + } + + if (!hdr->e_entry) { + XELOGE("ELF: Executable has no entry point!"); + return false; + } + + // Entry point virtual address + entry_point_ = hdr->e_entry; + + // Copy the ELF header + elf_header_mem_.resize(hdr->e_ehsize); + std::memcpy(elf_header_mem_.data(), hdr, hdr->e_ehsize); + + assert_true(hdr->e_phentsize == sizeof(elf32_phdr)); + elf32_phdr* phdr = (elf32_phdr*)(pelf + hdr->e_phoff); + for (uint32_t i = 0; i < hdr->e_phnum; i++) { + if (phdr[i].p_type == 1 /* PT_LOAD */) { + // Allocate and copy into memory. + // Base address @ 0x80000000 + uint32_t virtual_addr = phdr[i].p_vaddr < 0x80000000 + ? phdr[i].p_vaddr + 0x80000000 + : phdr[i].p_vaddr; + if (!memory() + ->LookupHeap(virtual_addr) + ->AllocFixed( + virtual_addr, phdr[i].p_memsz, phdr[i].p_align, + xe::kMemoryAllocationReserve | xe::kMemoryAllocationCommit, + xe::kMemoryProtectRead | xe::kMemoryProtectWrite)) { + XELOGE("ELF: Could not allocate memory!"); + } + + auto p = memory()->TranslateVirtual(virtual_addr); + std::memset(p, 0, phdr[i].p_memsz); + std::memcpy(p, pelf + phdr[i].p_offset, + std::min(phdr[i].p_memsz, phdr[i].p_filesz)); + + // Notify backend about executable code. + if (phdr[i].p_flags & 0x1 /* PF_X */) { + processor_->backend()->CommitExecutableRange( + virtual_addr, virtual_addr + phdr[i].p_memsz); + } + } + } + + return true; +} + +std::unique_ptr ElfModule::CreateFunction(uint32_t address) { + return std::unique_ptr( + processor_->backend()->CreateGuestFunction(this, address)); +} + +} // namespace cpu +} // namespace xe \ No newline at end of file diff --git a/src/xenia/cpu/elf_module.h b/src/xenia/cpu/elf_module.h new file mode 100644 index 000000000..8b8e50e18 --- /dev/null +++ b/src/xenia/cpu/elf_module.h @@ -0,0 +1,55 @@ +/** + ****************************************************************************** + * Xenia : Xbox 360 Emulator Research Project * + ****************************************************************************** + * Copyright 2015 Ben Vanik. All rights reserved. * + * Released under the BSD license - see LICENSE in the root for more details. * + ****************************************************************************** + */ + +#ifndef XENIA_CPU_ELF_MODULE_H_ +#define XENIA_CPU_ELF_MODULE_H_ + +#include + +#include "xenia/cpu/module.h" + +namespace xe { +namespace kernel { +class KernelState; +} // namespace kernel + +namespace cpu { + +// ELF module: Used to load libxenon executables. +class ElfModule : public xe::cpu::Module { + public: + ElfModule(Processor* processor, kernel::KernelState* kernel_state); + virtual ~ElfModule(); + + bool loaded() const { return loaded_; } + uint32_t entry_point() const { return entry_point_; } + const std::string& name() const { return name_; } + const std::string& path() const { return path_; } + + bool Load(const std::string& name, const std::string& path, + const void* elf_addr, size_t elf_length); + bool Unload(); + + protected: + std::unique_ptr CreateFunction(uint32_t address) override; + + private: + std::string name_; + std::string path_; + kernel::KernelState* kernel_state_; + + bool loaded_; + std::vector elf_header_mem_; // Holds the ELF header + uint32_t entry_point_; +}; + +} // namespace cpu +} // namespace xe + +#endif // XENIA_CPU_ELF_MODULE_H_ \ No newline at end of file diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index d8cfd57ca..bd5769a55 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -187,7 +187,8 @@ X_STATUS Emulator::LaunchPath(std::wstring path) { if (last_dot == std::wstring::npos) { // Likely an STFS container. return LaunchStfsContainer(path); - } else if (path.substr(last_dot) == L".xex") { + } else if (path.substr(last_dot) == L".xex" || + path.substr(last_dot) == L".elf") { // Treat as a naked xex file. return LaunchXexFile(path); } else { diff --git a/src/xenia/kernel/objects/xuser_module.cc b/src/xenia/kernel/objects/xuser_module.cc index 7b2400b86..9f4499264 100644 --- a/src/xenia/kernel/objects/xuser_module.cc +++ b/src/xenia/kernel/objects/xuser_module.cc @@ -12,6 +12,7 @@ #include #include "xenia/base/logging.h" +#include "xenia/cpu/elf_module.h" #include "xenia/cpu/processor.h" #include "xenia/cpu/xex_module.h" #include "xenia/emulator.h" @@ -45,8 +46,16 @@ X_STATUS XUserModule::LoadFromFile(std::string path) { return result; } + ModuleFormat fmt; + auto magic = *(xe::be*)mmap->data(); + if (magic == 'XEX2') { + fmt = kModuleFormatXex; + } else if (magic == 0x7F454C46 /* 0x7F 'ELF' */) { + fmt = kModuleFormatElf; + } + // Load the module. - result = LoadFromMemory(mmap->data(), mmap->size()); + result = LoadFromMemory(mmap->data(), mmap->size(), fmt); } else { std::vector buffer(fs_entry->size()); @@ -66,45 +75,74 @@ X_STATUS XUserModule::LoadFromFile(std::string path) { return result; } + ModuleFormat fmt; + uint32_t magic = *(uint32_t*)buffer.data(); + if (magic == 'XEX2') { + fmt = kModuleFormatXex; + } else if (magic == 0x7F454C46 /* 0x7F 'ELF' */) { + fmt = kModuleFormatElf; + } + // Load the module. - result = LoadFromMemory(buffer.data(), bytes_read); + result = LoadFromMemory(buffer.data(), bytes_read, fmt); } return result; } -X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length) { +X_STATUS XUserModule::LoadFromMemory(const void* addr, const size_t length, + ModuleFormat module_format) { auto processor = kernel_state()->processor(); + module_format_ = module_format; - // Prepare the module for execution. - // Runtime takes ownership. - auto xex_module = std::make_unique(processor, kernel_state()); - if (!xex_module->Load(name_, path_, addr, length)) { - return X_STATUS_UNSUCCESSFUL; + if (module_format == kModuleFormatXex) { + // Prepare the module for execution. + // Runtime takes ownership. + auto xex_module = + std::make_unique(processor, kernel_state()); + if (!xex_module->Load(name_, path_, addr, length)) { + return X_STATUS_UNSUCCESSFUL; + } + processor_module_ = xex_module.get(); + if (!processor->AddModule(std::move(xex_module))) { + 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(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_); + dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE); + } else if (module_format == kModuleFormatElf) { + auto elf_module = + std::make_unique(processor, kernel_state()); + if (!elf_module->Load(name_, path_, addr, length)) { + return X_STATUS_UNSUCCESSFUL; + } + + entry_point_ = elf_module->entry_point(); + stack_size_ = 1024 * 1024; // 1 MB + dll_module_ = false; // Hardcoded not a DLL (for now) + + processor_module_ = elf_module.get(); + if (!processor->AddModule(std::move(elf_module))) { + return X_STATUS_UNSUCCESSFUL; + } } - processor_module_ = xex_module.get(); - if (!processor->AddModule(std::move(xex_module))) { - 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(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_); - dll_module_ = !!(header->module_flags & XEX_MODULE_DLL_MODULE); OnLoad(); @@ -160,6 +198,11 @@ X_STATUS XUserModule::GetSection(const char* name, uint32_t* out_section_data, X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) { assert_not_null(out_ptr); + if (module_format_ == kModuleFormatElf) { + // Quick die. + return X_STATUS_UNSUCCESSFUL; + } + bool ret = xex_module()->GetOptHeader(key, out_ptr); if (!ret) { return X_STATUS_NOT_FOUND; @@ -170,6 +213,11 @@ X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key, void** out_ptr) { X_STATUS XUserModule::GetOptHeader(xe_xex2_header_keys key, uint32_t* out_header_guest_ptr) { + if (module_format_ == kModuleFormatElf) { + // Quick die. + return X_STATUS_UNSUCCESSFUL; + } + auto header = memory()->TranslateVirtual(guest_xex_header_); if (!header) { @@ -255,6 +303,11 @@ X_STATUS XUserModule::Launch(uint32_t flags) { } void XUserModule::Dump() { + if (module_format_ == kModuleFormatElf) { + // Quick die. + return; + } + xe::cpu::ExportResolver* export_resolver = kernel_state_->emulator()->export_resolver(); auto header = xex_header(); diff --git a/src/xenia/kernel/objects/xuser_module.h b/src/xenia/kernel/objects/xuser_module.h index c260d14c7..61f90b3a4 100644 --- a/src/xenia/kernel/objects/xuser_module.h +++ b/src/xenia/kernel/objects/xuser_module.h @@ -19,6 +19,11 @@ #include "xenia/xbox.h" namespace xe { +namespace cpu { +class XexModule; +class ElfModule; +} // namespace cpu + namespace kernel { class XUserModule : public XModule { @@ -26,10 +31,18 @@ class XUserModule : public XModule { XUserModule(KernelState* kernel_state, const char* path); ~XUserModule() override; + enum ModuleFormat { + kModuleFormatUndefined = 0, + kModuleFormatXex, + kModuleFormatElf, + }; + const xe::cpu::XexModule* xex_module() const { + assert_true(module_format_ == kModuleFormatXex); return reinterpret_cast(processor_module_); } xe::cpu::XexModule* xex_module() { + assert_true(module_format_ == kModuleFormatXex); return reinterpret_cast(processor_module_); } @@ -41,7 +54,8 @@ class XUserModule : public XModule { uint32_t stack_size() const { return stack_size_; } X_STATUS LoadFromFile(std::string path); - X_STATUS LoadFromMemory(const void* addr, const size_t length); + X_STATUS LoadFromMemory(const void* addr, const size_t length, + ModuleFormat module_type); X_STATUS Unload(); uint32_t GetProcAddressByOrdinal(uint16_t ordinal) override; @@ -70,7 +84,8 @@ class XUserModule : public XModule { void Dump(); private: - uint32_t guest_xex_header_; + uint32_t guest_xex_header_ = 0; + ModuleFormat module_format_ = kModuleFormatUndefined; bool dll_module_; uint32_t entry_point_;