mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	Loader: Implemented AppLoader interface for abstracting application loading.
- Various cleanups/refactorings to Loader, ELF, and NCCH modules. - Added AppLoader interface to ELF and NCCH. - Updated Qt/GLFW frontends to check AppLoader ResultStatus. NCCH: Removed extra qualification typos. Loader: Removed unnecessary #include's. NCCH: Improved readability of memcmp statements. NCCH: Added missing space. Elf: Removed unnecessary usage of unique_ptr. Loader: Removed unnecessary usage of unique_ptr.
This commit is contained in:
		
							parent
							
								
									79a48082e2
								
							
						
					
					
						commit
						7889cafc76
					
				
					 8 changed files with 690 additions and 551 deletions
				
			
		|  | @ -16,28 +16,21 @@ | |||
| 
 | ||||
| /// Application entry point
 | ||||
| int __cdecl main(int argc, char **argv) { | ||||
|     std::string program_dir = File::GetCurrentDir(); | ||||
| 
 | ||||
|     LogManager::Init(); | ||||
| 
 | ||||
|     if (argc < 2) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     std::string boot_filename = argv[1]; | ||||
|     EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; | ||||
| 
 | ||||
|     System::Init(emu_window); | ||||
| 
 | ||||
|     std::string boot_filename; | ||||
| 
 | ||||
|     if (argc < 2) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); | ||||
|     } | ||||
|     else { | ||||
|         boot_filename = argv[1]; | ||||
|     } | ||||
|     std::string error_str; | ||||
| 
 | ||||
|     bool res = Loader::LoadFile(boot_filename, &error_str); | ||||
| 
 | ||||
|     if (!res) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM: %s", error_str.c_str()); | ||||
|     if (Loader::ResultStatus::Success != Loader::LoadFile(boot_filename)) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM!"); | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     Core::RunLoop(); | ||||
|  |  | |||
|  | @ -135,11 +135,8 @@ void GMainWindow::BootGame(const char* filename) | |||
| 
 | ||||
|     // Load a game or die...
 | ||||
|     std::string boot_filename = filename; | ||||
|     std::string error_str; | ||||
|     bool res = Loader::LoadFile(boot_filename, &error_str); | ||||
| 
 | ||||
|     if (!res) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM: %s", error_str.c_str()); | ||||
|     if (Loader::ResultStatus::Success != Loader::LoadFile(boot_filename)) { | ||||
|         ERROR_LOG(BOOT, "Failed to load ROM!"); | ||||
|     } | ||||
| 
 | ||||
|     disasmWidget->Init(); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ | |||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <string> | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/file_util.h" | ||||
|  | @ -12,6 +13,220 @@ | |||
| #include "core/loader/elf.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // ELF Header Constants
 | ||||
| 
 | ||||
| // File type
 | ||||
| enum ElfType { | ||||
|     ET_NONE = 0, | ||||
|     ET_REL = 1, | ||||
|     ET_EXEC = 2, | ||||
|     ET_DYN = 3, | ||||
|     ET_CORE = 4, | ||||
|     ET_LOPROC = 0xFF00, | ||||
|     ET_HIPROC = 0xFFFF, | ||||
| }; | ||||
| 
 | ||||
| // Machine/Architecture
 | ||||
| enum ElfMachine { | ||||
|     EM_NONE = 0, | ||||
|     EM_M32 = 1, | ||||
|     EM_SPARC = 2, | ||||
|     EM_386 = 3, | ||||
|     EM_68K = 4, | ||||
|     EM_88K = 5, | ||||
|     EM_860 = 7, | ||||
|     EM_MIPS = 8 | ||||
| }; | ||||
| 
 | ||||
| // File version
 | ||||
| #define EV_NONE    0 | ||||
| #define EV_CURRENT 1 | ||||
| 
 | ||||
| // Identification index
 | ||||
| #define EI_MAG0    0 | ||||
| #define EI_MAG1    1 | ||||
| #define EI_MAG2    2 | ||||
| #define EI_MAG3    3 | ||||
| #define EI_CLASS   4 | ||||
| #define EI_DATA    5 | ||||
| #define EI_VERSION 6 | ||||
| #define EI_PAD     7 | ||||
| #define EI_NIDENT 16 | ||||
| 
 | ||||
| // Magic number
 | ||||
| #define ELFMAG0 0x7F | ||||
| #define ELFMAG1  'E' | ||||
| #define ELFMAG2  'L' | ||||
| #define ELFMAG3  'F' | ||||
| 
 | ||||
| // Sections constants
 | ||||
| 
 | ||||
| // Section types
 | ||||
| #define SHT_NULL            0 | ||||
| #define SHT_PROGBITS        1 | ||||
| #define SHT_SYMTAB          2 | ||||
| #define SHT_STRTAB          3 | ||||
| #define SHT_RELA            4 | ||||
| #define SHT_HASH            5 | ||||
| #define SHT_DYNAMIC         6 | ||||
| #define SHT_NOTE            7 | ||||
| #define SHT_NOBITS          8 | ||||
| #define SHT_REL             9 | ||||
| #define SHT_SHLIB          10 | ||||
| #define SHT_DYNSYM         11 | ||||
| #define SHT_LOPROC 0x70000000 | ||||
| #define SHT_HIPROC 0x7FFFFFFF | ||||
| #define SHT_LOUSER 0x80000000 | ||||
| #define SHT_HIUSER 0xFFFFFFFF | ||||
| 
 | ||||
| // Section flags
 | ||||
| enum ElfSectionFlags | ||||
| { | ||||
|     SHF_WRITE = 0x1, | ||||
|     SHF_ALLOC = 0x2, | ||||
|     SHF_EXECINSTR = 0x4, | ||||
|     SHF_MASKPROC = 0xF0000000, | ||||
| }; | ||||
| 
 | ||||
| // Segment types
 | ||||
| #define PT_NULL             0 | ||||
| #define PT_LOAD             1 | ||||
| #define PT_DYNAMIC          2 | ||||
| #define PT_INTERP           3 | ||||
| #define PT_NOTE             4 | ||||
| #define PT_SHLIB            5 | ||||
| #define PT_PHDR             6 | ||||
| #define PT_LOPROC  0x70000000 | ||||
| #define PT_HIPROC  0x7FFFFFFF | ||||
| 
 | ||||
| typedef unsigned int  Elf32_Addr; | ||||
| typedef unsigned short Elf32_Half; | ||||
| typedef unsigned int  Elf32_Off; | ||||
| typedef signed   int  Elf32_Sword; | ||||
| typedef unsigned int  Elf32_Word; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // ELF file header
 | ||||
| 
 | ||||
| struct Elf32_Ehdr { | ||||
|     unsigned char e_ident[EI_NIDENT]; | ||||
|     Elf32_Half    e_type; | ||||
|     Elf32_Half    e_machine; | ||||
|     Elf32_Word    e_version; | ||||
|     Elf32_Addr    e_entry; | ||||
|     Elf32_Off     e_phoff; | ||||
|     Elf32_Off     e_shoff; | ||||
|     Elf32_Word    e_flags; | ||||
|     Elf32_Half    e_ehsize; | ||||
|     Elf32_Half    e_phentsize; | ||||
|     Elf32_Half    e_phnum; | ||||
|     Elf32_Half    e_shentsize; | ||||
|     Elf32_Half    e_shnum; | ||||
|     Elf32_Half    e_shstrndx; | ||||
| }; | ||||
| 
 | ||||
| // Section header
 | ||||
| struct Elf32_Shdr { | ||||
|     Elf32_Word sh_name; | ||||
|     Elf32_Word sh_type; | ||||
|     Elf32_Word sh_flags; | ||||
|     Elf32_Addr sh_addr; | ||||
|     Elf32_Off  sh_offset; | ||||
|     Elf32_Word sh_size; | ||||
|     Elf32_Word sh_link; | ||||
|     Elf32_Word sh_info; | ||||
|     Elf32_Word sh_addralign; | ||||
|     Elf32_Word sh_entsize; | ||||
| }; | ||||
| 
 | ||||
| // Segment header
 | ||||
| struct Elf32_Phdr { | ||||
|     Elf32_Word p_type; | ||||
|     Elf32_Off  p_offset; | ||||
|     Elf32_Addr p_vaddr; | ||||
|     Elf32_Addr p_paddr; | ||||
|     Elf32_Word p_filesz; | ||||
|     Elf32_Word p_memsz; | ||||
|     Elf32_Word p_flags; | ||||
|     Elf32_Word p_align; | ||||
| }; | ||||
| 
 | ||||
| // Symbol table entry
 | ||||
| struct Elf32_Sym { | ||||
|     Elf32_Word    st_name; | ||||
|     Elf32_Addr    st_value; | ||||
|     Elf32_Word    st_size; | ||||
|     unsigned char st_info; | ||||
|     unsigned char st_other; | ||||
|     Elf32_Half    st_shndx; | ||||
| }; | ||||
| 
 | ||||
| // Relocation entries
 | ||||
| struct Elf32_Rel { | ||||
|     Elf32_Addr r_offset; | ||||
|     Elf32_Word r_info; | ||||
| }; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // ElfReader class
 | ||||
| 
 | ||||
| typedef int SectionID; | ||||
| 
 | ||||
| class ElfReader { | ||||
| private: | ||||
|     char *base; | ||||
|     u32 *base32; | ||||
| 
 | ||||
|     Elf32_Ehdr *header; | ||||
|     Elf32_Phdr *segments; | ||||
|     Elf32_Shdr *sections; | ||||
| 
 | ||||
|     u32 *sectionAddrs; | ||||
|     bool relocate; | ||||
|     u32 entryPoint; | ||||
| 
 | ||||
| public: | ||||
|     ElfReader(void *ptr); | ||||
|     ~ElfReader() { } | ||||
| 
 | ||||
|     u32 Read32(int off) const { return base32[off >> 2]; } | ||||
| 
 | ||||
|     // Quick accessors
 | ||||
|     ElfType GetType() const { return (ElfType)(header->e_type); } | ||||
|     ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } | ||||
|     u32 GetEntryPoint() const { return entryPoint; } | ||||
|     u32 GetFlags() const { return (u32)(header->e_flags); } | ||||
|     bool LoadInto(u32 vaddr); | ||||
|     bool LoadSymbols(); | ||||
| 
 | ||||
|     int GetNumSegments() const { return (int)(header->e_phnum); } | ||||
|     int GetNumSections() const { return (int)(header->e_shnum); } | ||||
|     const u8 *GetPtr(int offset) const { return (u8*)base + offset; } | ||||
|     const char *GetSectionName(int section) const; | ||||
|     const u8 *GetSectionDataPtr(int section) const { | ||||
|         if (section < 0 || section >= header->e_shnum) | ||||
|             return nullptr; | ||||
|         if (sections[section].sh_type != SHT_NOBITS) | ||||
|             return GetPtr(sections[section].sh_offset); | ||||
|         else | ||||
|             return nullptr; | ||||
|     } | ||||
|     bool IsCodeSection(int section) const { | ||||
|         return sections[section].sh_type == SHT_PROGBITS; | ||||
|     } | ||||
|     const u8 *GetSegmentPtr(int segment) { | ||||
|         return GetPtr(segments[segment].p_offset); | ||||
|     } | ||||
|     u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; } | ||||
|     int GetSectionSize(SectionID section) const { return sections[section].sh_size; } | ||||
|     SectionID GetSectionByName(const char *name, int firstSection = 0) const; //-1 for not found
 | ||||
| 
 | ||||
|     bool DidRelocate() { | ||||
|         return relocate; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| ElfReader::ElfReader(void *ptr) { | ||||
|     base = (char*)ptr; | ||||
|     base32 = (u32 *)ptr; | ||||
|  | @ -29,28 +244,25 @@ const char *ElfReader::GetSectionName(int section) const { | |||
|     if (sections[section].sh_type == SHT_NULL) | ||||
|         return nullptr; | ||||
| 
 | ||||
|     int nameOffset = sections[section].sh_name; | ||||
|     int name_offset = sections[section].sh_name; | ||||
|     char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); | ||||
| 
 | ||||
|     if (ptr) | ||||
|         return ptr + nameOffset; | ||||
|     else | ||||
|         return nullptr; | ||||
|         return ptr + name_offset; | ||||
| 
 | ||||
|     return nullptr; | ||||
| } | ||||
| 
 | ||||
| bool ElfReader::LoadInto(u32 vaddr) { | ||||
|     DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); | ||||
| 
 | ||||
|     // Should we relocate?
 | ||||
|     bRelocate = (header->e_type != ET_EXEC); | ||||
|     relocate = (header->e_type != ET_EXEC); | ||||
| 
 | ||||
|     if (bRelocate) | ||||
|     { | ||||
|     if (relocate) { | ||||
|         DEBUG_LOG(MASTER_LOG, "Relocatable module"); | ||||
|         entryPoint += vaddr; | ||||
|     } | ||||
|     else | ||||
|     { | ||||
|     } else { | ||||
|         DEBUG_LOG(MASTER_LOG, "Prerelocated executable"); | ||||
|     } | ||||
| 
 | ||||
|  | @ -58,17 +270,14 @@ bool ElfReader::LoadInto(u32 vaddr) { | |||
| 
 | ||||
|     // First pass : Get the bits into RAM
 | ||||
|     u32 segmentVAddr[32]; | ||||
|     u32 baseAddress = relocate ? vaddr : 0; | ||||
| 
 | ||||
|     u32 baseAddress = bRelocate ? vaddr : 0; | ||||
| 
 | ||||
|     for (int i = 0; i < header->e_phnum; i++) | ||||
|     { | ||||
|     for (int i = 0; i < header->e_phnum; i++) { | ||||
|         Elf32_Phdr *p = segments + i; | ||||
| 
 | ||||
|         INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); | ||||
| 
 | ||||
|         if (p->p_type == PT_LOAD) | ||||
|         { | ||||
|         if (p->p_type == PT_LOAD) { | ||||
|             segmentVAddr[i] = baseAddress + p->p_vaddr; | ||||
|             u32 writeAddr = segmentVAddr[i]; | ||||
| 
 | ||||
|  | @ -78,27 +287,19 @@ bool ElfReader::LoadInto(u32 vaddr) { | |||
|             u32 dstSize = p->p_memsz; | ||||
|             u32 *s = (u32*)src; | ||||
|             u32 *d = (u32*)dst; | ||||
|             for (int j = 0; j < (int)(srcSize + 3) / 4; j++) | ||||
|             { | ||||
|                 *d++ = /*_byteswap_ulong*/(*s++); | ||||
|             } | ||||
|             if (srcSize < dstSize) | ||||
|             { | ||||
|                 //memset(dst + srcSize, 0, dstSize-srcSize); //zero out bss
 | ||||
|             for (int j = 0; j < (int)(srcSize + 3) / 4; j++) { | ||||
|                 *d++ = (*s++); | ||||
|             } | ||||
|             INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     INFO_LOG(MASTER_LOG, "Done loading."); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const | ||||
| { | ||||
|     for (int i = firstSection; i < header->e_shnum; i++) | ||||
|     { | ||||
| SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { | ||||
|     for (int i = firstSection; i < header->e_shnum; i++) { | ||||
|         const char *secname = GetSectionName(i); | ||||
| 
 | ||||
|         if (secname != nullptr && strcmp(name, secname) == 0) | ||||
|  | @ -107,25 +308,21 @@ SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const | |||
|     return -1; | ||||
| } | ||||
| 
 | ||||
| bool ElfReader::LoadSymbols() | ||||
| { | ||||
| bool ElfReader::LoadSymbols() { | ||||
|     bool hasSymbols = false; | ||||
|     SectionID sec = GetSectionByName(".symtab"); | ||||
|     if (sec != -1) | ||||
|     { | ||||
|     if (sec != -1) { | ||||
|         int stringSection = sections[sec].sh_link; | ||||
|         const char *stringBase = (const char *)GetSectionDataPtr(stringSection); | ||||
| 
 | ||||
|         //We have a symbol table!
 | ||||
|         Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); | ||||
|         int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); | ||||
|         for (int sym = 0; sym < numSymbols; sym++) | ||||
|         { | ||||
|         for (int sym = 0; sym < numSymbols; sym++) { | ||||
|             int size = symtab[sym].st_size; | ||||
|             if (size == 0) | ||||
|                 continue; | ||||
| 
 | ||||
|             // int bind = symtab[sym].st_info >> 4;
 | ||||
|             int type = symtab[sym].st_info & 0xF; | ||||
| 
 | ||||
|             const char *name = stringBase + symtab[sym].st_name; | ||||
|  | @ -144,42 +341,41 @@ bool ElfReader::LoadSymbols() | |||
| 
 | ||||
| namespace Loader { | ||||
| 
 | ||||
| /// AppLoader_ELF constructor
 | ||||
| AppLoader_ELF::AppLoader_ELF(std::string& filename) : is_loaded(false) { | ||||
|     this->filename = filename; | ||||
| } | ||||
| 
 | ||||
| /// AppLoader_NCCH destructor
 | ||||
| AppLoader_ELF::~AppLoader_ELF() { | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads an ELF file | ||||
|  * @param filename String filename of ELF file | ||||
|  * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) | ||||
|  * @param error_string Pointer to string to put error message if an error has occurred | ||||
|  * @todo Move NCSD parsing out of here and create a separate function for loading these | ||||
|  * @return True on success, otherwise false | ||||
|  */ | ||||
| bool Load_ELF(std::string& filename, std::string* error_string) { | ||||
|     std::string full_path = filename; | ||||
|     std::string path, file, extension; | ||||
|     SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); | ||||
| #if EMU_PLATFORM == PLATFORM_WINDOWS | ||||
|     path = ReplaceAll(path, "/", "\\"); | ||||
| #endif | ||||
|     File::IOFile f(filename, "rb"); | ||||
| const ResultStatus AppLoader_ELF::Load() { | ||||
|     INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str()); | ||||
| 
 | ||||
|     if (f.IsOpen()) { | ||||
|         u32 size = (u32)f.GetSize(); | ||||
|         u8* buffer = new u8[size]; | ||||
|         ElfReader* elf_reader = NULL; | ||||
|     if (is_loaded) | ||||
|         return ResultStatus::ErrorAlreadyLoaded; | ||||
| 
 | ||||
|         f.ReadBytes(buffer, size); | ||||
|     File::IOFile file(filename, "rb"); | ||||
| 
 | ||||
|         elf_reader = new ElfReader(buffer); | ||||
|         elf_reader->LoadInto(0x00100000); | ||||
|     if (file.IsOpen()) { | ||||
|         u32 size = (u32)file.GetSize(); | ||||
|         std::unique_ptr<u8[]> buffer(new u8[size]); | ||||
|         file.ReadBytes(&buffer[0], size); | ||||
| 
 | ||||
|         Kernel::LoadExec(elf_reader->GetEntryPoint()); | ||||
| 
 | ||||
|         delete[] buffer; | ||||
|         delete elf_reader; | ||||
|         ElfReader elf_reader(&buffer[0]); | ||||
|         elf_reader.LoadInto(0x00100000); | ||||
|         Kernel::LoadExec(elf_reader.GetEntryPoint()); | ||||
|     } else { | ||||
|         *error_string = "Unable to open ELF file!"; | ||||
|         return false; | ||||
|         return ResultStatus::Error; | ||||
|     } | ||||
|     f.Close(); | ||||
| 
 | ||||
|     return true; | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  |  | |||
|  | @ -5,226 +5,28 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common/common_types.h" | ||||
| 
 | ||||
| // ELF Header Constants
 | ||||
| 
 | ||||
| // File type
 | ||||
| enum ElfType { | ||||
|     ET_NONE = 0, | ||||
|     ET_REL = 1, | ||||
|     ET_EXEC = 2, | ||||
|     ET_DYN = 3, | ||||
|     ET_CORE = 4, | ||||
|     ET_LOPROC = 0xFF00, | ||||
|     ET_HIPROC = 0xFFFF, | ||||
| }; | ||||
| 
 | ||||
| // Machine/Architecture
 | ||||
| enum ElfMachine { | ||||
|     EM_NONE = 0, | ||||
|     EM_M32 = 1, | ||||
|     EM_SPARC = 2, | ||||
|     EM_386 = 3, | ||||
|     EM_68K = 4, | ||||
|     EM_88K = 5, | ||||
|     EM_860 = 7, | ||||
|     EM_MIPS = 8 | ||||
| }; | ||||
| 
 | ||||
| // File version
 | ||||
| #define EV_NONE    0 | ||||
| #define EV_CURRENT 1 | ||||
| 
 | ||||
| // Identification index
 | ||||
| #define EI_MAG0    0 | ||||
| #define EI_MAG1    1 | ||||
| #define EI_MAG2    2 | ||||
| #define EI_MAG3    3 | ||||
| #define EI_CLASS   4 | ||||
| #define EI_DATA    5 | ||||
| #define EI_VERSION 6 | ||||
| #define EI_PAD     7 | ||||
| #define EI_NIDENT 16 | ||||
| 
 | ||||
| // Magic number
 | ||||
| #define ELFMAG0 0x7F | ||||
| #define ELFMAG1  'E' | ||||
| #define ELFMAG2  'L' | ||||
| #define ELFMAG3  'F' | ||||
| 
 | ||||
| // Sections constants
 | ||||
| 
 | ||||
| // Section types
 | ||||
| #define SHT_NULL            0 | ||||
| #define SHT_PROGBITS        1 | ||||
| #define SHT_SYMTAB          2 | ||||
| #define SHT_STRTAB          3 | ||||
| #define SHT_RELA            4 | ||||
| #define SHT_HASH            5 | ||||
| #define SHT_DYNAMIC         6 | ||||
| #define SHT_NOTE            7 | ||||
| #define SHT_NOBITS          8 | ||||
| #define SHT_REL             9 | ||||
| #define SHT_SHLIB          10 | ||||
| #define SHT_DYNSYM         11 | ||||
| #define SHT_LOPROC 0x70000000 | ||||
| #define SHT_HIPROC 0x7FFFFFFF | ||||
| #define SHT_LOUSER 0x80000000 | ||||
| #define SHT_HIUSER 0xFFFFFFFF | ||||
| 
 | ||||
| // Section flags
 | ||||
| enum ElfSectionFlags | ||||
| { | ||||
|     SHF_WRITE = 0x1, | ||||
|     SHF_ALLOC = 0x2, | ||||
|     SHF_EXECINSTR = 0x4, | ||||
|     SHF_MASKPROC = 0xF0000000, | ||||
| }; | ||||
| 
 | ||||
| // Segment types
 | ||||
| #define PT_NULL             0 | ||||
| #define PT_LOAD             1 | ||||
| #define PT_DYNAMIC          2 | ||||
| #define PT_INTERP           3 | ||||
| #define PT_NOTE             4 | ||||
| #define PT_SHLIB            5 | ||||
| #define PT_PHDR             6 | ||||
| #define PT_LOPROC  0x70000000 | ||||
| #define PT_HIPROC  0x7FFFFFFF | ||||
| 
 | ||||
| typedef unsigned int  Elf32_Addr; | ||||
| typedef unsigned short Elf32_Half; | ||||
| typedef unsigned int  Elf32_Off; | ||||
| typedef signed   int  Elf32_Sword; | ||||
| typedef unsigned int  Elf32_Word; | ||||
| 
 | ||||
| // ELF file header
 | ||||
| struct Elf32_Ehdr { | ||||
|     unsigned char e_ident[EI_NIDENT]; | ||||
|     Elf32_Half    e_type; | ||||
|     Elf32_Half    e_machine; | ||||
|     Elf32_Word    e_version; | ||||
|     Elf32_Addr    e_entry; | ||||
|     Elf32_Off     e_phoff; | ||||
|     Elf32_Off     e_shoff; | ||||
|     Elf32_Word    e_flags; | ||||
|     Elf32_Half    e_ehsize; | ||||
|     Elf32_Half    e_phentsize; | ||||
|     Elf32_Half    e_phnum; | ||||
|     Elf32_Half    e_shentsize; | ||||
|     Elf32_Half    e_shnum; | ||||
|     Elf32_Half    e_shstrndx; | ||||
| }; | ||||
| 
 | ||||
| // Section header
 | ||||
| struct Elf32_Shdr { | ||||
|     Elf32_Word sh_name; | ||||
|     Elf32_Word sh_type; | ||||
|     Elf32_Word sh_flags; | ||||
|     Elf32_Addr sh_addr; | ||||
|     Elf32_Off  sh_offset; | ||||
|     Elf32_Word sh_size; | ||||
|     Elf32_Word sh_link; | ||||
|     Elf32_Word sh_info; | ||||
|     Elf32_Word sh_addralign; | ||||
|     Elf32_Word sh_entsize; | ||||
| }; | ||||
| 
 | ||||
| // Segment header
 | ||||
| struct Elf32_Phdr { | ||||
|     Elf32_Word p_type; | ||||
|     Elf32_Off  p_offset; | ||||
|     Elf32_Addr p_vaddr; | ||||
|     Elf32_Addr p_paddr; | ||||
|     Elf32_Word p_filesz; | ||||
|     Elf32_Word p_memsz; | ||||
|     Elf32_Word p_flags; | ||||
|     Elf32_Word p_align; | ||||
| }; | ||||
| 
 | ||||
| // Symbol table entry
 | ||||
| struct Elf32_Sym { | ||||
|     Elf32_Word    st_name; | ||||
|     Elf32_Addr    st_value; | ||||
|     Elf32_Word    st_size; | ||||
|     unsigned char st_info; | ||||
|     unsigned char st_other; | ||||
|     Elf32_Half    st_shndx; | ||||
| }; | ||||
| 
 | ||||
| // Relocation entries
 | ||||
| struct Elf32_Rel { | ||||
|     Elf32_Addr r_offset; | ||||
|     Elf32_Word r_info; | ||||
| }; | ||||
| 
 | ||||
| typedef int SectionID; | ||||
| 
 | ||||
| class ElfReader { | ||||
| private: | ||||
|     char *base; | ||||
|     u32 *base32; | ||||
| 
 | ||||
|     Elf32_Ehdr *header; | ||||
|     Elf32_Phdr *segments; | ||||
|     Elf32_Shdr *sections; | ||||
| 
 | ||||
|     u32 *sectionAddrs; | ||||
|     bool bRelocate; | ||||
|     u32 entryPoint; | ||||
| 
 | ||||
| public: | ||||
|     ElfReader(void *ptr); | ||||
|     ~ElfReader() { } | ||||
| 
 | ||||
|     u32 Read32(int off) const { return base32[off >> 2]; } | ||||
| 
 | ||||
|     // Quick accessors
 | ||||
|     ElfType GetType() const { return (ElfType)(header->e_type); } | ||||
|     ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } | ||||
|     u32 GetEntryPoint() const { return entryPoint; } | ||||
|     u32 GetFlags() const { return (u32)(header->e_flags); } | ||||
|     bool LoadInto(u32 vaddr); | ||||
|     bool LoadSymbols(); | ||||
| 
 | ||||
|     int GetNumSegments() const { return (int)(header->e_phnum); } | ||||
|     int GetNumSections() const { return (int)(header->e_shnum); } | ||||
|     const u8 *GetPtr(int offset) const { return (u8*)base + offset; } | ||||
|     const char *GetSectionName(int section) const; | ||||
|     const u8 *GetSectionDataPtr(int section) const { | ||||
|         if (section < 0 || section >= header->e_shnum) | ||||
|             return nullptr; | ||||
|         if (sections[section].sh_type != SHT_NOBITS) | ||||
|             return GetPtr(sections[section].sh_offset); | ||||
|         else | ||||
|             return nullptr; | ||||
|     } | ||||
|     bool IsCodeSection(int section) const { | ||||
|         return sections[section].sh_type == SHT_PROGBITS; | ||||
|     } | ||||
|     const u8 *GetSegmentPtr(int segment) { | ||||
|         return GetPtr(segments[segment].p_offset); | ||||
|     } | ||||
|     u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; } | ||||
|     int GetSectionSize(SectionID section) const { return sections[section].sh_size; } | ||||
|     SectionID GetSectionByName(const char *name, int firstSection = 0) const; //-1 for not found
 | ||||
| 
 | ||||
|     bool DidRelocate() { | ||||
|         return bRelocate; | ||||
|     } | ||||
| }; | ||||
| #include "core/loader/loader.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Loader namespace
 | ||||
| 
 | ||||
| namespace Loader { | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads an ELF file | ||||
|  * @param filename String filename of ELF file | ||||
|  * @param error_string Pointer to string to put error message if an error has occurred | ||||
|  * @return True on success, otherwise false | ||||
|  */ | ||||
| bool Load_ELF(std::string& filename, std::string* error_string); | ||||
| /// Loads an ELF/AXF file
 | ||||
| class AppLoader_ELF : public AppLoader { | ||||
| public: | ||||
|     AppLoader_ELF(std::string& filename); | ||||
|     ~AppLoader_ELF(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Load the bootable file | ||||
|      * @return ResultStatus result of function | ||||
|      */ | ||||
|     const ResultStatus Load(); | ||||
| 
 | ||||
| private: | ||||
|     std::string filename; | ||||
|     bool        is_loaded; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  |  | |||
|  | @ -2,6 +2,8 @@ | |||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "core/loader/loader.h" | ||||
| #include "core/loader/elf.h" | ||||
| #include "core/loader/ncch.h" | ||||
|  | @ -16,59 +18,60 @@ namespace Loader { | |||
|  * @todo (ShizZy) this function sucks... make it actually check file contents etc. | ||||
|  * @return FileType of file | ||||
|  */ | ||||
| FileType IdentifyFile(std::string &filename) { | ||||
| const FileType IdentifyFile(const std::string &filename) { | ||||
|     if (filename.size() == 0) { | ||||
|         ERROR_LOG(LOADER, "invalid filename %s", filename.c_str()); | ||||
|         return FILETYPE_ERROR; | ||||
|         return FileType::Error; | ||||
|     } | ||||
|     std::string extension = filename.size() >= 5 ? filename.substr(filename.size() - 4) : ""; | ||||
| 
 | ||||
|     if (!strcasecmp(extension.c_str(), ".elf")) { | ||||
|         return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p
 | ||||
|         return FileType::ELF; // TODO(bunnei): Do some filetype checking :p
 | ||||
|     } | ||||
|     else if (!strcasecmp(extension.c_str(), ".axf")) { | ||||
|         return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p
 | ||||
|         return FileType::ELF; // TODO(bunnei): Do some filetype checking :p
 | ||||
|     } | ||||
|     else if (!strcasecmp(extension.c_str(), ".cxi")) { | ||||
|         return FILETYPE_CTR_CXI; // TODO(bunnei): Do some filetype checking :p
 | ||||
|         return FileType::CXI; // TODO(bunnei): Do some filetype checking :p
 | ||||
|     } | ||||
|     else if (!strcasecmp(extension.c_str(), ".cci")) { | ||||
|         return FILETYPE_CTR_CCI; // TODO(bunnei): Do some filetype checking :p
 | ||||
|         return FileType::CCI; // TODO(bunnei): Do some filetype checking :p
 | ||||
|     } | ||||
|     return FILETYPE_UNKNOWN; | ||||
|     return FileType::Unknown; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Identifies and loads a bootable file | ||||
|  * @param filename String filename of bootable file | ||||
|  * @param error_string Point to string to put error message if an error has occurred | ||||
|  * @return True on success, otherwise false | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| bool LoadFile(std::string &filename, std::string *error_string) { | ||||
|     INFO_LOG(LOADER, "Identifying file..."); | ||||
| const ResultStatus LoadFile(std::string& filename) { | ||||
|     INFO_LOG(LOADER, "Loading file %s...", filename.c_str()); | ||||
| 
 | ||||
|     // Note that this can modify filename!
 | ||||
|     switch (IdentifyFile(filename)) { | ||||
| 
 | ||||
|     case FILETYPE_CTR_ELF: | ||||
|         return Loader::Load_ELF(filename, error_string); | ||||
| 
 | ||||
|     case FILETYPE_CTR_CXI: | ||||
|     case FILETYPE_CTR_CCI: | ||||
|         return Loader::Load_NCCH(filename, error_string); | ||||
| 
 | ||||
|     case FILETYPE_ERROR: | ||||
|         ERROR_LOG(LOADER, "Could not read file"); | ||||
|         *error_string = "Error reading file"; | ||||
|         break; | ||||
| 
 | ||||
|     case FILETYPE_UNKNOWN: | ||||
|     default: | ||||
|         ERROR_LOG(LOADER, "Failed to identify file"); | ||||
|         *error_string = " Failed to identify file"; | ||||
|         break; | ||||
|     // Standard ELF file format...
 | ||||
|     case FileType::ELF: { | ||||
|         return AppLoader_ELF(filename).Load(); | ||||
|     } | ||||
|     return false; | ||||
| 
 | ||||
|     // NCCH/NCSD container formats...
 | ||||
|     case FileType::CXI: | ||||
|     case FileType::CCI: { | ||||
|         return AppLoader_NCCH(filename).Load(); | ||||
|     } | ||||
| 
 | ||||
|     // Error occurred durring IdentifyFile...
 | ||||
|     case FileType::Error: | ||||
| 
 | ||||
|     // IdentifyFile could know identify file type...
 | ||||
|     case FileType::Unknown: | ||||
| 
 | ||||
|     default: | ||||
|         return ResultStatus::ErrorInvalidFormat; | ||||
|     } | ||||
| 
 | ||||
|     return ResultStatus::Error; | ||||
| } | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
|  | @ -11,16 +13,94 @@ | |||
| 
 | ||||
| namespace Loader { | ||||
| 
 | ||||
| enum FileType { | ||||
|     FILETYPE_ERROR, | ||||
| /// File types supported by CTR
 | ||||
| enum class FileType { | ||||
|     Error, | ||||
|     Unknown, | ||||
|     CCI, | ||||
|     CXI, | ||||
|     CIA, | ||||
|     ELF, | ||||
| }; | ||||
| 
 | ||||
|     FILETYPE_CTR_CCI, | ||||
|     FILETYPE_CTR_CIA, | ||||
|     FILETYPE_CTR_CXI, | ||||
|     FILETYPE_CTR_ELF, | ||||
|     FILETYPE_CTR_BIN, | ||||
| /// Return type for functions in Loader namespace
 | ||||
| enum class ResultStatus { | ||||
|     Success, | ||||
|     Error, | ||||
|     ErrorInvalidFormat, | ||||
|     ErrorNotImplemented, | ||||
|     ErrorNotLoaded, | ||||
|     ErrorAlreadyLoaded, | ||||
| }; | ||||
| 
 | ||||
|     FILETYPE_UNKNOWN | ||||
| /// Interface for loading an application
 | ||||
| class AppLoader : NonCopyable { | ||||
| public: | ||||
|     AppLoader() { } | ||||
|     virtual ~AppLoader() { } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Load the application | ||||
|      * @return ResultStatus result of function | ||||
|      */ | ||||
|     virtual const ResultStatus Load() = 0; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Get the code (typically .code section) of the application | ||||
|      * @param error ResultStatus result of function | ||||
|      * @return Reference to code buffer | ||||
|      */ | ||||
|     virtual const std::vector<u8>& GetCode(ResultStatus& error) const { | ||||
|         error = ResultStatus::ErrorNotImplemented; | ||||
|         return code; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Get the icon (typically .icon section) of the application | ||||
|      * @param error ResultStatus result of function | ||||
|      * @return Reference to icon buffer | ||||
|      */ | ||||
|     virtual const std::vector<u8>& GetIcon(ResultStatus& error) const { | ||||
|         error = ResultStatus::ErrorNotImplemented; | ||||
|         return icon; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Get the banner (typically .banner section) of the application | ||||
|      * @param error ResultStatus result of function | ||||
|      * @return Reference to banner buffer | ||||
|      */ | ||||
|     virtual const std::vector<u8>& GetBanner(ResultStatus& error) const { | ||||
|         error = ResultStatus::ErrorNotImplemented; | ||||
|         return banner; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Get the logo (typically .logo section) of the application | ||||
|      * @param error ResultStatus result of function | ||||
|      * @return Reference to logo buffer | ||||
|      */ | ||||
|     virtual const std::vector<u8>& GetLogo(ResultStatus& error) const { | ||||
|         error = ResultStatus::ErrorNotImplemented; | ||||
|         return logo; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Get the RomFs archive of the application | ||||
|      * @param error ResultStatus result of function | ||||
|      * @return Reference to RomFs archive buffer | ||||
|      */ | ||||
|     virtual const std::vector<u8>& GetRomFs(ResultStatus error) const { | ||||
|         error = ResultStatus::ErrorNotImplemented; | ||||
|         return romfs; | ||||
|     } | ||||
| 
 | ||||
| protected: | ||||
|     std::vector<u8> code;   ///< ExeFS .code section
 | ||||
|     std::vector<u8> icon;   ///< ExeFS .icon section
 | ||||
|     std::vector<u8> banner; ///< ExeFS .banner section
 | ||||
|     std::vector<u8> logo;   ///< ExeFS .logo section
 | ||||
|     std::vector<u8> romfs;  ///< RomFs archive
 | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -28,14 +108,13 @@ enum FileType { | |||
|  * @param filename String filename of bootable file | ||||
|  * @return FileType of file | ||||
|  */ | ||||
| FileType IdentifyFile(std::string &filename); | ||||
| const FileType IdentifyFile(const std::string &filename); | ||||
| 
 | ||||
| /**
 | ||||
|  * Identifies and loads a bootable file | ||||
|  * @param filename String filename of bootable file | ||||
|  * @param error_string Point to string to put error message if an error has occurred | ||||
|  * @return True on success, otherwise false | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| bool LoadFile(std::string &filename, std::string *error_string); | ||||
| const ResultStatus LoadFile(std::string& filename); | ||||
| 
 | ||||
| } // namespace
 | ||||
|  |  | |||
|  | @ -2,142 +2,14 @@ | |||
| // Licensed under GPLv2
 | ||||
| // Refer to the license.txt file included.
 | ||||
| 
 | ||||
| #include <memory> | ||||
| 
 | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/loader/ncch.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/mem_map.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| /// NCCH header (Note: "NCCH" appears to be a publically unknown acronym)
 | ||||
| 
 | ||||
| struct NCCH_Header { | ||||
|     u8 signature[0x100]; | ||||
|     char magic[4]; | ||||
|     u32 content_size; | ||||
|     u8 partition_id[8]; | ||||
|     u16 maker_code; | ||||
|     u16 version; | ||||
|     u8 reserved_0[4]; | ||||
|     u8 program_id[8]; | ||||
|     u8 temp_flag; | ||||
|     u8 reserved_1[0x2f]; | ||||
|     u8 product_code[0x10]; | ||||
|     u8 extended_header_hash[0x20]; | ||||
|     u32 extended_header_size; | ||||
|     u8 reserved_2[4]; | ||||
|     u8 flags[8]; | ||||
|     u32 plain_region_offset; | ||||
|     u32 plain_region_size; | ||||
|     u8 reserved_3[8]; | ||||
|     u32 exefs_offset; | ||||
|     u32 exefs_size; | ||||
|     u32 exefs_hash_region_size; | ||||
|     u8 reserved_4[4]; | ||||
|     u32 romfs_offset; | ||||
|     u32 romfs_size; | ||||
|     u32 romfs_hash_region_size; | ||||
|     u8 reserved_5[4]; | ||||
|     u8 exefs_super_block_hash[0x20]; | ||||
|     u8 romfs_super_block_hash[0x20]; | ||||
| }; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // ExeFS (executable file system) headers
 | ||||
| 
 | ||||
| typedef struct { | ||||
|     char name[8]; | ||||
|     u32 offset; | ||||
|     u32 size; | ||||
| } ExeFs_SectionHeader; | ||||
| 
 | ||||
| typedef struct { | ||||
|     ExeFs_SectionHeader section[8]; | ||||
|     u8 reserved[0x80]; | ||||
|     u8 hashes[8][0x20]; | ||||
| } ExeFs_Header; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // ExHeader (executable file system header) headers
 | ||||
| 
 | ||||
| struct ExHeader_SystemInfoFlags{ | ||||
|     u8 reserved[5]; | ||||
|     u8 flag; | ||||
|     u8 remaster_version[2]; | ||||
| } exheader_systeminfoflags; | ||||
| 
 | ||||
| struct ExHeader_CodeSegmentInfo{ | ||||
|     u32 address; | ||||
|     u32 num_max_pages; | ||||
|     u32 code_size; | ||||
| } exheader_codesegmentinfo; | ||||
| 
 | ||||
| struct ExHeader_CodeSetInfo { | ||||
|     u8 name[8]; | ||||
|     ExHeader_SystemInfoFlags flags; | ||||
|     ExHeader_CodeSegmentInfo text; | ||||
|     u8 stacksize[4]; | ||||
|     ExHeader_CodeSegmentInfo ro; | ||||
|     u8 reserved[4]; | ||||
|     ExHeader_CodeSegmentInfo data; | ||||
|     u8 bsssize[4]; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_DependencyList{ | ||||
|     u8 program_id[0x30][8]; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_SystemInfo{ | ||||
|     u32 save_data_size; | ||||
|     u8 reserved[4]; | ||||
|     u8 jump_id[8]; | ||||
|     u8 reserved_2[0x30]; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_StorageInfo{ | ||||
|     u8 ext_save_data_id[8]; | ||||
|     u8 system_save_data_id[8]; | ||||
|     u8 reserved[8]; | ||||
|     u8 access_info[7]; | ||||
|     u8 other_attributes; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_ARM11_SystemLocalCaps{ | ||||
|     u8 program_id[8]; | ||||
|     u8 flags[8]; | ||||
|     u8 resource_limit_descriptor[0x10][2]; | ||||
|     ExHeader_StorageInfo storage_info; | ||||
|     u8 service_access_control[0x20][8]; | ||||
|     u8 reserved[0x1f]; | ||||
|     u8 resource_limit_category; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_ARM11_KernelCaps{ | ||||
|     u8 descriptors[28][4]; | ||||
|     u8 reserved[0x10]; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_ARM9_AccessControl{ | ||||
|     u8 descriptors[15]; | ||||
|     u8 descversion; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_Header{ | ||||
|     ExHeader_CodeSetInfo codeset_info; | ||||
|     ExHeader_DependencyList dependency_list; | ||||
|     ExHeader_SystemInfo system_info; | ||||
|     ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; | ||||
|     ExHeader_ARM11_KernelCaps arm11_kernel_caps; | ||||
|     ExHeader_ARM9_AccessControl arm9_access_control; | ||||
|     struct { | ||||
|         u8 signature[0x100]; | ||||
|         u8 ncch_public_key_modulus[0x100]; | ||||
|         ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; | ||||
|         ExHeader_ARM11_KernelCaps arm11_kernel_caps; | ||||
|         ExHeader_ARM9_AccessControl arm9_access_control; | ||||
|     } access_desc; | ||||
| }; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Loader namespace
 | ||||
| 
 | ||||
|  | @ -163,11 +35,9 @@ u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { | |||
|  * @param compressed_size Size of compressed buffer | ||||
|  * @param decompressed Decompressed buffer | ||||
|  * @param decompressed_size Size of decompressed buffer | ||||
|  * @param error_string String populated with error message on failure | ||||
|  * @return True on success, otherwise false | ||||
|  */ | ||||
| bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size,  | ||||
|     std::string* error_string) { | ||||
| bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { | ||||
|     u8* footer = compressed + compressed_size - 8; | ||||
|     u32 buffer_top_and_bottom = *(u32*)footer; | ||||
|     u32 i, j; | ||||
|  | @ -191,8 +61,8 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 | |||
|                 break; | ||||
| 
 | ||||
|             if(control & 0x80) { | ||||
|                 // Check if compression is out of bounds
 | ||||
|                 if(index < 2) { | ||||
|                     *error_string = "Compression out of bounds"; | ||||
|                     return false; | ||||
|                 } | ||||
|                 index -= 2; | ||||
|  | @ -202,22 +72,22 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 | |||
|                 segment_offset &= 0x0FFF; | ||||
|                 segment_offset += 2; | ||||
| 
 | ||||
|                 // Check if compression is out of bounds
 | ||||
|                 if(out < segment_size) { | ||||
|                     *error_string = "Compression out of bounds"; | ||||
|                     return false; | ||||
|                 } | ||||
|                 for(j = 0; j < segment_size; j++) { | ||||
|                     u8 data; | ||||
|                     // Check if compression is out of bounds
 | ||||
|                     if(out + segment_offset >= decompressed_size) { | ||||
|                         *error_string = "Compression out of bounds"; | ||||
|                         return false; | ||||
|                     } | ||||
|                     data  = decompressed[out + segment_offset]; | ||||
|                     decompressed[--out] = data; | ||||
|                 } | ||||
|             } else { | ||||
|                 // Check if compression is out of bounds
 | ||||
|                 if(out < 1) { | ||||
|                     *error_string = "Compression out of bounds"; | ||||
|                     return false; | ||||
|                 } | ||||
|                 decompressed[--out] = compressed[--index]; | ||||
|  | @ -228,34 +98,96 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 | |||
|     return true; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Load a data buffer into memory at the specified address | ||||
|  * @param addr Address to load memory into | ||||
|  * @param buffer Buffer of data to load into memory | ||||
|  * @param size Size of data to load into memory | ||||
|  * @todo Perhaps move this code somewhere more generic? | ||||
|  */ | ||||
| void LoadBuffer(const u32 addr, const u8* const buffer, const int size) { | ||||
|     u32 *dst = (u32*)Memory::GetPointer(addr); | ||||
|     u32 *src = (u32*)buffer; | ||||
|     int size_aligned = (size + 3) / 4; | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // AppLoader_NCCH class
 | ||||
| 
 | ||||
|     for (int j = 0; j < size_aligned; j++) { | ||||
|         *dst++ = (*src++); | ||||
|     } | ||||
|     return; | ||||
| /// AppLoader_NCCH constructor
 | ||||
| AppLoader_NCCH::AppLoader_NCCH(std::string& filename) { | ||||
|     this->filename = filename; | ||||
|     is_loaded = false; | ||||
|     is_compressed = false; | ||||
|     entry_point = 0; | ||||
|     ncch_offset = 0; | ||||
|     exefs_offset = 0; | ||||
| } | ||||
| 
 | ||||
| /// AppLoader_NCCH destructor
 | ||||
| AppLoader_NCCH::~AppLoader_NCCH() { | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads .code section into memory for booting | ||||
|  * @return ResultStatus result of function | ||||
|  */ | ||||
| const ResultStatus AppLoader_NCCH::LoadExec() const { | ||||
|     if (!is_loaded)  | ||||
|         return ResultStatus::ErrorNotLoaded; | ||||
| 
 | ||||
|     for (std::vector<u8>::size_type i = 0; i != code.size(); i++) { | ||||
|         Memory::Write8(entry_point + i, code[i]); | ||||
|     } | ||||
|     Kernel::LoadExec(entry_point); | ||||
|      | ||||
|     return ResultStatus::Success; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Reads an application section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) | ||||
|  * @param file Handle to file to read from | ||||
|  * @param name Name of section to read out of NCCH file | ||||
|  * @param buffer Buffer to read section into. | ||||
|  */ | ||||
| const ResultStatus AppLoader_NCCH::LoadSection(File::IOFile& file, const char* name,  | ||||
|     std::vector<u8>& buffer) { | ||||
|     // Iterate through the ExeFs archive until we find the .code file...
 | ||||
|     for (int i = 0; i < kExeFs_MaxSections; i++) { | ||||
|         INFO_LOG(LOADER, "ExeFS section %d:", i); | ||||
|         INFO_LOG(LOADER, "    name:   %s", exefs_header.section[i].name); | ||||
|         INFO_LOG(LOADER, "    offset: 0x%08X", exefs_header.section[i].offset); | ||||
|         INFO_LOG(LOADER, "    size:   0x%08X", exefs_header.section[i].size); | ||||
| 
 | ||||
|         // Load the .code section (executable code)...
 | ||||
|         if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { | ||||
|             s64 section_offset = (exefs_header.section[i].offset + exefs_offset +  | ||||
|                 sizeof(ExeFs_Header) + ncch_offset); | ||||
|             file.Seek(section_offset, 0); | ||||
| 
 | ||||
|             // Section is compressed...
 | ||||
|             if (i == 0 && is_compressed) { | ||||
|                 // Read compressed .code section...
 | ||||
|                 std::unique_ptr<u8[]> temp_buffer(new u8[exefs_header.section[i].size]); | ||||
|                 file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size); | ||||
| 
 | ||||
|                 // Decompress .code section...
 | ||||
|                 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], exefs_header.section[i].size); | ||||
|                 buffer.resize(decompressed_size); | ||||
|                 if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0],  | ||||
|                     decompressed_size)) { | ||||
|                     return ResultStatus::ErrorInvalidFormat; | ||||
|                 } | ||||
|             // Section is uncompressed...
 | ||||
|             } else { | ||||
|                 buffer.resize(exefs_header.section[i].size); | ||||
|                 file.ReadBytes(&buffer[0], exefs_header.section[i].size); | ||||
|             } | ||||
|             return ResultStatus::Success; | ||||
|         } | ||||
|     } | ||||
|     return ResultStatus::Error; | ||||
| }  | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) | ||||
|  * @param filename String filename of NCCH file | ||||
|  * @param error_string Pointer to string to put error message if an error has occurred | ||||
|  * @todo Move NCSD parsing out of here and create a separate function for loading these | ||||
|  * @return True on success, otherwise false | ||||
|  */ | ||||
| bool Load_NCCH(std::string& filename, std::string* error_string) { | ||||
| const ResultStatus AppLoader_NCCH::Load() { | ||||
|     INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); | ||||
| 
 | ||||
|     if (is_loaded) | ||||
|         return ResultStatus::ErrorAlreadyLoaded; | ||||
| 
 | ||||
|     File::IOFile file(filename, "rb"); | ||||
| 
 | ||||
|     if (file.IsOpen()) { | ||||
|  | @ -263,80 +195,50 @@ bool Load_NCCH(std::string& filename, std::string* error_string) { | |||
|         file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); | ||||
| 
 | ||||
|         // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
 | ||||
|         int ncch_off = 0; // Offset to NCCH header, can be 0 or after NCSD header
 | ||||
|         if (memcmp(&ncch_header.magic, "NCSD", 4) == 0) { | ||||
|         if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { | ||||
|             WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!"); | ||||
|             ncch_off = 0x4000; | ||||
|             file.Seek(ncch_off, 0); | ||||
|             ncch_offset = 0x4000; | ||||
|             file.Seek(ncch_offset, 0); | ||||
|             file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); | ||||
|         } | ||||
|          | ||||
|         // Verify we are loading the correct file type...
 | ||||
|         if (memcmp(&ncch_header.magic, "NCCH", 4) != 0) { | ||||
|             *error_string = "Invalid NCCH magic number (likely incorrect file type)"; | ||||
|             return false; | ||||
|         } | ||||
|         if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) | ||||
|             return ResultStatus::ErrorInvalidFormat; | ||||
| 
 | ||||
|         // Read ExHeader
 | ||||
|         ExHeader_Header exheader_header; | ||||
|         file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); | ||||
| 
 | ||||
|         bool is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; | ||||
|         is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; | ||||
|         entry_point = exheader_header.codeset_info.text.address; | ||||
| 
 | ||||
|         INFO_LOG(LOADER, "Name:            %s", exheader_header.codeset_info.name); | ||||
|         INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no"); | ||||
|         INFO_LOG(LOADER, "Entry point:     0x%08X", entry_point); | ||||
| 
 | ||||
|         // Read ExeFS
 | ||||
|         u32 exefs_offset = ncch_header.exefs_offset * kExeFs_BlockSize; | ||||
|         exefs_offset = ncch_header.exefs_offset * kExeFs_BlockSize; | ||||
|         u32 exefs_size = ncch_header.exefs_size * kExeFs_BlockSize; | ||||
| 
 | ||||
|         INFO_LOG(LOADER, "ExeFS offset:    0x%08X", exefs_offset); | ||||
|         INFO_LOG(LOADER, "ExeFS size:      0x%08X", exefs_size); | ||||
| 
 | ||||
|         ExeFs_Header exefs_header; | ||||
|         file.Seek(exefs_offset + ncch_off, 0); | ||||
|         file.Seek(exefs_offset + ncch_offset, 0); | ||||
|         file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); | ||||
| 
 | ||||
|         // Iterate through the ExeFs archive until we find the .code file...
 | ||||
|         for (int i = 0; i < kExeFs_MaxSections; i++) { | ||||
|             INFO_LOG(LOADER, "ExeFS section %d:", i); | ||||
|             INFO_LOG(LOADER, "    name:   %s", exefs_header.section[i].name); | ||||
|             INFO_LOG(LOADER, "    offset: 0x%08X", exefs_header.section[i].offset); | ||||
|             INFO_LOG(LOADER, "    size:   0x%08X", exefs_header.section[i].size); | ||||
|         // TODO(bunnei): Check ResultStatus here...
 | ||||
|         LoadSection(file, ".code", code); | ||||
|         LoadSection(file, ".icon", icon); | ||||
|         LoadSection(file, ".banner", banner); | ||||
|         LoadSection(file, ".logo", logo); | ||||
| 
 | ||||
|             // Load the .code section (executable code)...
 | ||||
|             if (strcmp((char*) exefs_header.section[i].name, ".code") == 0) { | ||||
|                 file.Seek(exefs_header.section[i].offset + exefs_offset + sizeof(ExeFs_Header) +  | ||||
|                     ncch_off, 0); | ||||
|         is_loaded = true; // Set state to loaded
 | ||||
| 
 | ||||
|                 u8* buffer = new u8[exefs_header.section[i].size]; | ||||
|                 file.ReadBytes(buffer, exefs_header.section[i].size); | ||||
|         LoadExec(); // Load the executable into memory for booting
 | ||||
| 
 | ||||
|                 // Load compressed executable...
 | ||||
|                 if (i == 0 && is_compressed) { | ||||
|                     u32 decompressed_size = LZSS_GetDecompressedSize(buffer, | ||||
|                         exefs_header.section[i].size); | ||||
| 
 | ||||
|                     if (!LZSS_Decompress(buffer, exefs_header.section[i].size, | ||||
|                         Memory::GetPointer(exheader_header.codeset_info.text.address), | ||||
|                         decompressed_size, error_string)) { | ||||
|                         return false; | ||||
|                     } | ||||
|                 // Load uncompressed executable...
 | ||||
|                 } else { | ||||
|                     // Load .code section into memory...
 | ||||
|                     LoadBuffer(exheader_header.codeset_info.text.address, buffer,  | ||||
|                         exefs_header.section[i].size); | ||||
|                 } | ||||
|                 delete[] buffer; | ||||
| 
 | ||||
|                 // Setup kernel emulation to boot .code section...
 | ||||
|                 Kernel::LoadExec(exheader_header.codeset_info.text.address); | ||||
| 
 | ||||
|                 // No need to load the other files from ExeFS until we do something with them...
 | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return ResultStatus::Success; | ||||
|     } | ||||
|     return false; | ||||
|     return ResultStatus::Error; | ||||
| } | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  |  | |||
|  | @ -5,17 +5,184 @@ | |||
| #pragma once | ||||
| 
 | ||||
| #include "common/common.h" | ||||
| #include "common/file_util.h" | ||||
| 
 | ||||
| #include "core/loader/loader.h" | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| /// NCCH header (Note: "NCCH" appears to be a publically unknown acronym)
 | ||||
| 
 | ||||
| struct NCCH_Header { | ||||
|     u8 signature[0x100]; | ||||
|     char magic[4]; | ||||
|     u32 content_size; | ||||
|     u8 partition_id[8]; | ||||
|     u16 maker_code; | ||||
|     u16 version; | ||||
|     u8 reserved_0[4]; | ||||
|     u8 program_id[8]; | ||||
|     u8 temp_flag; | ||||
|     u8 reserved_1[0x2f]; | ||||
|     u8 product_code[0x10]; | ||||
|     u8 extended_header_hash[0x20]; | ||||
|     u32 extended_header_size; | ||||
|     u8 reserved_2[4]; | ||||
|     u8 flags[8]; | ||||
|     u32 plain_region_offset; | ||||
|     u32 plain_region_size; | ||||
|     u8 reserved_3[8]; | ||||
|     u32 exefs_offset; | ||||
|     u32 exefs_size; | ||||
|     u32 exefs_hash_region_size; | ||||
|     u8 reserved_4[4]; | ||||
|     u32 romfs_offset; | ||||
|     u32 romfs_size; | ||||
|     u32 romfs_hash_region_size; | ||||
|     u8 reserved_5[4]; | ||||
|     u8 exefs_super_block_hash[0x20]; | ||||
|     u8 romfs_super_block_hash[0x20]; | ||||
| }; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // ExeFS (executable file system) headers
 | ||||
| 
 | ||||
| typedef struct { | ||||
|     char name[8]; | ||||
|     u32 offset; | ||||
|     u32 size; | ||||
| } ExeFs_SectionHeader; | ||||
| 
 | ||||
| typedef struct { | ||||
|     ExeFs_SectionHeader section[8]; | ||||
|     u8 reserved[0x80]; | ||||
|     u8 hashes[8][0x20]; | ||||
| } ExeFs_Header; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // ExHeader (executable file system header) headers
 | ||||
| 
 | ||||
| struct ExHeader_SystemInfoFlags{ | ||||
|     u8 reserved[5]; | ||||
|     u8 flag; | ||||
|     u8 remaster_version[2]; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_CodeSegmentInfo{ | ||||
|     u32 address; | ||||
|     u32 num_max_pages; | ||||
|     u32 code_size; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_CodeSetInfo { | ||||
|     u8 name[8]; | ||||
|     ExHeader_SystemInfoFlags flags; | ||||
|     ExHeader_CodeSegmentInfo text; | ||||
|     u8 stacksize[4]; | ||||
|     ExHeader_CodeSegmentInfo ro; | ||||
|     u8 reserved[4]; | ||||
|     ExHeader_CodeSegmentInfo data; | ||||
|     u8 bsssize[4]; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_DependencyList{ | ||||
|     u8 program_id[0x30][8]; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_SystemInfo{ | ||||
|     u32 save_data_size; | ||||
|     u8 reserved[4]; | ||||
|     u8 jump_id[8]; | ||||
|     u8 reserved_2[0x30]; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_StorageInfo{ | ||||
|     u8 ext_save_data_id[8]; | ||||
|     u8 system_save_data_id[8]; | ||||
|     u8 reserved[8]; | ||||
|     u8 access_info[7]; | ||||
|     u8 other_attributes; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_ARM11_SystemLocalCaps{ | ||||
|     u8 program_id[8]; | ||||
|     u8 flags[8]; | ||||
|     u8 resource_limit_descriptor[0x10][2]; | ||||
|     ExHeader_StorageInfo storage_info; | ||||
|     u8 service_access_control[0x20][8]; | ||||
|     u8 reserved[0x1f]; | ||||
|     u8 resource_limit_category; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_ARM11_KernelCaps{ | ||||
|     u8 descriptors[28][4]; | ||||
|     u8 reserved[0x10]; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_ARM9_AccessControl{ | ||||
|     u8 descriptors[15]; | ||||
|     u8 descversion; | ||||
| }; | ||||
| 
 | ||||
| struct ExHeader_Header{ | ||||
|     ExHeader_CodeSetInfo codeset_info; | ||||
|     ExHeader_DependencyList dependency_list; | ||||
|     ExHeader_SystemInfo system_info; | ||||
|     ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; | ||||
|     ExHeader_ARM11_KernelCaps arm11_kernel_caps; | ||||
|     ExHeader_ARM9_AccessControl arm9_access_control; | ||||
|     struct { | ||||
|         u8 signature[0x100]; | ||||
|         u8 ncch_public_key_modulus[0x100]; | ||||
|         ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; | ||||
|         ExHeader_ARM11_KernelCaps arm11_kernel_caps; | ||||
|         ExHeader_ARM9_AccessControl arm9_access_control; | ||||
|     } access_desc; | ||||
| }; | ||||
| 
 | ||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||
| // Loader namespace
 | ||||
| 
 | ||||
| namespace Loader { | ||||
| 
 | ||||
| /**
 | ||||
|  * Loads an NCCH file (e.g. from a CCI or CXI) | ||||
|  * @param filename String filename of NCCH file | ||||
|  * @param error_string Pointer to string to put error message if an error has occurred | ||||
|  * @return True on success, otherwise false | ||||
|  */ | ||||
| bool Load_NCCH(std::string& filename, std::string* error_string); | ||||
| /// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
 | ||||
| class AppLoader_NCCH : public AppLoader { | ||||
| public: | ||||
|     AppLoader_NCCH(std::string& filename); | ||||
|     ~AppLoader_NCCH(); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Load the application | ||||
|      * @return ResultStatus result of function | ||||
|      */ | ||||
|     const ResultStatus Load(); | ||||
| 
 | ||||
| private: | ||||
| 
 | ||||
|     /**
 | ||||
|      * Reads an application section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) | ||||
|      * @param file Handle to file to read from | ||||
|      * @param name Name of section to read out of NCCH file | ||||
|      * @param buffer Buffer to read section into. | ||||
|      */ | ||||
|     const ResultStatus LoadSection(File::IOFile& file, const char* name,  | ||||
|         std::vector<u8>& buffer); | ||||
| 
 | ||||
|     /**
 | ||||
|      * Loads .code section into memory for booting | ||||
|      * @return ResultStatus result of function | ||||
|      */ | ||||
|     const ResultStatus LoadExec() const; | ||||
| 
 | ||||
|     std::string     filename; | ||||
|     bool            is_loaded; | ||||
|     bool            is_compressed; | ||||
|     u32             entry_point; | ||||
| 
 | ||||
|     u32             ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header
 | ||||
|     u32             exefs_offset; | ||||
|      | ||||
|     ExeFs_Header    exefs_header; | ||||
|     ExHeader_Header exheader_header; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Loader
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue