summaryrefslogtreecommitdiff
path: root/src/core/loader
diff options
context:
space:
mode:
authorGravatar bunnei2014-06-18 18:58:09 -0400
committerGravatar bunnei2014-06-24 19:29:58 -0400
commit7889cafc76ac99b8509fa3cd1558a09f8a7e5f91 (patch)
treee6ffea9ec1c334bfca13404c47a2191fd281554c /src/core/loader
parentNCCH: Changed decompression to load .code directly into memory rather than an... (diff)
downloadyuzu-7889cafc76ac99b8509fa3cd1558a09f8a7e5f91.tar.gz
yuzu-7889cafc76ac99b8509fa3cd1558a09f8a7e5f91.tar.xz
yuzu-7889cafc76ac99b8509fa3cd1558a09f8a7e5f91.zip
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.
Diffstat (limited to 'src/core/loader')
-rw-r--r--src/core/loader/elf.cpp318
-rw-r--r--src/core/loader/elf.h230
-rw-r--r--src/core/loader/loader.cpp55
-rw-r--r--src/core/loader/loader.h103
-rw-r--r--src/core/loader/ncch.cpp308
-rw-r--r--src/core/loader/ncch.h181
6 files changed, 672 insertions, 523 deletions
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index f93354817..065601546 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <string> 5#include <string>
6#include <memory>
6 7
7#include "common/common.h" 8#include "common/common.h"
8#include "common/file_util.h" 9#include "common/file_util.h"
@@ -12,6 +13,220 @@
12#include "core/loader/elf.h" 13#include "core/loader/elf.h"
13#include "core/hle/kernel/kernel.h" 14#include "core/hle/kernel/kernel.h"
14 15
16////////////////////////////////////////////////////////////////////////////////////////////////////
17// ELF Header Constants
18
19// File type
20enum ElfType {
21 ET_NONE = 0,
22 ET_REL = 1,
23 ET_EXEC = 2,
24 ET_DYN = 3,
25 ET_CORE = 4,
26 ET_LOPROC = 0xFF00,
27 ET_HIPROC = 0xFFFF,
28};
29
30// Machine/Architecture
31enum ElfMachine {
32 EM_NONE = 0,
33 EM_M32 = 1,
34 EM_SPARC = 2,
35 EM_386 = 3,
36 EM_68K = 4,
37 EM_88K = 5,
38 EM_860 = 7,
39 EM_MIPS = 8
40};
41
42// File version
43#define EV_NONE 0
44#define EV_CURRENT 1
45
46// Identification index
47#define EI_MAG0 0
48#define EI_MAG1 1
49#define EI_MAG2 2
50#define EI_MAG3 3
51#define EI_CLASS 4
52#define EI_DATA 5
53#define EI_VERSION 6
54#define EI_PAD 7
55#define EI_NIDENT 16
56
57// Magic number
58#define ELFMAG0 0x7F
59#define ELFMAG1 'E'
60#define ELFMAG2 'L'
61#define ELFMAG3 'F'
62
63// Sections constants
64
65// Section types
66#define SHT_NULL 0
67#define SHT_PROGBITS 1
68#define SHT_SYMTAB 2
69#define SHT_STRTAB 3
70#define SHT_RELA 4
71#define SHT_HASH 5
72#define SHT_DYNAMIC 6
73#define SHT_NOTE 7
74#define SHT_NOBITS 8
75#define SHT_REL 9
76#define SHT_SHLIB 10
77#define SHT_DYNSYM 11
78#define SHT_LOPROC 0x70000000
79#define SHT_HIPROC 0x7FFFFFFF
80#define SHT_LOUSER 0x80000000
81#define SHT_HIUSER 0xFFFFFFFF
82
83// Section flags
84enum ElfSectionFlags
85{
86 SHF_WRITE = 0x1,
87 SHF_ALLOC = 0x2,
88 SHF_EXECINSTR = 0x4,
89 SHF_MASKPROC = 0xF0000000,
90};
91
92// Segment types
93#define PT_NULL 0
94#define PT_LOAD 1
95#define PT_DYNAMIC 2
96#define PT_INTERP 3
97#define PT_NOTE 4
98#define PT_SHLIB 5
99#define PT_PHDR 6
100#define PT_LOPROC 0x70000000
101#define PT_HIPROC 0x7FFFFFFF
102
103typedef unsigned int Elf32_Addr;
104typedef unsigned short Elf32_Half;
105typedef unsigned int Elf32_Off;
106typedef signed int Elf32_Sword;
107typedef unsigned int Elf32_Word;
108
109////////////////////////////////////////////////////////////////////////////////////////////////////
110// ELF file header
111
112struct Elf32_Ehdr {
113 unsigned char e_ident[EI_NIDENT];
114 Elf32_Half e_type;
115 Elf32_Half e_machine;
116 Elf32_Word e_version;
117 Elf32_Addr e_entry;
118 Elf32_Off e_phoff;
119 Elf32_Off e_shoff;
120 Elf32_Word e_flags;
121 Elf32_Half e_ehsize;
122 Elf32_Half e_phentsize;
123 Elf32_Half e_phnum;
124 Elf32_Half e_shentsize;
125 Elf32_Half e_shnum;
126 Elf32_Half e_shstrndx;
127};
128
129// Section header
130struct Elf32_Shdr {
131 Elf32_Word sh_name;
132 Elf32_Word sh_type;
133 Elf32_Word sh_flags;
134 Elf32_Addr sh_addr;
135 Elf32_Off sh_offset;
136 Elf32_Word sh_size;
137 Elf32_Word sh_link;
138 Elf32_Word sh_info;
139 Elf32_Word sh_addralign;
140 Elf32_Word sh_entsize;
141};
142
143// Segment header
144struct Elf32_Phdr {
145 Elf32_Word p_type;
146 Elf32_Off p_offset;
147 Elf32_Addr p_vaddr;
148 Elf32_Addr p_paddr;
149 Elf32_Word p_filesz;
150 Elf32_Word p_memsz;
151 Elf32_Word p_flags;
152 Elf32_Word p_align;
153};
154
155// Symbol table entry
156struct Elf32_Sym {
157 Elf32_Word st_name;
158 Elf32_Addr st_value;
159 Elf32_Word st_size;
160 unsigned char st_info;
161 unsigned char st_other;
162 Elf32_Half st_shndx;
163};
164
165// Relocation entries
166struct Elf32_Rel {
167 Elf32_Addr r_offset;
168 Elf32_Word r_info;
169};
170
171////////////////////////////////////////////////////////////////////////////////////////////////////
172// ElfReader class
173
174typedef int SectionID;
175
176class ElfReader {
177private:
178 char *base;
179 u32 *base32;
180
181 Elf32_Ehdr *header;
182 Elf32_Phdr *segments;
183 Elf32_Shdr *sections;
184
185 u32 *sectionAddrs;
186 bool relocate;
187 u32 entryPoint;
188
189public:
190 ElfReader(void *ptr);
191 ~ElfReader() { }
192
193 u32 Read32(int off) const { return base32[off >> 2]; }
194
195 // Quick accessors
196 ElfType GetType() const { return (ElfType)(header->e_type); }
197 ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); }
198 u32 GetEntryPoint() const { return entryPoint; }
199 u32 GetFlags() const { return (u32)(header->e_flags); }
200 bool LoadInto(u32 vaddr);
201 bool LoadSymbols();
202
203 int GetNumSegments() const { return (int)(header->e_phnum); }
204 int GetNumSections() const { return (int)(header->e_shnum); }
205 const u8 *GetPtr(int offset) const { return (u8*)base + offset; }
206 const char *GetSectionName(int section) const;
207 const u8 *GetSectionDataPtr(int section) const {
208 if (section < 0 || section >= header->e_shnum)
209 return nullptr;
210 if (sections[section].sh_type != SHT_NOBITS)
211 return GetPtr(sections[section].sh_offset);
212 else
213 return nullptr;
214 }
215 bool IsCodeSection(int section) const {
216 return sections[section].sh_type == SHT_PROGBITS;
217 }
218 const u8 *GetSegmentPtr(int segment) {
219 return GetPtr(segments[segment].p_offset);
220 }
221 u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; }
222 int GetSectionSize(SectionID section) const { return sections[section].sh_size; }
223 SectionID GetSectionByName(const char *name, int firstSection = 0) const; //-1 for not found
224
225 bool DidRelocate() {
226 return relocate;
227 }
228};
229
15ElfReader::ElfReader(void *ptr) { 230ElfReader::ElfReader(void *ptr) {
16 base = (char*)ptr; 231 base = (char*)ptr;
17 base32 = (u32 *)ptr; 232 base32 = (u32 *)ptr;
@@ -29,28 +244,25 @@ const char *ElfReader::GetSectionName(int section) const {
29 if (sections[section].sh_type == SHT_NULL) 244 if (sections[section].sh_type == SHT_NULL)
30 return nullptr; 245 return nullptr;
31 246
32 int nameOffset = sections[section].sh_name; 247 int name_offset = sections[section].sh_name;
33 char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); 248 char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx);
34 249
35 if (ptr) 250 if (ptr)
36 return ptr + nameOffset; 251 return ptr + name_offset;
37 else 252
38 return nullptr; 253 return nullptr;
39} 254}
40 255
41bool ElfReader::LoadInto(u32 vaddr) { 256bool ElfReader::LoadInto(u32 vaddr) {
42 DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); 257 DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx);
43 258
44 // Should we relocate? 259 // Should we relocate?
45 bRelocate = (header->e_type != ET_EXEC); 260 relocate = (header->e_type != ET_EXEC);
46 261
47 if (bRelocate) 262 if (relocate) {
48 {
49 DEBUG_LOG(MASTER_LOG, "Relocatable module"); 263 DEBUG_LOG(MASTER_LOG, "Relocatable module");
50 entryPoint += vaddr; 264 entryPoint += vaddr;
51 } 265 } else {
52 else
53 {
54 DEBUG_LOG(MASTER_LOG, "Prerelocated executable"); 266 DEBUG_LOG(MASTER_LOG, "Prerelocated executable");
55 } 267 }
56 268
@@ -58,17 +270,14 @@ bool ElfReader::LoadInto(u32 vaddr) {
58 270
59 // First pass : Get the bits into RAM 271 // First pass : Get the bits into RAM
60 u32 segmentVAddr[32]; 272 u32 segmentVAddr[32];
273 u32 baseAddress = relocate ? vaddr : 0;
61 274
62 u32 baseAddress = bRelocate ? vaddr : 0; 275 for (int i = 0; i < header->e_phnum; i++) {
63
64 for (int i = 0; i < header->e_phnum; i++)
65 {
66 Elf32_Phdr *p = segments + i; 276 Elf32_Phdr *p = segments + i;
67 277
68 INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); 278 INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz);
69 279
70 if (p->p_type == PT_LOAD) 280 if (p->p_type == PT_LOAD) {
71 {
72 segmentVAddr[i] = baseAddress + p->p_vaddr; 281 segmentVAddr[i] = baseAddress + p->p_vaddr;
73 u32 writeAddr = segmentVAddr[i]; 282 u32 writeAddr = segmentVAddr[i];
74 283
@@ -78,27 +287,19 @@ bool ElfReader::LoadInto(u32 vaddr) {
78 u32 dstSize = p->p_memsz; 287 u32 dstSize = p->p_memsz;
79 u32 *s = (u32*)src; 288 u32 *s = (u32*)src;
80 u32 *d = (u32*)dst; 289 u32 *d = (u32*)dst;
81 for (int j = 0; j < (int)(srcSize + 3) / 4; j++) 290 for (int j = 0; j < (int)(srcSize + 3) / 4; j++) {
82 { 291 *d++ = (*s++);
83 *d++ = /*_byteswap_ulong*/(*s++);
84 }
85 if (srcSize < dstSize)
86 {
87 //memset(dst + srcSize, 0, dstSize-srcSize); //zero out bss
88 } 292 }
89 INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); 293 INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz);
90 } 294 }
91 } 295 }
92 296
93
94 INFO_LOG(MASTER_LOG, "Done loading."); 297 INFO_LOG(MASTER_LOG, "Done loading.");
95 return true; 298 return true;
96} 299}
97 300
98SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const 301SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const {
99{ 302 for (int i = firstSection; i < header->e_shnum; i++) {
100 for (int i = firstSection; i < header->e_shnum; i++)
101 {
102 const char *secname = GetSectionName(i); 303 const char *secname = GetSectionName(i);
103 304
104 if (secname != nullptr && strcmp(name, secname) == 0) 305 if (secname != nullptr && strcmp(name, secname) == 0)
@@ -107,25 +308,21 @@ SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const
107 return -1; 308 return -1;
108} 309}
109 310
110bool ElfReader::LoadSymbols() 311bool ElfReader::LoadSymbols() {
111{
112 bool hasSymbols = false; 312 bool hasSymbols = false;
113 SectionID sec = GetSectionByName(".symtab"); 313 SectionID sec = GetSectionByName(".symtab");
114 if (sec != -1) 314 if (sec != -1) {
115 {
116 int stringSection = sections[sec].sh_link; 315 int stringSection = sections[sec].sh_link;
117 const char *stringBase = (const char *)GetSectionDataPtr(stringSection); 316 const char *stringBase = (const char *)GetSectionDataPtr(stringSection);
118 317
119 //We have a symbol table! 318 //We have a symbol table!
120 Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); 319 Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec));
121 int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); 320 int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym);
122 for (int sym = 0; sym < numSymbols; sym++) 321 for (int sym = 0; sym < numSymbols; sym++) {
123 {
124 int size = symtab[sym].st_size; 322 int size = symtab[sym].st_size;
125 if (size == 0) 323 if (size == 0)
126 continue; 324 continue;
127 325
128 // int bind = symtab[sym].st_info >> 4;
129 int type = symtab[sym].st_info & 0xF; 326 int type = symtab[sym].st_info & 0xF;
130 327
131 const char *name = stringBase + symtab[sym].st_name; 328 const char *name = stringBase + symtab[sym].st_name;
@@ -144,42 +341,41 @@ bool ElfReader::LoadSymbols()
144 341
145namespace Loader { 342namespace Loader {
146 343
344/// AppLoader_ELF constructor
345AppLoader_ELF::AppLoader_ELF(std::string& filename) : is_loaded(false) {
346 this->filename = filename;
347}
348
349/// AppLoader_NCCH destructor
350AppLoader_ELF::~AppLoader_ELF() {
351}
352
147/** 353/**
148 * Loads an ELF file 354 * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
149 * @param filename String filename of ELF file
150 * @param error_string Pointer to string to put error message if an error has occurred 355 * @param error_string Pointer to string to put error message if an error has occurred
356 * @todo Move NCSD parsing out of here and create a separate function for loading these
151 * @return True on success, otherwise false 357 * @return True on success, otherwise false
152 */ 358 */
153bool Load_ELF(std::string& filename, std::string* error_string) { 359const ResultStatus AppLoader_ELF::Load() {
154 std::string full_path = filename; 360 INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str());
155 std::string path, file, extension;
156 SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension);
157#if EMU_PLATFORM == PLATFORM_WINDOWS
158 path = ReplaceAll(path, "/", "\\");
159#endif
160 File::IOFile f(filename, "rb");
161 361
162 if (f.IsOpen()) { 362 if (is_loaded)
163 u32 size = (u32)f.GetSize(); 363 return ResultStatus::ErrorAlreadyLoaded;
164 u8* buffer = new u8[size];
165 ElfReader* elf_reader = NULL;
166 364
167 f.ReadBytes(buffer, size); 365 File::IOFile file(filename, "rb");
168 366
169 elf_reader = new ElfReader(buffer); 367 if (file.IsOpen()) {
170 elf_reader->LoadInto(0x00100000); 368 u32 size = (u32)file.GetSize();
369 std::unique_ptr<u8[]> buffer(new u8[size]);
370 file.ReadBytes(&buffer[0], size);
171 371
172 Kernel::LoadExec(elf_reader->GetEntryPoint()); 372 ElfReader elf_reader(&buffer[0]);
173 373 elf_reader.LoadInto(0x00100000);
174 delete[] buffer; 374 Kernel::LoadExec(elf_reader.GetEntryPoint());
175 delete elf_reader;
176 } else { 375 } else {
177 *error_string = "Unable to open ELF file!"; 376 return ResultStatus::Error;
178 return false;
179 } 377 }
180 f.Close(); 378 return ResultStatus::Success;
181
182 return true;
183} 379}
184 380
185} // namespace Loader 381} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index 24d2f91be..3fb010113 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -5,226 +5,28 @@
5#pragma once 5#pragma once
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/loader/loader.h"
8 9
9// ELF Header Constants 10////////////////////////////////////////////////////////////////////////////////////////////////////
10 11// Loader namespace
11// File type
12enum ElfType {
13 ET_NONE = 0,
14 ET_REL = 1,
15 ET_EXEC = 2,
16 ET_DYN = 3,
17 ET_CORE = 4,
18 ET_LOPROC = 0xFF00,
19 ET_HIPROC = 0xFFFF,
20};
21
22// Machine/Architecture
23enum ElfMachine {
24 EM_NONE = 0,
25 EM_M32 = 1,
26 EM_SPARC = 2,
27 EM_386 = 3,
28 EM_68K = 4,
29 EM_88K = 5,
30 EM_860 = 7,
31 EM_MIPS = 8
32};
33
34// File version
35#define EV_NONE 0
36#define EV_CURRENT 1
37
38// Identification index
39#define EI_MAG0 0
40#define EI_MAG1 1
41#define EI_MAG2 2
42#define EI_MAG3 3
43#define EI_CLASS 4
44#define EI_DATA 5
45#define EI_VERSION 6
46#define EI_PAD 7
47#define EI_NIDENT 16
48
49// Magic number
50#define ELFMAG0 0x7F
51#define ELFMAG1 'E'
52#define ELFMAG2 'L'
53#define ELFMAG3 'F'
54
55// Sections constants
56
57// Section types
58#define SHT_NULL 0
59#define SHT_PROGBITS 1
60#define SHT_SYMTAB 2
61#define SHT_STRTAB 3
62#define SHT_RELA 4
63#define SHT_HASH 5
64#define SHT_DYNAMIC 6
65#define SHT_NOTE 7
66#define SHT_NOBITS 8
67#define SHT_REL 9
68#define SHT_SHLIB 10
69#define SHT_DYNSYM 11
70#define SHT_LOPROC 0x70000000
71#define SHT_HIPROC 0x7FFFFFFF
72#define SHT_LOUSER 0x80000000
73#define SHT_HIUSER 0xFFFFFFFF
74
75// Section flags
76enum ElfSectionFlags
77{
78 SHF_WRITE = 0x1,
79 SHF_ALLOC = 0x2,
80 SHF_EXECINSTR = 0x4,
81 SHF_MASKPROC = 0xF0000000,
82};
83
84// Segment types
85#define PT_NULL 0
86#define PT_LOAD 1
87#define PT_DYNAMIC 2
88#define PT_INTERP 3
89#define PT_NOTE 4
90#define PT_SHLIB 5
91#define PT_PHDR 6
92#define PT_LOPROC 0x70000000
93#define PT_HIPROC 0x7FFFFFFF
94
95typedef unsigned int Elf32_Addr;
96typedef unsigned short Elf32_Half;
97typedef unsigned int Elf32_Off;
98typedef signed int Elf32_Sword;
99typedef unsigned int Elf32_Word;
100
101// ELF file header
102struct Elf32_Ehdr {
103 unsigned char e_ident[EI_NIDENT];
104 Elf32_Half e_type;
105 Elf32_Half e_machine;
106 Elf32_Word e_version;
107 Elf32_Addr e_entry;
108 Elf32_Off e_phoff;
109 Elf32_Off e_shoff;
110 Elf32_Word e_flags;
111 Elf32_Half e_ehsize;
112 Elf32_Half e_phentsize;
113 Elf32_Half e_phnum;
114 Elf32_Half e_shentsize;
115 Elf32_Half e_shnum;
116 Elf32_Half e_shstrndx;
117};
118
119// Section header
120struct Elf32_Shdr {
121 Elf32_Word sh_name;
122 Elf32_Word sh_type;
123 Elf32_Word sh_flags;
124 Elf32_Addr sh_addr;
125 Elf32_Off sh_offset;
126 Elf32_Word sh_size;
127 Elf32_Word sh_link;
128 Elf32_Word sh_info;
129 Elf32_Word sh_addralign;
130 Elf32_Word sh_entsize;
131};
132
133// Segment header
134struct Elf32_Phdr {
135 Elf32_Word p_type;
136 Elf32_Off p_offset;
137 Elf32_Addr p_vaddr;
138 Elf32_Addr p_paddr;
139 Elf32_Word p_filesz;
140 Elf32_Word p_memsz;
141 Elf32_Word p_flags;
142 Elf32_Word p_align;
143};
144
145// Symbol table entry
146struct Elf32_Sym {
147 Elf32_Word st_name;
148 Elf32_Addr st_value;
149 Elf32_Word st_size;
150 unsigned char st_info;
151 unsigned char st_other;
152 Elf32_Half st_shndx;
153};
154
155// Relocation entries
156struct Elf32_Rel {
157 Elf32_Addr r_offset;
158 Elf32_Word r_info;
159};
160
161typedef int SectionID;
162
163class ElfReader {
164private:
165 char *base;
166 u32 *base32;
167
168 Elf32_Ehdr *header;
169 Elf32_Phdr *segments;
170 Elf32_Shdr *sections;
171 12
172 u32 *sectionAddrs; 13namespace Loader {
173 bool bRelocate;
174 u32 entryPoint;
175 14
15/// Loads an ELF/AXF file
16class AppLoader_ELF : public AppLoader {
176public: 17public:
177 ElfReader(void *ptr); 18 AppLoader_ELF(std::string& filename);
178 ~ElfReader() { } 19 ~AppLoader_ELF();
179 20
180 u32 Read32(int off) const { return base32[off >> 2]; } 21 /**
22 * Load the bootable file
23 * @return ResultStatus result of function
24 */
25 const ResultStatus Load();
181 26
182 // Quick accessors 27private:
183 ElfType GetType() const { return (ElfType)(header->e_type); } 28 std::string filename;
184 ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } 29 bool is_loaded;
185 u32 GetEntryPoint() const { return entryPoint; }
186 u32 GetFlags() const { return (u32)(header->e_flags); }
187 bool LoadInto(u32 vaddr);
188 bool LoadSymbols();
189
190 int GetNumSegments() const { return (int)(header->e_phnum); }
191 int GetNumSections() const { return (int)(header->e_shnum); }
192 const u8 *GetPtr(int offset) const { return (u8*)base + offset; }
193 const char *GetSectionName(int section) const;
194 const u8 *GetSectionDataPtr(int section) const {
195 if (section < 0 || section >= header->e_shnum)
196 return nullptr;
197 if (sections[section].sh_type != SHT_NOBITS)
198 return GetPtr(sections[section].sh_offset);
199 else
200 return nullptr;
201 }
202 bool IsCodeSection(int section) const {
203 return sections[section].sh_type == SHT_PROGBITS;
204 }
205 const u8 *GetSegmentPtr(int segment) {
206 return GetPtr(segments[segment].p_offset);
207 }
208 u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; }
209 int GetSectionSize(SectionID section) const { return sections[section].sh_size; }
210 SectionID GetSectionByName(const char *name, int firstSection = 0) const; //-1 for not found
211
212 bool DidRelocate() {
213 return bRelocate;
214 }
215}; 30};
216 31
217////////////////////////////////////////////////////////////////////////////////////////////////////
218// Loader namespace
219
220namespace Loader {
221
222/**
223 * Loads an ELF file
224 * @param filename String filename of ELF file
225 * @param error_string Pointer to string to put error message if an error has occurred
226 * @return True on success, otherwise false
227 */
228bool Load_ELF(std::string& filename, std::string* error_string);
229
230} // namespace Loader 32} // namespace Loader
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 1a647d8a5..dd0863ff3 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory>
6
5#include "core/loader/loader.h" 7#include "core/loader/loader.h"
6#include "core/loader/elf.h" 8#include "core/loader/elf.h"
7#include "core/loader/ncch.h" 9#include "core/loader/ncch.h"
@@ -16,59 +18,60 @@ namespace Loader {
16 * @todo (ShizZy) this function sucks... make it actually check file contents etc. 18 * @todo (ShizZy) this function sucks... make it actually check file contents etc.
17 * @return FileType of file 19 * @return FileType of file
18 */ 20 */
19FileType IdentifyFile(std::string &filename) { 21const FileType IdentifyFile(const std::string &filename) {
20 if (filename.size() == 0) { 22 if (filename.size() == 0) {
21 ERROR_LOG(LOADER, "invalid filename %s", filename.c_str()); 23 ERROR_LOG(LOADER, "invalid filename %s", filename.c_str());
22 return FILETYPE_ERROR; 24 return FileType::Error;
23 } 25 }
24 std::string extension = filename.size() >= 5 ? filename.substr(filename.size() - 4) : ""; 26 std::string extension = filename.size() >= 5 ? filename.substr(filename.size() - 4) : "";
25 27
26 if (!strcasecmp(extension.c_str(), ".elf")) { 28 if (!strcasecmp(extension.c_str(), ".elf")) {
27 return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p 29 return FileType::ELF; // TODO(bunnei): Do some filetype checking :p
28 } 30 }
29 else if (!strcasecmp(extension.c_str(), ".axf")) { 31 else if (!strcasecmp(extension.c_str(), ".axf")) {
30 return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p 32 return FileType::ELF; // TODO(bunnei): Do some filetype checking :p
31 } 33 }
32 else if (!strcasecmp(extension.c_str(), ".cxi")) { 34 else if (!strcasecmp(extension.c_str(), ".cxi")) {
33 return FILETYPE_CTR_CXI; // TODO(bunnei): Do some filetype checking :p 35 return FileType::CXI; // TODO(bunnei): Do some filetype checking :p
34 } 36 }
35 else if (!strcasecmp(extension.c_str(), ".cci")) { 37 else if (!strcasecmp(extension.c_str(), ".cci")) {
36 return FILETYPE_CTR_CCI; // TODO(bunnei): Do some filetype checking :p 38 return FileType::CCI; // TODO(bunnei): Do some filetype checking :p
37 } 39 }
38 return FILETYPE_UNKNOWN; 40 return FileType::Unknown;
39} 41}
40 42
41/** 43/**
42 * Identifies and loads a bootable file 44 * Identifies and loads a bootable file
43 * @param filename String filename of bootable file 45 * @param filename String filename of bootable file
44 * @param error_string Point to string to put error message if an error has occurred 46 * @return ResultStatus result of function
45 * @return True on success, otherwise false
46 */ 47 */
47bool LoadFile(std::string &filename, std::string *error_string) { 48const ResultStatus LoadFile(std::string& filename) {
48 INFO_LOG(LOADER, "Identifying file..."); 49 INFO_LOG(LOADER, "Loading file %s...", filename.c_str());
49 50
50 // Note that this can modify filename!
51 switch (IdentifyFile(filename)) { 51 switch (IdentifyFile(filename)) {
52 52
53 case FILETYPE_CTR_ELF: 53 // Standard ELF file format...
54 return Loader::Load_ELF(filename, error_string); 54 case FileType::ELF: {
55 return AppLoader_ELF(filename).Load();
56 }
55 57
56 case FILETYPE_CTR_CXI: 58 // NCCH/NCSD container formats...
57 case FILETYPE_CTR_CCI: 59 case FileType::CXI:
58 return Loader::Load_NCCH(filename, error_string); 60 case FileType::CCI: {
61 return AppLoader_NCCH(filename).Load();
62 }
59 63
60 case FILETYPE_ERROR: 64 // Error occurred durring IdentifyFile...
61 ERROR_LOG(LOADER, "Could not read file"); 65 case FileType::Error:
62 *error_string = "Error reading file"; 66
63 break; 67 // IdentifyFile could know identify file type...
68 case FileType::Unknown:
64 69
65 case FILETYPE_UNKNOWN:
66 default: 70 default:
67 ERROR_LOG(LOADER, "Failed to identify file"); 71 return ResultStatus::ErrorInvalidFormat;
68 *error_string = " Failed to identify file";
69 break;
70 } 72 }
71 return false; 73
74 return ResultStatus::Error;
72} 75}
73 76
74} // namespace Loader 77} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 979003553..42caa29e6 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -4,6 +4,8 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <vector>
8
7#include "common/common.h" 9#include "common/common.h"
8 10
9//////////////////////////////////////////////////////////////////////////////////////////////////// 11////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -11,16 +13,94 @@
11 13
12namespace Loader { 14namespace Loader {
13 15
14enum FileType { 16/// File types supported by CTR
15 FILETYPE_ERROR, 17enum class FileType {
18 Error,
19 Unknown,
20 CCI,
21 CXI,
22 CIA,
23 ELF,
24};
25
26/// Return type for functions in Loader namespace
27enum class ResultStatus {
28 Success,
29 Error,
30 ErrorInvalidFormat,
31 ErrorNotImplemented,
32 ErrorNotLoaded,
33 ErrorAlreadyLoaded,
34};
35
36/// Interface for loading an application
37class AppLoader : NonCopyable {
38public:
39 AppLoader() { }
40 virtual ~AppLoader() { }
41
42 /**
43 * Load the application
44 * @return ResultStatus result of function
45 */
46 virtual const ResultStatus Load() = 0;
47
48 /**
49 * Get the code (typically .code section) of the application
50 * @param error ResultStatus result of function
51 * @return Reference to code buffer
52 */
53 virtual const std::vector<u8>& GetCode(ResultStatus& error) const {
54 error = ResultStatus::ErrorNotImplemented;
55 return code;
56 }
57
58 /**
59 * Get the icon (typically .icon section) of the application
60 * @param error ResultStatus result of function
61 * @return Reference to icon buffer
62 */
63 virtual const std::vector<u8>& GetIcon(ResultStatus& error) const {
64 error = ResultStatus::ErrorNotImplemented;
65 return icon;
66 }
67
68 /**
69 * Get the banner (typically .banner section) of the application
70 * @param error ResultStatus result of function
71 * @return Reference to banner buffer
72 */
73 virtual const std::vector<u8>& GetBanner(ResultStatus& error) const {
74 error = ResultStatus::ErrorNotImplemented;
75 return banner;
76 }
77
78 /**
79 * Get the logo (typically .logo section) of the application
80 * @param error ResultStatus result of function
81 * @return Reference to logo buffer
82 */
83 virtual const std::vector<u8>& GetLogo(ResultStatus& error) const {
84 error = ResultStatus::ErrorNotImplemented;
85 return logo;
86 }
16 87
17 FILETYPE_CTR_CCI, 88 /**
18 FILETYPE_CTR_CIA, 89 * Get the RomFs archive of the application
19 FILETYPE_CTR_CXI, 90 * @param error ResultStatus result of function
20 FILETYPE_CTR_ELF, 91 * @return Reference to RomFs archive buffer
21 FILETYPE_CTR_BIN, 92 */
93 virtual const std::vector<u8>& GetRomFs(ResultStatus error) const {
94 error = ResultStatus::ErrorNotImplemented;
95 return romfs;
96 }
22 97
23 FILETYPE_UNKNOWN 98protected:
99 std::vector<u8> code; ///< ExeFS .code section
100 std::vector<u8> icon; ///< ExeFS .icon section
101 std::vector<u8> banner; ///< ExeFS .banner section
102 std::vector<u8> logo; ///< ExeFS .logo section
103 std::vector<u8> romfs; ///< RomFs archive
24}; 104};
25 105
26/** 106/**
@@ -28,14 +108,13 @@ enum FileType {
28 * @param filename String filename of bootable file 108 * @param filename String filename of bootable file
29 * @return FileType of file 109 * @return FileType of file
30 */ 110 */
31FileType IdentifyFile(std::string &filename); 111const FileType IdentifyFile(const std::string &filename);
32 112
33/** 113/**
34 * Identifies and loads a bootable file 114 * Identifies and loads a bootable file
35 * @param filename String filename of bootable file 115 * @param filename String filename of bootable file
36 * @param error_string Point to string to put error message if an error has occurred 116 * @return ResultStatus result of function
37 * @return True on success, otherwise false
38 */ 117 */
39bool LoadFile(std::string &filename, std::string *error_string); 118const ResultStatus LoadFile(std::string& filename);
40 119
41} // namespace 120} // namespace
diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp
index 23864d262..765efcf65 100644
--- a/src/core/loader/ncch.cpp
+++ b/src/core/loader/ncch.cpp
@@ -2,6 +2,8 @@
2// Licensed under GPLv2 2// Licensed under GPLv2
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <memory>
6
5#include "common/file_util.h" 7#include "common/file_util.h"
6 8
7#include "core/loader/ncch.h" 9#include "core/loader/ncch.h"
@@ -9,136 +11,6 @@
9#include "core/mem_map.h" 11#include "core/mem_map.h"
10 12
11//////////////////////////////////////////////////////////////////////////////////////////////////// 13////////////////////////////////////////////////////////////////////////////////////////////////////
12/// NCCH header (Note: "NCCH" appears to be a publically unknown acronym)
13
14struct NCCH_Header {
15 u8 signature[0x100];
16 char magic[4];
17 u32 content_size;
18 u8 partition_id[8];
19 u16 maker_code;
20 u16 version;
21 u8 reserved_0[4];
22 u8 program_id[8];
23 u8 temp_flag;
24 u8 reserved_1[0x2f];
25 u8 product_code[0x10];
26 u8 extended_header_hash[0x20];
27 u32 extended_header_size;
28 u8 reserved_2[4];
29 u8 flags[8];
30 u32 plain_region_offset;
31 u32 plain_region_size;
32 u8 reserved_3[8];
33 u32 exefs_offset;
34 u32 exefs_size;
35 u32 exefs_hash_region_size;
36 u8 reserved_4[4];
37 u32 romfs_offset;
38 u32 romfs_size;
39 u32 romfs_hash_region_size;
40 u8 reserved_5[4];
41 u8 exefs_super_block_hash[0x20];
42 u8 romfs_super_block_hash[0x20];
43};
44
45////////////////////////////////////////////////////////////////////////////////////////////////////
46// ExeFS (executable file system) headers
47
48typedef struct {
49 char name[8];
50 u32 offset;
51 u32 size;
52} ExeFs_SectionHeader;
53
54typedef struct {
55 ExeFs_SectionHeader section[8];
56 u8 reserved[0x80];
57 u8 hashes[8][0x20];
58} ExeFs_Header;
59
60////////////////////////////////////////////////////////////////////////////////////////////////////
61// ExHeader (executable file system header) headers
62
63struct ExHeader_SystemInfoFlags{
64 u8 reserved[5];
65 u8 flag;
66 u8 remaster_version[2];
67} exheader_systeminfoflags;
68
69struct ExHeader_CodeSegmentInfo{
70 u32 address;
71 u32 num_max_pages;
72 u32 code_size;
73} exheader_codesegmentinfo;
74
75struct ExHeader_CodeSetInfo {
76 u8 name[8];
77 ExHeader_SystemInfoFlags flags;
78 ExHeader_CodeSegmentInfo text;
79 u8 stacksize[4];
80 ExHeader_CodeSegmentInfo ro;
81 u8 reserved[4];
82 ExHeader_CodeSegmentInfo data;
83 u8 bsssize[4];
84};
85
86struct ExHeader_DependencyList{
87 u8 program_id[0x30][8];
88};
89
90struct ExHeader_SystemInfo{
91 u32 save_data_size;
92 u8 reserved[4];
93 u8 jump_id[8];
94 u8 reserved_2[0x30];
95};
96
97struct ExHeader_StorageInfo{
98 u8 ext_save_data_id[8];
99 u8 system_save_data_id[8];
100 u8 reserved[8];
101 u8 access_info[7];
102 u8 other_attributes;
103};
104
105struct ExHeader_ARM11_SystemLocalCaps{
106 u8 program_id[8];
107 u8 flags[8];
108 u8 resource_limit_descriptor[0x10][2];
109 ExHeader_StorageInfo storage_info;
110 u8 service_access_control[0x20][8];
111 u8 reserved[0x1f];
112 u8 resource_limit_category;
113};
114
115struct ExHeader_ARM11_KernelCaps{
116 u8 descriptors[28][4];
117 u8 reserved[0x10];
118};
119
120struct ExHeader_ARM9_AccessControl{
121 u8 descriptors[15];
122 u8 descversion;
123};
124
125struct ExHeader_Header{
126 ExHeader_CodeSetInfo codeset_info;
127 ExHeader_DependencyList dependency_list;
128 ExHeader_SystemInfo system_info;
129 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
130 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
131 ExHeader_ARM9_AccessControl arm9_access_control;
132 struct {
133 u8 signature[0x100];
134 u8 ncch_public_key_modulus[0x100];
135 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
136 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
137 ExHeader_ARM9_AccessControl arm9_access_control;
138 } access_desc;
139};
140
141////////////////////////////////////////////////////////////////////////////////////////////////////
142// Loader namespace 14// Loader namespace
143 15
144namespace Loader { 16namespace Loader {
@@ -163,11 +35,9 @@ u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) {
163 * @param compressed_size Size of compressed buffer 35 * @param compressed_size Size of compressed buffer
164 * @param decompressed Decompressed buffer 36 * @param decompressed Decompressed buffer
165 * @param decompressed_size Size of decompressed buffer 37 * @param decompressed_size Size of decompressed buffer
166 * @param error_string String populated with error message on failure
167 * @return True on success, otherwise false 38 * @return True on success, otherwise false
168 */ 39 */
169bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size, 40bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) {
170 std::string* error_string) {
171 u8* footer = compressed + compressed_size - 8; 41 u8* footer = compressed + compressed_size - 8;
172 u32 buffer_top_and_bottom = *(u32*)footer; 42 u32 buffer_top_and_bottom = *(u32*)footer;
173 u32 i, j; 43 u32 i, j;
@@ -191,8 +61,8 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32
191 break; 61 break;
192 62
193 if(control & 0x80) { 63 if(control & 0x80) {
64 // Check if compression is out of bounds
194 if(index < 2) { 65 if(index < 2) {
195 *error_string = "Compression out of bounds";
196 return false; 66 return false;
197 } 67 }
198 index -= 2; 68 index -= 2;
@@ -202,22 +72,22 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32
202 segment_offset &= 0x0FFF; 72 segment_offset &= 0x0FFF;
203 segment_offset += 2; 73 segment_offset += 2;
204 74
75 // Check if compression is out of bounds
205 if(out < segment_size) { 76 if(out < segment_size) {
206 *error_string = "Compression out of bounds";
207 return false; 77 return false;
208 } 78 }
209 for(j = 0; j < segment_size; j++) { 79 for(j = 0; j < segment_size; j++) {
210 u8 data; 80 u8 data;
81 // Check if compression is out of bounds
211 if(out + segment_offset >= decompressed_size) { 82 if(out + segment_offset >= decompressed_size) {
212 *error_string = "Compression out of bounds";
213 return false; 83 return false;
214 } 84 }
215 data = decompressed[out + segment_offset]; 85 data = decompressed[out + segment_offset];
216 decompressed[--out] = data; 86 decompressed[--out] = data;
217 } 87 }
218 } else { 88 } else {
89 // Check if compression is out of bounds
219 if(out < 1) { 90 if(out < 1) {
220 *error_string = "Compression out of bounds";
221 return false; 91 return false;
222 } 92 }
223 decompressed[--out] = compressed[--index]; 93 decompressed[--out] = compressed[--index];
@@ -228,34 +98,96 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32
228 return true; 98 return true;
229} 99}
230 100
101////////////////////////////////////////////////////////////////////////////////////////////////////
102// AppLoader_NCCH class
103
104/// AppLoader_NCCH constructor
105AppLoader_NCCH::AppLoader_NCCH(std::string& filename) {
106 this->filename = filename;
107 is_loaded = false;
108 is_compressed = false;
109 entry_point = 0;
110 ncch_offset = 0;
111 exefs_offset = 0;
112}
113
114/// AppLoader_NCCH destructor
115AppLoader_NCCH::~AppLoader_NCCH() {
116}
117
231/** 118/**
232 * Load a data buffer into memory at the specified address 119 * Loads .code section into memory for booting
233 * @param addr Address to load memory into 120 * @return ResultStatus result of function
234 * @param buffer Buffer of data to load into memory
235 * @param size Size of data to load into memory
236 * @todo Perhaps move this code somewhere more generic?
237 */ 121 */
238void LoadBuffer(const u32 addr, const u8* const buffer, const int size) { 122const ResultStatus AppLoader_NCCH::LoadExec() const {
239 u32 *dst = (u32*)Memory::GetPointer(addr); 123 if (!is_loaded)
240 u32 *src = (u32*)buffer; 124 return ResultStatus::ErrorNotLoaded;
241 int size_aligned = (size + 3) / 4;
242 125
243 for (int j = 0; j < size_aligned; j++) { 126 for (std::vector<u8>::size_type i = 0; i != code.size(); i++) {
244 *dst++ = (*src++); 127 Memory::Write8(entry_point + i, code[i]);
245 } 128 }
246 return; 129 Kernel::LoadExec(entry_point);
130
131 return ResultStatus::Success;
247} 132}
248 133
249/** 134/**
135 * Reads an application section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
136 * @param file Handle to file to read from
137 * @param name Name of section to read out of NCCH file
138 * @param buffer Buffer to read section into.
139 */
140const ResultStatus AppLoader_NCCH::LoadSection(File::IOFile& file, const char* name,
141 std::vector<u8>& buffer) {
142 // Iterate through the ExeFs archive until we find the .code file...
143 for (int i = 0; i < kExeFs_MaxSections; i++) {
144 INFO_LOG(LOADER, "ExeFS section %d:", i);
145 INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name);
146 INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset);
147 INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size);
148
149 // Load the .code section (executable code)...
150 if (strcmp((const char*)exefs_header.section[i].name, name) == 0) {
151 s64 section_offset = (exefs_header.section[i].offset + exefs_offset +
152 sizeof(ExeFs_Header) + ncch_offset);
153 file.Seek(section_offset, 0);
154
155 // Section is compressed...
156 if (i == 0 && is_compressed) {
157 // Read compressed .code section...
158 std::unique_ptr<u8[]> temp_buffer(new u8[exefs_header.section[i].size]);
159 file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size);
160
161 // Decompress .code section...
162 u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], exefs_header.section[i].size);
163 buffer.resize(decompressed_size);
164 if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0],
165 decompressed_size)) {
166 return ResultStatus::ErrorInvalidFormat;
167 }
168 // Section is uncompressed...
169 } else {
170 buffer.resize(exefs_header.section[i].size);
171 file.ReadBytes(&buffer[0], exefs_header.section[i].size);
172 }
173 return ResultStatus::Success;
174 }
175 }
176 return ResultStatus::Error;
177}
178
179/**
250 * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) 180 * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
251 * @param filename String filename of NCCH file
252 * @param error_string Pointer to string to put error message if an error has occurred 181 * @param error_string Pointer to string to put error message if an error has occurred
253 * @todo Move NCSD parsing out of here and create a separate function for loading these 182 * @todo Move NCSD parsing out of here and create a separate function for loading these
254 * @return True on success, otherwise false 183 * @return True on success, otherwise false
255 */ 184 */
256bool Load_NCCH(std::string& filename, std::string* error_string) { 185const ResultStatus AppLoader_NCCH::Load() {
257 INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); 186 INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str());
258 187
188 if (is_loaded)
189 return ResultStatus::ErrorAlreadyLoaded;
190
259 File::IOFile file(filename, "rb"); 191 File::IOFile file(filename, "rb");
260 192
261 if (file.IsOpen()) { 193 if (file.IsOpen()) {
@@ -263,80 +195,50 @@ bool Load_NCCH(std::string& filename, std::string* error_string) {
263 file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); 195 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
264 196
265 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... 197 // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)...
266 int ncch_off = 0; // Offset to NCCH header, can be 0 or after NCSD header 198 if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) {
267 if (memcmp(&ncch_header.magic, "NCSD", 4) == 0) {
268 WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!"); 199 WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!");
269 ncch_off = 0x4000; 200 ncch_offset = 0x4000;
270 file.Seek(ncch_off, 0); 201 file.Seek(ncch_offset, 0);
271 file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); 202 file.ReadBytes(&ncch_header, sizeof(NCCH_Header));
272 } 203 }
204
273 // Verify we are loading the correct file type... 205 // Verify we are loading the correct file type...
274 if (memcmp(&ncch_header.magic, "NCCH", 4) != 0) { 206 if (0 != memcmp(&ncch_header.magic, "NCCH", 4))
275 *error_string = "Invalid NCCH magic number (likely incorrect file type)"; 207 return ResultStatus::ErrorInvalidFormat;
276 return false; 208
277 }
278 // Read ExHeader 209 // Read ExHeader
279 ExHeader_Header exheader_header;
280 file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); 210 file.ReadBytes(&exheader_header, sizeof(ExHeader_Header));
281 211
282 bool is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; 212 is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1;
213 entry_point = exheader_header.codeset_info.text.address;
283 214
284 INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name); 215 INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name);
285 INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no"); 216 INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no");
217 INFO_LOG(LOADER, "Entry point: 0x%08X", entry_point);
286 218
287 // Read ExeFS 219 // Read ExeFS
288 u32 exefs_offset = ncch_header.exefs_offset * kExeFs_BlockSize; 220 exefs_offset = ncch_header.exefs_offset * kExeFs_BlockSize;
289 u32 exefs_size = ncch_header.exefs_size * kExeFs_BlockSize; 221 u32 exefs_size = ncch_header.exefs_size * kExeFs_BlockSize;
290 222
291 INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset); 223 INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset);
292 INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size); 224 INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size);
293 225
294 ExeFs_Header exefs_header; 226 file.Seek(exefs_offset + ncch_offset, 0);
295 file.Seek(exefs_offset + ncch_off, 0);
296 file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); 227 file.ReadBytes(&exefs_header, sizeof(ExeFs_Header));
297 228
298 // Iterate through the ExeFs archive until we find the .code file... 229 // TODO(bunnei): Check ResultStatus here...
299 for (int i = 0; i < kExeFs_MaxSections; i++) { 230 LoadSection(file, ".code", code);
300 INFO_LOG(LOADER, "ExeFS section %d:", i); 231 LoadSection(file, ".icon", icon);
301 INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); 232 LoadSection(file, ".banner", banner);
302 INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); 233 LoadSection(file, ".logo", logo);
303 INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size);
304
305 // Load the .code section (executable code)...
306 if (strcmp((char*) exefs_header.section[i].name, ".code") == 0) {
307 file.Seek(exefs_header.section[i].offset + exefs_offset + sizeof(ExeFs_Header) +
308 ncch_off, 0);
309
310 u8* buffer = new u8[exefs_header.section[i].size];
311 file.ReadBytes(buffer, exefs_header.section[i].size);
312
313 // Load compressed executable...
314 if (i == 0 && is_compressed) {
315 u32 decompressed_size = LZSS_GetDecompressedSize(buffer,
316 exefs_header.section[i].size);
317
318 if (!LZSS_Decompress(buffer, exefs_header.section[i].size,
319 Memory::GetPointer(exheader_header.codeset_info.text.address),
320 decompressed_size, error_string)) {
321 return false;
322 }
323 // Load uncompressed executable...
324 } else {
325 // Load .code section into memory...
326 LoadBuffer(exheader_header.codeset_info.text.address, buffer,
327 exefs_header.section[i].size);
328 }
329 delete[] buffer;
330 234
331 // Setup kernel emulation to boot .code section... 235 is_loaded = true; // Set state to loaded
332 Kernel::LoadExec(exheader_header.codeset_info.text.address);
333 236
334 // No need to load the other files from ExeFS until we do something with them... 237 LoadExec(); // Load the executable into memory for booting
335 return true; 238
336 } 239 return ResultStatus::Success;
337 }
338 } 240 }
339 return false; 241 return ResultStatus::Error;
340} 242}
341 243
342} // namespace Loader 244} // namespace Loader
diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h
index 778e8b456..3aae5417c 100644
--- a/src/core/loader/ncch.h
+++ b/src/core/loader/ncch.h
@@ -5,17 +5,184 @@
5#pragma once 5#pragma once
6 6
7#include "common/common.h" 7#include "common/common.h"
8#include "common/file_util.h"
9
10#include "core/loader/loader.h"
11
12////////////////////////////////////////////////////////////////////////////////////////////////////
13/// NCCH header (Note: "NCCH" appears to be a publically unknown acronym)
14
15struct NCCH_Header {
16 u8 signature[0x100];
17 char magic[4];
18 u32 content_size;
19 u8 partition_id[8];
20 u16 maker_code;
21 u16 version;
22 u8 reserved_0[4];
23 u8 program_id[8];
24 u8 temp_flag;
25 u8 reserved_1[0x2f];
26 u8 product_code[0x10];
27 u8 extended_header_hash[0x20];
28 u32 extended_header_size;
29 u8 reserved_2[4];
30 u8 flags[8];
31 u32 plain_region_offset;
32 u32 plain_region_size;
33 u8 reserved_3[8];
34 u32 exefs_offset;
35 u32 exefs_size;
36 u32 exefs_hash_region_size;
37 u8 reserved_4[4];
38 u32 romfs_offset;
39 u32 romfs_size;
40 u32 romfs_hash_region_size;
41 u8 reserved_5[4];
42 u8 exefs_super_block_hash[0x20];
43 u8 romfs_super_block_hash[0x20];
44};
45
46////////////////////////////////////////////////////////////////////////////////////////////////////
47// ExeFS (executable file system) headers
48
49typedef struct {
50 char name[8];
51 u32 offset;
52 u32 size;
53} ExeFs_SectionHeader;
54
55typedef struct {
56 ExeFs_SectionHeader section[8];
57 u8 reserved[0x80];
58 u8 hashes[8][0x20];
59} ExeFs_Header;
60
61////////////////////////////////////////////////////////////////////////////////////////////////////
62// ExHeader (executable file system header) headers
63
64struct ExHeader_SystemInfoFlags{
65 u8 reserved[5];
66 u8 flag;
67 u8 remaster_version[2];
68};
69
70struct ExHeader_CodeSegmentInfo{
71 u32 address;
72 u32 num_max_pages;
73 u32 code_size;
74};
75
76struct ExHeader_CodeSetInfo {
77 u8 name[8];
78 ExHeader_SystemInfoFlags flags;
79 ExHeader_CodeSegmentInfo text;
80 u8 stacksize[4];
81 ExHeader_CodeSegmentInfo ro;
82 u8 reserved[4];
83 ExHeader_CodeSegmentInfo data;
84 u8 bsssize[4];
85};
86
87struct ExHeader_DependencyList{
88 u8 program_id[0x30][8];
89};
90
91struct ExHeader_SystemInfo{
92 u32 save_data_size;
93 u8 reserved[4];
94 u8 jump_id[8];
95 u8 reserved_2[0x30];
96};
97
98struct ExHeader_StorageInfo{
99 u8 ext_save_data_id[8];
100 u8 system_save_data_id[8];
101 u8 reserved[8];
102 u8 access_info[7];
103 u8 other_attributes;
104};
105
106struct ExHeader_ARM11_SystemLocalCaps{
107 u8 program_id[8];
108 u8 flags[8];
109 u8 resource_limit_descriptor[0x10][2];
110 ExHeader_StorageInfo storage_info;
111 u8 service_access_control[0x20][8];
112 u8 reserved[0x1f];
113 u8 resource_limit_category;
114};
115
116struct ExHeader_ARM11_KernelCaps{
117 u8 descriptors[28][4];
118 u8 reserved[0x10];
119};
120
121struct ExHeader_ARM9_AccessControl{
122 u8 descriptors[15];
123 u8 descversion;
124};
125
126struct ExHeader_Header{
127 ExHeader_CodeSetInfo codeset_info;
128 ExHeader_DependencyList dependency_list;
129 ExHeader_SystemInfo system_info;
130 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
131 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
132 ExHeader_ARM9_AccessControl arm9_access_control;
133 struct {
134 u8 signature[0x100];
135 u8 ncch_public_key_modulus[0x100];
136 ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps;
137 ExHeader_ARM11_KernelCaps arm11_kernel_caps;
138 ExHeader_ARM9_AccessControl arm9_access_control;
139 } access_desc;
140};
8 141
9//////////////////////////////////////////////////////////////////////////////////////////////////// 142////////////////////////////////////////////////////////////////////////////////////////////////////
143// Loader namespace
10 144
11namespace Loader { 145namespace Loader {
12 146
13/** 147/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI)
14 * Loads an NCCH file (e.g. from a CCI or CXI) 148class AppLoader_NCCH : public AppLoader {
15 * @param filename String filename of NCCH file 149public:
16 * @param error_string Pointer to string to put error message if an error has occurred 150 AppLoader_NCCH(std::string& filename);
17 * @return True on success, otherwise false 151 ~AppLoader_NCCH();
18 */ 152
19bool Load_NCCH(std::string& filename, std::string* error_string); 153 /**
154 * Load the application
155 * @return ResultStatus result of function
156 */
157 const ResultStatus Load();
158
159private:
160
161 /**
162 * Reads an application section of an NCCH file into AppLoader (e.g. .code, .logo, etc.)
163 * @param file Handle to file to read from
164 * @param name Name of section to read out of NCCH file
165 * @param buffer Buffer to read section into.
166 */
167 const ResultStatus LoadSection(File::IOFile& file, const char* name,
168 std::vector<u8>& buffer);
169
170 /**
171 * Loads .code section into memory for booting
172 * @return ResultStatus result of function
173 */
174 const ResultStatus LoadExec() const;
175
176 std::string filename;
177 bool is_loaded;
178 bool is_compressed;
179 u32 entry_point;
180
181 u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header
182 u32 exefs_offset;
183
184 ExeFs_Header exefs_header;
185 ExHeader_Header exheader_header;
186};
20 187
21} // namespace Loader 188} // namespace Loader