mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-30 21:30:04 +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
 | /// Application entry point
 | ||||||
| int __cdecl main(int argc, char **argv) { | int __cdecl main(int argc, char **argv) { | ||||||
|     std::string program_dir = File::GetCurrentDir(); |  | ||||||
| 
 |  | ||||||
|     LogManager::Init(); |     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; |     EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; | ||||||
| 
 | 
 | ||||||
|     System::Init(emu_window); |     System::Init(emu_window); | ||||||
| 
 | 
 | ||||||
|     std::string boot_filename; |     if (Loader::ResultStatus::Success != Loader::LoadFile(boot_filename)) { | ||||||
| 
 |         ERROR_LOG(BOOT, "Failed to load ROM!"); | ||||||
|     if (argc < 2) { |         return -1; | ||||||
|         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()); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Core::RunLoop(); |     Core::RunLoop(); | ||||||
|  |  | ||||||
|  | @ -135,11 +135,8 @@ void GMainWindow::BootGame(const char* filename) | ||||||
| 
 | 
 | ||||||
|     // Load a game or die...
 |     // Load a game or die...
 | ||||||
|     std::string boot_filename = filename; |     std::string boot_filename = filename; | ||||||
|     std::string error_str; |     if (Loader::ResultStatus::Success != Loader::LoadFile(boot_filename)) { | ||||||
|     bool res = Loader::LoadFile(boot_filename, &error_str); |         ERROR_LOG(BOOT, "Failed to load ROM!"); | ||||||
| 
 |  | ||||||
|     if (!res) { |  | ||||||
|         ERROR_LOG(BOOT, "Failed to load ROM: %s", error_str.c_str()); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     disasmWidget->Init(); |     disasmWidget->Init(); | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
| #include <string> | #include <string> | ||||||
|  | #include <memory> | ||||||
| 
 | 
 | ||||||
| #include "common/common.h" | #include "common/common.h" | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
|  | @ -12,6 +13,220 @@ | ||||||
| #include "core/loader/elf.h" | #include "core/loader/elf.h" | ||||||
| #include "core/hle/kernel/kernel.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) { | ElfReader::ElfReader(void *ptr) { | ||||||
|     base = (char*)ptr; |     base = (char*)ptr; | ||||||
|     base32 = (u32 *)ptr; |     base32 = (u32 *)ptr; | ||||||
|  | @ -29,12 +244,12 @@ const char *ElfReader::GetSectionName(int section) const { | ||||||
|     if (sections[section].sh_type == SHT_NULL) |     if (sections[section].sh_type == SHT_NULL) | ||||||
|         return nullptr; |         return nullptr; | ||||||
| 
 | 
 | ||||||
|     int nameOffset = sections[section].sh_name; |     int name_offset = sections[section].sh_name; | ||||||
|     char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); |     char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); | ||||||
| 
 | 
 | ||||||
|     if (ptr) |     if (ptr) | ||||||
|         return ptr + nameOffset; |         return ptr + name_offset; | ||||||
|     else | 
 | ||||||
|     return nullptr; |     return nullptr; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -42,15 +257,12 @@ bool ElfReader::LoadInto(u32 vaddr) { | ||||||
|     DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); |     DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); | ||||||
| 
 | 
 | ||||||
|     // Should we relocate?
 |     // 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"); |         DEBUG_LOG(MASTER_LOG, "Relocatable module"); | ||||||
|         entryPoint += vaddr; |         entryPoint += vaddr; | ||||||
|     } |     } else { | ||||||
|     else |  | ||||||
|     { |  | ||||||
|         DEBUG_LOG(MASTER_LOG, "Prerelocated executable"); |         DEBUG_LOG(MASTER_LOG, "Prerelocated executable"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -58,17 +270,14 @@ bool ElfReader::LoadInto(u32 vaddr) { | ||||||
| 
 | 
 | ||||||
|     // First pass : Get the bits into RAM
 |     // First pass : Get the bits into RAM
 | ||||||
|     u32 segmentVAddr[32]; |     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; |         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); |         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; |             segmentVAddr[i] = baseAddress + p->p_vaddr; | ||||||
|             u32 writeAddr = segmentVAddr[i]; |             u32 writeAddr = segmentVAddr[i]; | ||||||
| 
 | 
 | ||||||
|  | @ -78,27 +287,19 @@ bool ElfReader::LoadInto(u32 vaddr) { | ||||||
|             u32 dstSize = p->p_memsz; |             u32 dstSize = p->p_memsz; | ||||||
|             u32 *s = (u32*)src; |             u32 *s = (u32*)src; | ||||||
|             u32 *d = (u32*)dst; |             u32 *d = (u32*)dst; | ||||||
|             for (int j = 0; j < (int)(srcSize + 3) / 4; j++) |             for (int j = 0; j < (int)(srcSize + 3) / 4; j++) { | ||||||
|             { |                 *d++ = (*s++); | ||||||
|                 *d++ = /*_byteswap_ulong*/(*s++); |  | ||||||
|             } |  | ||||||
|             if (srcSize < dstSize) |  | ||||||
|             { |  | ||||||
|                 //memset(dst + srcSize, 0, dstSize-srcSize); //zero out bss
 |  | ||||||
|             } |             } | ||||||
|             INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); |             INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|     INFO_LOG(MASTER_LOG, "Done loading."); |     INFO_LOG(MASTER_LOG, "Done loading."); | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const | SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { | ||||||
| { |     for (int i = firstSection; i < header->e_shnum; i++) { | ||||||
|     for (int i = firstSection; i < header->e_shnum; i++) |  | ||||||
|     { |  | ||||||
|         const char *secname = GetSectionName(i); |         const char *secname = GetSectionName(i); | ||||||
| 
 | 
 | ||||||
|         if (secname != nullptr && strcmp(name, secname) == 0) |         if (secname != nullptr && strcmp(name, secname) == 0) | ||||||
|  | @ -107,25 +308,21 @@ SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const | ||||||
|     return -1; |     return -1; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool ElfReader::LoadSymbols() | bool ElfReader::LoadSymbols() { | ||||||
| { |  | ||||||
|     bool hasSymbols = false; |     bool hasSymbols = false; | ||||||
|     SectionID sec = GetSectionByName(".symtab"); |     SectionID sec = GetSectionByName(".symtab"); | ||||||
|     if (sec != -1) |     if (sec != -1) { | ||||||
|     { |  | ||||||
|         int stringSection = sections[sec].sh_link; |         int stringSection = sections[sec].sh_link; | ||||||
|         const char *stringBase = (const char *)GetSectionDataPtr(stringSection); |         const char *stringBase = (const char *)GetSectionDataPtr(stringSection); | ||||||
| 
 | 
 | ||||||
|         //We have a symbol table!
 |         //We have a symbol table!
 | ||||||
|         Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); |         Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); | ||||||
|         int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); |         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; |             int size = symtab[sym].st_size; | ||||||
|             if (size == 0) |             if (size == 0) | ||||||
|                 continue; |                 continue; | ||||||
| 
 | 
 | ||||||
|             // int bind = symtab[sym].st_info >> 4;
 |  | ||||||
|             int type = symtab[sym].st_info & 0xF; |             int type = symtab[sym].st_info & 0xF; | ||||||
| 
 | 
 | ||||||
|             const char *name = stringBase + symtab[sym].st_name; |             const char *name = stringBase + symtab[sym].st_name; | ||||||
|  | @ -144,42 +341,41 @@ bool ElfReader::LoadSymbols() | ||||||
| 
 | 
 | ||||||
| namespace Loader { | 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 |  * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) | ||||||
|  * @param filename String filename of ELF file |  | ||||||
|  * @param error_string Pointer to string to put error message if an error has occurred |  * @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 |  * @return True on success, otherwise false | ||||||
|  */ |  */ | ||||||
| bool Load_ELF(std::string& filename, std::string* error_string) { | const ResultStatus AppLoader_ELF::Load() { | ||||||
|     std::string full_path = filename; |     INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str()); | ||||||
|     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"); |  | ||||||
| 
 | 
 | ||||||
|     if (f.IsOpen()) { |     if (is_loaded) | ||||||
|         u32 size = (u32)f.GetSize(); |         return ResultStatus::ErrorAlreadyLoaded; | ||||||
|         u8* buffer = new u8[size]; |  | ||||||
|         ElfReader* elf_reader = NULL; |  | ||||||
| 
 | 
 | ||||||
|         f.ReadBytes(buffer, size); |     File::IOFile file(filename, "rb"); | ||||||
| 
 | 
 | ||||||
|         elf_reader = new ElfReader(buffer); |     if (file.IsOpen()) { | ||||||
|         elf_reader->LoadInto(0x00100000); |         u32 size = (u32)file.GetSize(); | ||||||
|  |         std::unique_ptr<u8[]> buffer(new u8[size]); | ||||||
|  |         file.ReadBytes(&buffer[0], size); | ||||||
| 
 | 
 | ||||||
|         Kernel::LoadExec(elf_reader->GetEntryPoint()); |         ElfReader elf_reader(&buffer[0]); | ||||||
| 
 |         elf_reader.LoadInto(0x00100000); | ||||||
|         delete[] buffer; |         Kernel::LoadExec(elf_reader.GetEntryPoint()); | ||||||
|         delete elf_reader; |  | ||||||
|     } else { |     } else { | ||||||
|         *error_string = "Unable to open ELF file!"; |         return ResultStatus::Error; | ||||||
|         return false; |  | ||||||
|     } |     } | ||||||
|     f.Close(); |     return ResultStatus::Success; | ||||||
| 
 |  | ||||||
|     return true; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace Loader
 | } // namespace Loader
 | ||||||
|  |  | ||||||
|  | @ -5,226 +5,28 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| 
 | #include "core/loader/loader.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; |  | ||||||
|     } |  | ||||||
| }; |  | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
| // Loader namespace
 | // Loader namespace
 | ||||||
| 
 | 
 | ||||||
| namespace Loader { | namespace Loader { | ||||||
| 
 | 
 | ||||||
|  | /// Loads an ELF/AXF file
 | ||||||
|  | class AppLoader_ELF : public AppLoader { | ||||||
|  | public: | ||||||
|  |     AppLoader_ELF(std::string& filename); | ||||||
|  |     ~AppLoader_ELF(); | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|  * Loads an ELF file |      * Load the bootable file | ||||||
|  * @param filename String filename of ELF file |      * @return ResultStatus result of function | ||||||
|  * @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); |     const ResultStatus Load(); | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::string filename; | ||||||
|  |     bool        is_loaded; | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
| } // namespace Loader
 | } // namespace Loader
 | ||||||
|  |  | ||||||
|  | @ -2,6 +2,8 @@ | ||||||
| // Licensed under GPLv2
 | // Licensed under GPLv2
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <memory> | ||||||
|  | 
 | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| #include "core/loader/elf.h" | #include "core/loader/elf.h" | ||||||
| #include "core/loader/ncch.h" | #include "core/loader/ncch.h" | ||||||
|  | @ -16,59 +18,60 @@ namespace Loader { | ||||||
|  * @todo (ShizZy) this function sucks... make it actually check file contents etc. |  * @todo (ShizZy) this function sucks... make it actually check file contents etc. | ||||||
|  * @return FileType of file |  * @return FileType of file | ||||||
|  */ |  */ | ||||||
| FileType IdentifyFile(std::string &filename) { | const FileType IdentifyFile(const std::string &filename) { | ||||||
|     if (filename.size() == 0) { |     if (filename.size() == 0) { | ||||||
|         ERROR_LOG(LOADER, "invalid filename %s", filename.c_str()); |         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) : ""; |     std::string extension = filename.size() >= 5 ? filename.substr(filename.size() - 4) : ""; | ||||||
| 
 | 
 | ||||||
|     if (!strcasecmp(extension.c_str(), ".elf")) { |     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")) { |     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")) { |     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")) { |     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 |  * Identifies and loads a bootable file | ||||||
|  * @param filename String filename of 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 ResultStatus result of function | ||||||
|  * @return True on success, otherwise false |  | ||||||
|  */ |  */ | ||||||
| bool LoadFile(std::string &filename, std::string *error_string) { | const ResultStatus LoadFile(std::string& filename) { | ||||||
|     INFO_LOG(LOADER, "Identifying file..."); |     INFO_LOG(LOADER, "Loading file %s...", filename.c_str()); | ||||||
| 
 | 
 | ||||||
|     // Note that this can modify filename!
 |  | ||||||
|     switch (IdentifyFile(filename)) { |     switch (IdentifyFile(filename)) { | ||||||
| 
 | 
 | ||||||
|     case FILETYPE_CTR_ELF: |     // Standard ELF file format...
 | ||||||
|         return Loader::Load_ELF(filename, error_string); |     case FileType::ELF: { | ||||||
| 
 |         return AppLoader_ELF(filename).Load(); | ||||||
|     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; |  | ||||||
|     } |     } | ||||||
|     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
 | } // namespace Loader
 | ||||||
|  |  | ||||||
|  | @ -4,6 +4,8 @@ | ||||||
| 
 | 
 | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
|  | #include <vector> | ||||||
|  | 
 | ||||||
| #include "common/common.h" | #include "common/common.h" | ||||||
| 
 | 
 | ||||||
| ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | @ -11,16 +13,94 @@ | ||||||
| 
 | 
 | ||||||
| namespace Loader { | namespace Loader { | ||||||
| 
 | 
 | ||||||
| enum FileType { | /// File types supported by CTR
 | ||||||
|     FILETYPE_ERROR, | enum class FileType { | ||||||
|  |     Error, | ||||||
|  |     Unknown, | ||||||
|  |     CCI, | ||||||
|  |     CXI, | ||||||
|  |     CIA, | ||||||
|  |     ELF, | ||||||
|  | }; | ||||||
| 
 | 
 | ||||||
|     FILETYPE_CTR_CCI, | /// Return type for functions in Loader namespace
 | ||||||
|     FILETYPE_CTR_CIA, | enum class ResultStatus { | ||||||
|     FILETYPE_CTR_CXI, |     Success, | ||||||
|     FILETYPE_CTR_ELF, |     Error, | ||||||
|     FILETYPE_CTR_BIN, |     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 |  * @param filename String filename of bootable file | ||||||
|  * @return FileType of file |  * @return FileType of file | ||||||
|  */ |  */ | ||||||
| FileType IdentifyFile(std::string &filename); | const FileType IdentifyFile(const std::string &filename); | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Identifies and loads a bootable file |  * Identifies and loads a bootable file | ||||||
|  * @param filename String filename of 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 ResultStatus result of function | ||||||
|  * @return True on success, otherwise false |  | ||||||
|  */ |  */ | ||||||
| bool LoadFile(std::string &filename, std::string *error_string); | const ResultStatus LoadFile(std::string& filename); | ||||||
| 
 | 
 | ||||||
| } // namespace
 | } // namespace
 | ||||||
|  |  | ||||||
|  | @ -2,142 +2,14 @@ | ||||||
| // Licensed under GPLv2
 | // Licensed under GPLv2
 | ||||||
| // Refer to the license.txt file included.
 | // Refer to the license.txt file included.
 | ||||||
| 
 | 
 | ||||||
|  | #include <memory> | ||||||
|  | 
 | ||||||
| #include "common/file_util.h" | #include "common/file_util.h" | ||||||
| 
 | 
 | ||||||
| #include "core/loader/ncch.h" | #include "core/loader/ncch.h" | ||||||
| #include "core/hle/kernel/kernel.h" | #include "core/hle/kernel/kernel.h" | ||||||
| #include "core/mem_map.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
 | // Loader namespace
 | ||||||
| 
 | 
 | ||||||
|  | @ -163,11 +35,9 @@ u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { | ||||||
|  * @param compressed_size Size of compressed buffer |  * @param compressed_size Size of compressed buffer | ||||||
|  * @param decompressed Decompressed buffer |  * @param decompressed Decompressed buffer | ||||||
|  * @param decompressed_size Size of 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 |  * @return True on success, otherwise false | ||||||
|  */ |  */ | ||||||
| bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size,  | bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { | ||||||
|     std::string* error_string) { |  | ||||||
|     u8* footer = compressed + compressed_size - 8; |     u8* footer = compressed + compressed_size - 8; | ||||||
|     u32 buffer_top_and_bottom = *(u32*)footer; |     u32 buffer_top_and_bottom = *(u32*)footer; | ||||||
|     u32 i, j; |     u32 i, j; | ||||||
|  | @ -191,8 +61,8 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 | ||||||
|                 break; |                 break; | ||||||
| 
 | 
 | ||||||
|             if(control & 0x80) { |             if(control & 0x80) { | ||||||
|  |                 // Check if compression is out of bounds
 | ||||||
|                 if(index < 2) { |                 if(index < 2) { | ||||||
|                     *error_string = "Compression out of bounds"; |  | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|                 index -= 2; |                 index -= 2; | ||||||
|  | @ -202,22 +72,22 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 | ||||||
|                 segment_offset &= 0x0FFF; |                 segment_offset &= 0x0FFF; | ||||||
|                 segment_offset += 2; |                 segment_offset += 2; | ||||||
| 
 | 
 | ||||||
|  |                 // Check if compression is out of bounds
 | ||||||
|                 if(out < segment_size) { |                 if(out < segment_size) { | ||||||
|                     *error_string = "Compression out of bounds"; |  | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|                 for(j = 0; j < segment_size; j++) { |                 for(j = 0; j < segment_size; j++) { | ||||||
|                     u8 data; |                     u8 data; | ||||||
|  |                     // Check if compression is out of bounds
 | ||||||
|                     if(out + segment_offset >= decompressed_size) { |                     if(out + segment_offset >= decompressed_size) { | ||||||
|                         *error_string = "Compression out of bounds"; |  | ||||||
|                         return false; |                         return false; | ||||||
|                     } |                     } | ||||||
|                     data  = decompressed[out + segment_offset]; |                     data  = decompressed[out + segment_offset]; | ||||||
|                     decompressed[--out] = data; |                     decompressed[--out] = data; | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|  |                 // Check if compression is out of bounds
 | ||||||
|                 if(out < 1) { |                 if(out < 1) { | ||||||
|                     *error_string = "Compression out of bounds"; |  | ||||||
|                     return false; |                     return false; | ||||||
|                 } |                 } | ||||||
|                 decompressed[--out] = compressed[--index]; |                 decompressed[--out] = compressed[--index]; | ||||||
|  | @ -228,73 +98,47 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 | ||||||
|     return true; |     return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | ////////////////////////////////////////////////////////////////////////////////////////////////////
 | ||||||
|  * Load a data buffer into memory at the specified address | // AppLoader_NCCH class
 | ||||||
|  * @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; |  | ||||||
| 
 | 
 | ||||||
|     for (int j = 0; j < size_aligned; j++) { | /// AppLoader_NCCH constructor
 | ||||||
|         *dst++ = (*src++); | 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; | ||||||
| } | } | ||||||
|     return; | 
 | ||||||
|  | /// AppLoader_NCCH destructor
 | ||||||
|  | AppLoader_NCCH::~AppLoader_NCCH() { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) |  * Loads .code section into memory for booting | ||||||
|  * @param filename String filename of NCCH file |  * @return ResultStatus result of function | ||||||
|  * @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::LoadExec() const { | ||||||
|     INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); |     if (!is_loaded)  | ||||||
|  |         return ResultStatus::ErrorNotLoaded; | ||||||
| 
 | 
 | ||||||
|     File::IOFile file(filename, "rb"); |     for (std::vector<u8>::size_type i = 0; i != code.size(); i++) { | ||||||
| 
 |         Memory::Write8(entry_point + i, code[i]); | ||||||
|     if (file.IsOpen()) { |  | ||||||
|         NCCH_Header ncch_header; |  | ||||||
|         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) { |  | ||||||
|             WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!"); |  | ||||||
|             ncch_off = 0x4000; |  | ||||||
|             file.Seek(ncch_off, 0); |  | ||||||
|             file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); |  | ||||||
|     } |     } | ||||||
|         // Verify we are loading the correct file type...
 |     Kernel::LoadExec(entry_point); | ||||||
|         if (memcmp(&ncch_header.magic, "NCCH", 4) != 0) { |      | ||||||
|             *error_string = "Invalid NCCH magic number (likely incorrect file type)"; |     return ResultStatus::Success; | ||||||
|             return false; |  | ||||||
| } | } | ||||||
|         // Read ExHeader
 |  | ||||||
|         ExHeader_Header exheader_header; |  | ||||||
|         file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); |  | ||||||
| 
 |  | ||||||
|         bool is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; |  | ||||||
| 
 |  | ||||||
|         INFO_LOG(LOADER, "Name:            %s", exheader_header.codeset_info.name); |  | ||||||
|         INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no"); |  | ||||||
| 
 |  | ||||||
|         // Read ExeFS
 |  | ||||||
|         u32 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.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); |  | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * 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...
 |     // Iterate through the ExeFs archive until we find the .code file...
 | ||||||
|     for (int i = 0; i < kExeFs_MaxSections; i++) { |     for (int i = 0; i < kExeFs_MaxSections; i++) { | ||||||
|         INFO_LOG(LOADER, "ExeFS section %d:", i); |         INFO_LOG(LOADER, "ExeFS section %d:", i); | ||||||
|  | @ -303,40 +147,98 @@ bool Load_NCCH(std::string& filename, std::string* error_string) { | ||||||
|         INFO_LOG(LOADER, "    size:   0x%08X", exefs_header.section[i].size); |         INFO_LOG(LOADER, "    size:   0x%08X", exefs_header.section[i].size); | ||||||
| 
 | 
 | ||||||
|         // Load the .code section (executable code)...
 |         // Load the .code section (executable code)...
 | ||||||
|             if (strcmp((char*) exefs_header.section[i].name, ".code") == 0) { |         if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { | ||||||
|                 file.Seek(exefs_header.section[i].offset + exefs_offset + sizeof(ExeFs_Header) +  |             s64 section_offset = (exefs_header.section[i].offset + exefs_offset +  | ||||||
|                     ncch_off, 0); |                 sizeof(ExeFs_Header) + ncch_offset); | ||||||
|  |             file.Seek(section_offset, 0); | ||||||
| 
 | 
 | ||||||
|                 u8* buffer = new u8[exefs_header.section[i].size]; |             // Section is compressed...
 | ||||||
|                 file.ReadBytes(buffer, exefs_header.section[i].size); |  | ||||||
| 
 |  | ||||||
|                 // Load compressed executable...
 |  | ||||||
|             if (i == 0 && is_compressed) { |             if (i == 0 && is_compressed) { | ||||||
|                     u32 decompressed_size = LZSS_GetDecompressedSize(buffer, |                 // Read compressed .code section...
 | ||||||
|                         exefs_header.section[i].size); |                 std::unique_ptr<u8[]> temp_buffer(new u8[exefs_header.section[i].size]); | ||||||
|  |                 file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size); | ||||||
| 
 | 
 | ||||||
|                     if (!LZSS_Decompress(buffer, exefs_header.section[i].size, |                 // Decompress .code section...
 | ||||||
|                         Memory::GetPointer(exheader_header.codeset_info.text.address), |                 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], exefs_header.section[i].size); | ||||||
|                         decompressed_size, error_string)) { |                 buffer.resize(decompressed_size); | ||||||
|                         return false; |                 if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0],  | ||||||
|  |                     decompressed_size)) { | ||||||
|  |                     return ResultStatus::ErrorInvalidFormat; | ||||||
|                 } |                 } | ||||||
|                 // Load uncompressed executable...
 |             // Section is uncompressed...
 | ||||||
|             } else { |             } else { | ||||||
|                     // Load .code section into memory...
 |                 buffer.resize(exefs_header.section[i].size); | ||||||
|                     LoadBuffer(exheader_header.codeset_info.text.address, buffer,  |                 file.ReadBytes(&buffer[0], exefs_header.section[i].size); | ||||||
|                         exefs_header.section[i].size); |             } | ||||||
|  |             return ResultStatus::Success; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return ResultStatus::Error; | ||||||
| }  | }  | ||||||
|                 delete[] buffer; |  | ||||||
| 
 | 
 | ||||||
|                 // Setup kernel emulation to boot .code section...
 | /**
 | ||||||
|                 Kernel::LoadExec(exheader_header.codeset_info.text.address); |  * 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 | ||||||
|  |  */ | ||||||
|  | const ResultStatus AppLoader_NCCH::Load() { | ||||||
|  |     INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); | ||||||
| 
 | 
 | ||||||
|                 // No need to load the other files from ExeFS until we do something with them...
 |     if (is_loaded) | ||||||
|                 return true; |         return ResultStatus::ErrorAlreadyLoaded; | ||||||
|  | 
 | ||||||
|  |     File::IOFile file(filename, "rb"); | ||||||
|  | 
 | ||||||
|  |     if (file.IsOpen()) { | ||||||
|  |         NCCH_Header ncch_header; | ||||||
|  |         file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); | ||||||
|  | 
 | ||||||
|  |         // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
 | ||||||
|  |         if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { | ||||||
|  |             WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!"); | ||||||
|  |             ncch_offset = 0x4000; | ||||||
|  |             file.Seek(ncch_offset, 0); | ||||||
|  |             file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); | ||||||
|         } |         } | ||||||
|  |          | ||||||
|  |         // Verify we are loading the correct file type...
 | ||||||
|  |         if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) | ||||||
|  |             return ResultStatus::ErrorInvalidFormat; | ||||||
|  | 
 | ||||||
|  |         // Read ExHeader
 | ||||||
|  |         file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); | ||||||
|  | 
 | ||||||
|  |         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
 | ||||||
|  |         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); | ||||||
|  | 
 | ||||||
|  |         file.Seek(exefs_offset + ncch_offset, 0); | ||||||
|  |         file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); | ||||||
|  | 
 | ||||||
|  |         // TODO(bunnei): Check ResultStatus here...
 | ||||||
|  |         LoadSection(file, ".code", code); | ||||||
|  |         LoadSection(file, ".icon", icon); | ||||||
|  |         LoadSection(file, ".banner", banner); | ||||||
|  |         LoadSection(file, ".logo", logo); | ||||||
|  | 
 | ||||||
|  |         is_loaded = true; // Set state to loaded
 | ||||||
|  | 
 | ||||||
|  |         LoadExec(); // Load the executable into memory for booting
 | ||||||
|  | 
 | ||||||
|  |         return ResultStatus::Success; | ||||||
|     } |     } | ||||||
|     } |     return ResultStatus::Error; | ||||||
|     return false; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| } // namespace Loader
 | } // namespace Loader
 | ||||||
|  |  | ||||||
|  | @ -5,17 +5,184 @@ | ||||||
| #pragma once | #pragma once | ||||||
| 
 | 
 | ||||||
| #include "common/common.h" | #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 { | namespace Loader { | ||||||
| 
 | 
 | ||||||
|  | /// 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(); | ||||||
|  | 
 | ||||||
|     /**
 |     /**
 | ||||||
|  * Loads an NCCH file (e.g. from a CCI or CXI) |      * Load the application | ||||||
|  * @param filename String filename of NCCH file |      * @return ResultStatus result of function | ||||||
|  * @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); |     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
 | } // namespace Loader
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue