From 10ae45b5e7b4d66660803cc5dfe26aa6e1b8843f Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 16 Jun 2014 17:51:04 -0400 Subject: [PATCH 01/15] Loader: Removed unused CXI and DAT loading code. --- src/core/loader.cpp | 76 --------------------------------------------- 1 file changed, 76 deletions(-) diff --git a/src/core/loader.cpp b/src/core/loader.cpp index ff1c873bb..c14f2259c 100644 --- a/src/core/loader.cpp +++ b/src/core/loader.cpp @@ -8,34 +8,12 @@ #include "core/loader.h" #include "core/system.h" #include "core/core.h" -#include "core/file_sys/directory_file_system.h" #include "core/elf/elf_reader.h" #include "core/hle/kernel/kernel.h" #include "core/mem_map.h" //////////////////////////////////////////////////////////////////////////////////////////////////// -/// Loads an extracted CXI from a directory -bool LoadDirectory_CXI(std::string &filename) { - std::string full_path = filename; - std::string path, file, extension; - SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); -#if EMU_PLATFORM == PLATFORM_WINDOWS - path = ReplaceAll(path, "/", "\\"); -#endif - DirectoryFileSystem *fs = new DirectoryFileSystem(&System::g_ctr_file_system, path); - System::g_ctr_file_system.Mount("fs:", fs); - - std::string final_name = "fs:/" + file + extension; - File::IOFile f(filename, "rb"); - - if (f.IsOpen()) { - // TODO(ShizZy): read here to memory.... - } - ERROR_LOG(TIME, "Unimplemented function!"); - return true; -} - /// Loads a CTR ELF file bool Load_ELF(std::string &filename) { std::string full_path = filename; @@ -68,54 +46,6 @@ bool Load_ELF(std::string &filename) { return true; } -/// Loads a Launcher DAT file -bool Load_DAT(std::string &filename) { - std::string full_path = filename; - std::string path, file, extension; - SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); -#if EMU_PLATFORM == PLATFORM_WINDOWS - path = ReplaceAll(path, "/", "\\"); -#endif - File::IOFile f(filename, "rb"); - - if (f.IsOpen()) { - u64 size = f.GetSize(); - u8* buffer = new u8[size]; - - f.ReadBytes(buffer, size); - - /** - * (mattvail) We shouldn't really support this type of file - * but for the sake of making it easier... we'll temporarily/hackishly - * allow it. No sense in making a proper reader for this. - */ - u32 entry_point = 0x00100000; // write to same entrypoint as elf - u32 payload_offset = 0xA150; - - const u8 *src = &buffer[payload_offset]; - u8 *dst = Memory::GetPointer(entry_point); - u32 srcSize = size - payload_offset; //just load everything... - u32 *s = (u32*)src; - u32 *d = (u32*)dst; - for (int j = 0; j < (int)(srcSize + 3) / 4; j++) - { - *d++ = (*s++); - } - - Kernel::LoadExec(entry_point); - - - delete[] buffer; - } - else { - return false; - } - f.Close(); - - return true; -} - - /// Loads a CTR BIN file extracted from an ExeFS bool Load_BIN(std::string &filename) { std::string full_path = filename; @@ -229,12 +159,6 @@ bool LoadFile(std::string &filename, std::string *error_string) { case FILETYPE_CTR_BIN: return Load_BIN(filename); - case FILETYPE_LAUNCHER_DAT: - return Load_DAT(filename); - - case FILETYPE_DIRECTORY_CXI: - return LoadDirectory_CXI(filename); - case FILETYPE_ERROR: ERROR_LOG(LOADER, "Could not read file"); *error_string = "Error reading file"; From 2d7c46c874bdaebec09116c6aa0620026f5dc180 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 16 Jun 2014 17:53:25 -0400 Subject: [PATCH 02/15] Loader: Added stubbed detection of CXI and CCI files. --- src/core/loader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/loader.cpp b/src/core/loader.cpp index c14f2259c..94b59e5da 100644 --- a/src/core/loader.cpp +++ b/src/core/loader.cpp @@ -120,6 +120,12 @@ FileType IdentifyFile(std::string &filename) { else if (!strcasecmp(extension.c_str(), ".axf")) { return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p } + else if (!strcasecmp(extension.c_str(), ".cxi")) { + return FILETYPE_CTR_CXI; // TODO(bunnei): Do some filetype checking :p + } + else if (!strcasecmp(extension.c_str(), ".cci")) { + return FILETYPE_CTR_CCI; // TODO(bunnei): Do some filetype checking :p + } else if (!strcasecmp(extension.c_str(), ".bin")) { return FILETYPE_CTR_BIN; } From 0aca202ae936d3fccbab34f36d9246e0598849a5 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 16 Jun 2014 18:03:13 -0400 Subject: [PATCH 03/15] Loader: Moved elf and loader modules to a "loader" subdirectory. --- src/citra/citra.cpp | 2 +- src/citra_qt/bootmanager.cpp | 2 +- src/citra_qt/main.cpp | 3 ++- src/core/CMakeLists.txt | 10 ++++---- src/core/core.vcxproj | 12 ++++----- src/core/core.vcxproj.filters | 34 ++++++++++++++----------- src/core/{elf => loader}/elf_reader.cpp | 2 +- src/core/{elf => loader}/elf_reader.h | 2 +- src/core/{elf => loader}/elf_types.h | 0 src/core/{ => loader}/loader.cpp | 4 +-- src/core/{ => loader}/loader.h | 0 11 files changed, 38 insertions(+), 33 deletions(-) rename src/core/{elf => loader}/elf_reader.cpp (99%) rename src/core/{elf => loader}/elf_reader.h (98%) rename src/core/{elf => loader}/elf_types.h (100%) rename src/core/{ => loader}/loader.cpp (98%) rename src/core/{ => loader}/loader.h (100%) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index 5a8642d1b..f6bb10f29 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -8,7 +8,7 @@ #include "core/system.h" #include "core/core.h" -#include "core/loader.h" +#include "core/loader/loader.h" #include "citra/emu_window/emu_window_glfw.h" diff --git a/src/citra_qt/bootmanager.cpp b/src/citra_qt/bootmanager.cpp index f85116419..421b2af55 100644 --- a/src/citra_qt/bootmanager.cpp +++ b/src/citra_qt/bootmanager.cpp @@ -5,7 +5,7 @@ #include "bootmanager.hxx" #include "core/core.h" -#include "core/loader.h" +#include "core/loader/loader.h" #include "core/hw/hw.h" #include "video_core/video_core.h" diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index 087716c01..cff0b6a2a 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -23,9 +23,10 @@ #include "debugger/graphics_cmdlists.hxx" #include "core/system.h" -#include "core/loader.h" #include "core/core.h" +#include "core/loader/loader.h" #include "core/arm/disassembler/load_symbol_map.h" + #include "version.h" diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 7116b88e9..2361fd2e3 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,6 +1,7 @@ set(SRCS core.cpp core_timing.cpp - loader.cpp + loader/elf_reader.cpp + loader/loader.cpp mem_map.cpp mem_map_funcs.cpp system.cpp @@ -27,7 +28,6 @@ set(SRCS core.cpp arm/interpreter/mmu/tlb.cpp arm/interpreter/mmu/wb.cpp arm/interpreter/mmu/xscale_copro.cpp - elf/elf_reader.cpp file_sys/directory_file_system.cpp file_sys/meta_file_system.cpp hle/hle.cpp @@ -50,7 +50,9 @@ set(SRCS core.cpp set(HEADERS core.h core_timing.h - loader.h + loader/elf_reader.h + loader/elf_types.h + loader/loader.h mem_map.h system.h arm/disassembler/arm_disasm.h @@ -72,8 +74,6 @@ set(HEADERS core.h arm/interpreter/vfp/asm_vfp.h arm/interpreter/vfp/vfp.h arm/interpreter/vfp/vfp_helper.h - elf/elf_reader.h - elf/elf_types.h file_sys/directory_file_system.h file_sys/file_sys.h file_sys/meta_file_system.h diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 8eb189a8b..07d3d57c8 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -162,7 +162,6 @@ - @@ -182,7 +181,8 @@ - + + @@ -210,8 +210,6 @@ - - @@ -233,7 +231,9 @@ - + + + @@ -243,4 +243,4 @@ - + \ No newline at end of file diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index da781f816..5b2be3398 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -16,9 +16,6 @@ {d1158fc4-3e0f-431f-9d3b-f30bbfeb4ad5} - - {7ae34319-6d72-4d12-bc62-9b438ba9241f} - {8b62769e-3e2a-4a57-a7bc-b3b2933c2bc7} @@ -34,6 +31,9 @@ {8089d94b-5faa-43dc-854b-ffd2fa2e7fe3} + + {491d5558-5f3a-4283-8ba4-0a58b1984e37} + @@ -72,11 +72,7 @@ hw - - elf - - @@ -171,6 +167,12 @@ hle\service + + loader + + + loader + @@ -212,18 +214,11 @@ hw - - elf - - - elf - arm - @@ -307,8 +302,17 @@ hle\service + + loader + + + loader + + + loader + - + \ No newline at end of file diff --git a/src/core/elf/elf_reader.cpp b/src/core/loader/elf_reader.cpp similarity index 99% rename from src/core/elf/elf_reader.cpp rename to src/core/loader/elf_reader.cpp index c62332cec..123747f8e 100644 --- a/src/core/elf/elf_reader.cpp +++ b/src/core/loader/elf_reader.cpp @@ -8,7 +8,7 @@ #include "common/symbols.h" #include "core/mem_map.h" -#include "core/elf/elf_reader.h" +#include "core/loader/elf_reader.h" //void bswap(Elf32_Word &w) {w = Common::swap32(w);} //void bswap(Elf32_Half &w) {w = Common::swap16(w);} diff --git a/src/core/elf/elf_reader.h b/src/core/loader/elf_reader.h similarity index 98% rename from src/core/elf/elf_reader.h rename to src/core/loader/elf_reader.h index 3e2869f87..6f0ad84b3 100644 --- a/src/core/elf/elf_reader.h +++ b/src/core/loader/elf_reader.h @@ -4,7 +4,7 @@ #pragma once -#include "core/elf/elf_types.h" +#include "core/loader/elf_types.h" enum KnownElfTypes { diff --git a/src/core/elf/elf_types.h b/src/core/loader/elf_types.h similarity index 100% rename from src/core/elf/elf_types.h rename to src/core/loader/elf_types.h diff --git a/src/core/loader.cpp b/src/core/loader/loader.cpp similarity index 98% rename from src/core/loader.cpp rename to src/core/loader/loader.cpp index 94b59e5da..7e6922e0c 100644 --- a/src/core/loader.cpp +++ b/src/core/loader/loader.cpp @@ -5,10 +5,10 @@ #include "common/common_types.h" #include "common/file_util.h" -#include "core/loader.h" +#include "core/loader/loader.h" +#include "core/loader/elf_reader.h" #include "core/system.h" #include "core/core.h" -#include "core/elf/elf_reader.h" #include "core/hle/kernel/kernel.h" #include "core/mem_map.h" diff --git a/src/core/loader.h b/src/core/loader/loader.h similarity index 100% rename from src/core/loader.h rename to src/core/loader/loader.h From 3577dd027ddbaa2517eacad700ed0768a1f4b518 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 16 Jun 2014 22:57:09 -0400 Subject: [PATCH 04/15] Loader: Added support for booting NCCH executables. NCCH: Fixed typo in printing NCCH filename. --- src/core/CMakeLists.txt | 2 + src/core/core.vcxproj | 2 + src/core/core.vcxproj.filters | 6 + src/core/loader/loader.cpp | 5 + src/core/loader/ncch.cpp | 348 ++++++++++++++++++++++++++++++++++ src/core/loader/ncch.h | 21 ++ 6 files changed, 384 insertions(+) create mode 100644 src/core/loader/ncch.cpp create mode 100644 src/core/loader/ncch.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 2361fd2e3..3f2d99e44 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -2,6 +2,7 @@ set(SRCS core.cpp core_timing.cpp loader/elf_reader.cpp loader/loader.cpp + loader/ncch.cpp mem_map.cpp mem_map_funcs.cpp system.cpp @@ -53,6 +54,7 @@ set(HEADERS core.h loader/elf_reader.h loader/elf_types.h loader/loader.h + loader/ncch.h mem_map.h system.h arm/disassembler/arm_disasm.h diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index 07d3d57c8..bda89ff1d 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -183,6 +183,7 @@ + @@ -234,6 +235,7 @@ + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 5b2be3398..84f6160b5 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -173,6 +173,9 @@ loader + + loader + @@ -311,6 +314,9 @@ loader + + loader + diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 7e6922e0c..41fa9c32e 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -7,6 +7,7 @@ #include "core/loader/loader.h" #include "core/loader/elf_reader.h" +#include "core/loader/ncch.h" #include "core/system.h" #include "core/core.h" #include "core/hle/kernel/kernel.h" @@ -165,6 +166,10 @@ bool LoadFile(std::string &filename, std::string *error_string) { case FILETYPE_CTR_BIN: return Load_BIN(filename); + 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"; diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp new file mode 100644 index 000000000..a01411e11 --- /dev/null +++ b/src/core/loader/ncch.cpp @@ -0,0 +1,348 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "common/file_util.h" + +#include "core/loader/ncch.h" +#include "core/hle/kernel/kernel.h" +#include "core/mem_map.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// NCCH header (Note: "NCCH" appears to be a publically unknown acronym) + +struct NCCH_Header { + u8 signature[0x100]; + char magic[4]; + u32 content_size; + u8 partition_id[8]; + u16 maker_code; + u16 version; + u8 reserved_0[4]; + u8 program_id[8]; + u8 temp_flag; + u8 reserved_1[0x2f]; + u8 product_code[0x10]; + u8 extended_header_hash[0x20]; + u32 extended_header_size; + u8 reserved_2[4]; + u8 flags[8]; + u32 plain_region_offset; + u32 plain_region_size; + u8 reserved_3[8]; + u32 exefs_offset; + u32 exefs_size; + u32 exefs_hash_region_size; + u8 reserved_4[4]; + u32 romfs_offset; + u32 romfs_size; + u32 romfs_hash_region_size; + u8 reserved_5[4]; + u8 exefs_super_block_hash[0x20]; + u8 romfs_super_block_hash[0x20]; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ExeFS (executable file system) headers + +typedef struct { + char name[8]; + u32 offset; + u32 size; +} ExeFs_SectionHeader; + +typedef struct { + ExeFs_SectionHeader section[8]; + u8 reserved[0x80]; + u8 hashes[8][0x20]; +} ExeFs_Header; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ExHeader (executable file system header) headers + +struct ExHeader_SystemInfoFlags{ + u8 reserved[5]; + u8 flag; + u8 remaster_version[2]; +} exheader_systeminfoflags; + +struct ExHeader_CodeSegmentInfo{ + u32 address; + u32 num_max_pages; + u32 code_size; +} exheader_codesegmentinfo; + +struct ExHeader_CodeSetInfo { + u8 name[8]; + ExHeader_SystemInfoFlags flags; + ExHeader_CodeSegmentInfo text; + u8 stacksize[4]; + ExHeader_CodeSegmentInfo ro; + u8 reserved[4]; + ExHeader_CodeSegmentInfo data; + u8 bsssize[4]; +}; + +struct ExHeader_DependencyList{ + u8 program_id[0x30][8]; +}; + +struct ExHeader_SystemInfo{ + u32 save_data_size; + u8 reserved[4]; + u8 jump_id[8]; + u8 reserved_2[0x30]; +}; + +struct ExHeader_StorageInfo{ + u8 ext_save_data_id[8]; + u8 system_save_data_id[8]; + u8 reserved[8]; + u8 access_info[7]; + u8 other_attributes; +}; + +struct ExHeader_ARM11_SystemLocalCaps{ + u8 program_id[8]; + u8 flags[8]; + u8 resource_limit_descriptor[0x10][2]; + ExHeader_StorageInfo storage_info; + u8 service_access_control[0x20][8]; + u8 reserved[0x1f]; + u8 resource_limit_category; +}; + +struct ExHeader_ARM11_KernelCaps{ + u8 descriptors[28][4]; + u8 reserved[0x10]; +}; + +struct ExHeader_ARM9_AccessControl{ + u8 descriptors[15]; + u8 descversion; +}; + +struct ExHeader_Header{ + ExHeader_CodeSetInfo codeset_info; + ExHeader_DependencyList dependency_list; + ExHeader_SystemInfo system_info; + ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; + ExHeader_ARM11_KernelCaps arm11_kernel_caps; + ExHeader_ARM9_AccessControl arm9_access_control; + struct { + u8 signature[0x100]; + u8 ncch_public_key_modulus[0x100]; + ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; + ExHeader_ARM11_KernelCaps arm11_kernel_caps; + ExHeader_ARM9_AccessControl arm9_access_control; + } access_desc; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Loader namespace + +namespace Loader { + +const int kExeFs_MaxSections = 8; ///< Maximum number of sections (files) in an ExeFs +const int kExeFs_BlockSize = 0x200; ///< Size of ExeFS blocks (in bytes) + +/** + * Get the decompressed size of an LZSS compressed ExeFS file + * @param buffer Buffer of compressed file + * @param size Size of compressed buffer + * @return Size of decompressed buffer + */ +u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { + u32 offset_size = *(u32*)(buffer + size - 4); + return offset_size + size; +} + +/** + * Decompress ExeFS file (compressed with LZSS) + * @param compressed Compressed buffer + * @param compressed_size Size of compressed buffer + * @param decompressed Decompressed buffer + * @param decompressed_size Size of decompressed buffer + * @param error_string String populated with error message on failure + * @return True on success, otherwise false + */ +bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size, + std::string* error_string) { + u8* footer = compressed + compressed_size - 8; + u32 buffer_top_and_bottom = *(u32*)footer; + u32 i, j; + u32 out = decompressed_size; + u32 index = compressed_size - ((buffer_top_and_bottom >> 24) & 0xFF); + u8 control; + u32 stop_index = compressed_size - (buffer_top_and_bottom & 0xFFFFFF); + + memset(decompressed, 0, decompressed_size); + memcpy(decompressed, compressed, compressed_size); + + while(index > stop_index) { + control = compressed[--index]; + + for(i = 0; i < 8; i++) { + if(index <= stop_index) + break; + if(index <= 0) + break; + if(out <= 0) + break; + + if(control & 0x80) { + if(index < 2) { + *error_string = "Compression out of bounds"; + return false; + } + index -= 2; + + u32 segment_offset = compressed[index] | (compressed[index + 1] << 8); + u32 segment_size = ((segment_offset >> 12) & 15) + 3; + segment_offset &= 0x0FFF; + segment_offset += 2; + + if(out < segment_size) { + *error_string = "Compression out of bounds"; + return false; + } + for(j = 0; j < segment_size; j++) { + u8 data; + if(out + segment_offset >= decompressed_size) { + *error_string = "Compression out of bounds"; + return false; + } + data = decompressed[out + segment_offset]; + decompressed[--out] = data; + } + } else { + if(out < 1) { + *error_string = "Compression out of bounds"; + return false; + } + decompressed[--out] = compressed[--index]; + } + control <<= 1; + } + } + return true; +} + +/** + * Load a data buffer into memory at the specified address + * @param addr Address to load memory into + * @param buffer Buffer of data to load into memory + * @param size Size of data to load into memory + * @todo Perhaps move this code somewhere more generic? + */ +void LoadBuffer(const u32 addr, const u8* const buffer, const int size) { + u32 *dst = (u32*)Memory::GetPointer(addr); + u32 *src = (u32*)buffer; + int size_aligned = (size + 3) / 4; + + for (int j = 0; j < size_aligned; j++) { + *dst++ = (*src++); + } + return; +} + +/** + * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) + * @param filename String filename of NCCH file + * @param error_string Pointer to string to put error message if an error has occurred + * @todo Move NCSD parsing out of here and create a separate function for loading these + * @return True on success, otherwise false + */ +bool Load_NCCH(std::string& filename, std::string* error_string) { + INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); + + 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)... + 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... + if (memcmp(&ncch_header.magic, "NCCH", 4) != 0) { + *error_string = "Invalid NCCH magic number (likely incorrect file type)"; + 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)); + + // Iterate through the ExeFs archive until we find the .code file... + for (int i = 0; i < kExeFs_MaxSections; i++) { + INFO_LOG(LOADER, "ExeFS section %d:", i); + INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); + INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); + INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size); + + // Load the .code section (executable code)... + if (strcmp((char*) exefs_header.section[i].name, ".code") == 0) { + file.Seek(exefs_header.section[i].offset + exefs_offset + sizeof(ExeFs_Header) + + ncch_off, 0); + + u8* buffer = new u8[exefs_header.section[i].size]; + file.ReadBytes(buffer, exefs_header.section[i].size); + + // Load compressed executable... + if (i == 0 && is_compressed) { + u32 decompressed_size = LZSS_GetDecompressedSize(buffer, + exefs_header.section[i].size); + u8* decompressed_buffer = new u8[decompressed_size]; + + if (!LZSS_Decompress(buffer, exefs_header.section[i].size, decompressed_buffer, + decompressed_size, error_string)) { + return false; + } + // Load .code section into memory... + LoadBuffer(exheader_header.codeset_info.text.address, decompressed_buffer, + decompressed_size); + + delete[] decompressed_buffer; + + // Load uncompressed executable... + } else { + // Load .code section into memory... + LoadBuffer(exheader_header.codeset_info.text.address, buffer, + exefs_header.section[i].size); + } + delete[] buffer; + + // Setup kernel emulation to boot .code section... + Kernel::LoadExec(exheader_header.codeset_info.text.address); + + // No need to load the other files from ExeFS until we do something with them... + return true; + } + } + } + return false; +} + +} // namespace Loader diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h new file mode 100644 index 000000000..778e8b456 --- /dev/null +++ b/src/core/loader/ncch.h @@ -0,0 +1,21 @@ +// Copyright 2014 Citra Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "common/common.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +namespace Loader { + +/** + * Loads an NCCH file (e.g. from a CCI or CXI) + * @param filename String filename of NCCH file + * @param error_string Pointer to string to put error message if an error has occurred + * @return True on success, otherwise false + */ +bool Load_NCCH(std::string& filename, std::string* error_string); + +} // namespace Loader From 1da361c7ab55e1dbe6709a738e228bfab5a5bb78 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 16 Jun 2014 23:05:10 -0400 Subject: [PATCH 05/15] Elf: Renamed modules to be consistent with new loader naming, fixed tabs -> spaces. --- src/core/CMakeLists.txt | 5 +- src/core/core.vcxproj | 5 +- src/core/core.vcxproj.filters | 15 +- src/core/loader/elf.cpp | 190 ++++++++++++++++++++ src/core/loader/{elf_types.h => elf.h} | 234 +++++++++++++++---------- src/core/loader/elf_reader.cpp | 190 -------------------- src/core/loader/elf_reader.h | 75 -------- src/core/loader/loader.cpp | 2 +- 8 files changed, 343 insertions(+), 373 deletions(-) create mode 100644 src/core/loader/elf.cpp rename src/core/loader/{elf_types.h => elf.h} (51%) delete mode 100644 src/core/loader/elf_reader.cpp delete mode 100644 src/core/loader/elf_reader.h diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3f2d99e44..634f4d572 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,6 +1,6 @@ set(SRCS core.cpp core_timing.cpp - loader/elf_reader.cpp + loader/elf.cpp loader/loader.cpp loader/ncch.cpp mem_map.cpp @@ -51,8 +51,7 @@ set(SRCS core.cpp set(HEADERS core.h core_timing.h - loader/elf_reader.h - loader/elf_types.h + loader/elf.h loader/loader.h loader/ncch.h mem_map.h diff --git a/src/core/core.vcxproj b/src/core/core.vcxproj index bda89ff1d..e2216760a 100644 --- a/src/core/core.vcxproj +++ b/src/core/core.vcxproj @@ -181,7 +181,7 @@ - + @@ -232,8 +232,7 @@ - - + diff --git a/src/core/core.vcxproj.filters b/src/core/core.vcxproj.filters index 84f6160b5..91d3292da 100644 --- a/src/core/core.vcxproj.filters +++ b/src/core/core.vcxproj.filters @@ -167,15 +167,15 @@ hle\service - - loader - loader loader + + loader + @@ -305,18 +305,15 @@ hle\service - - loader - - - loader - loader loader + + loader + diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp new file mode 100644 index 000000000..153c30f51 --- /dev/null +++ b/src/core/loader/elf.cpp @@ -0,0 +1,190 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include + +#include "common/common.h" + +#include "common/symbols.h" +#include "core/mem_map.h" +#include "core/loader/elf.h" + +//void bswap(Elf32_Word &w) {w = Common::swap32(w);} +//void bswap(Elf32_Half &w) {w = Common::swap16(w);} + +#define bswap(w) w // Dirty bswap disable for now... 3DS is little endian, anyway + +static void byteswapHeader(Elf32_Ehdr &ELF_H) +{ + bswap(ELF_H.e_type); + bswap(ELF_H.e_machine); + bswap(ELF_H.e_ehsize); + bswap(ELF_H.e_phentsize); + bswap(ELF_H.e_phnum); + bswap(ELF_H.e_shentsize); + bswap(ELF_H.e_shnum); + bswap(ELF_H.e_shstrndx); + bswap(ELF_H.e_version); + bswap(ELF_H.e_entry); + bswap(ELF_H.e_phoff); + bswap(ELF_H.e_shoff); + bswap(ELF_H.e_flags); +} + +static void byteswapSegment(Elf32_Phdr &sec) +{ + bswap(sec.p_align); + bswap(sec.p_filesz); + bswap(sec.p_flags); + bswap(sec.p_memsz); + bswap(sec.p_offset); + bswap(sec.p_paddr); + bswap(sec.p_vaddr); + bswap(sec.p_type); +} + +static void byteswapSection(Elf32_Shdr &sec) +{ + bswap(sec.sh_addr); + bswap(sec.sh_addralign); + bswap(sec.sh_entsize); + bswap(sec.sh_flags); + bswap(sec.sh_info); + bswap(sec.sh_link); + bswap(sec.sh_name); + bswap(sec.sh_offset); + bswap(sec.sh_size); + bswap(sec.sh_type); +} + +ElfReader::ElfReader(void *ptr) +{ + base = (char*)ptr; + base32 = (u32 *)ptr; + header = (Elf32_Ehdr*)ptr; + byteswapHeader(*header); + + segments = (Elf32_Phdr *)(base + header->e_phoff); + sections = (Elf32_Shdr *)(base + header->e_shoff); + + entryPoint = header->e_entry; + + LoadSymbols(); +} + +const char *ElfReader::GetSectionName(int section) const +{ + if (sections[section].sh_type == SHT_NULL) + return nullptr; + + int nameOffset = sections[section].sh_name; + char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); + + if (ptr) + return ptr + nameOffset; + else + return nullptr; +} + +bool ElfReader::LoadInto(u32 vaddr) +{ + DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); + + // Should we relocate? + bRelocate = (header->e_type != ET_EXEC); + + if (bRelocate) + { + DEBUG_LOG(MASTER_LOG, "Relocatable module"); + entryPoint += vaddr; + } + else + { + DEBUG_LOG(MASTER_LOG, "Prerelocated executable"); + } + + INFO_LOG(MASTER_LOG, "%i segments:", header->e_phnum); + + // First pass : Get the bits into RAM + u32 segmentVAddr[32]; + + u32 baseAddress = bRelocate ? vaddr : 0; + + for (int i = 0; i < header->e_phnum; i++) + { + Elf32_Phdr *p = segments + i; + + INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); + + if (p->p_type == PT_LOAD) + { + segmentVAddr[i] = baseAddress + p->p_vaddr; + u32 writeAddr = segmentVAddr[i]; + + const u8 *src = GetSegmentPtr(i); + u8 *dst = Memory::GetPointer(writeAddr); + u32 srcSize = p->p_filesz; + u32 dstSize = p->p_memsz; + u32 *s = (u32*)src; + u32 *d = (u32*)dst; + for (int j = 0; j < (int)(srcSize + 3) / 4; j++) + { + *d++ = /*_byteswap_ulong*/(*s++); + } + if (srcSize < dstSize) + { + //memset(dst + srcSize, 0, dstSize-srcSize); //zero out bss + } + INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); + } + } + + + INFO_LOG(MASTER_LOG, "Done loading."); + return true; +} + +SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const +{ + for (int i = firstSection; i < header->e_shnum; i++) + { + const char *secname = GetSectionName(i); + + if (secname != nullptr && strcmp(name, secname) == 0) + return i; + } + return -1; +} + +bool ElfReader::LoadSymbols() +{ + bool hasSymbols = false; + SectionID sec = GetSectionByName(".symtab"); + if (sec != -1) + { + int stringSection = sections[sec].sh_link; + const char *stringBase = (const char *)GetSectionDataPtr(stringSection); + + //We have a symbol table! + Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); + int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); + for (int sym = 0; sym < numSymbols; sym++) + { + int size = symtab[sym].st_size; + if (size == 0) + continue; + + // int bind = symtab[sym].st_info >> 4; + int type = symtab[sym].st_info & 0xF; + + const char *name = stringBase + symtab[sym].st_name; + + Symbols::Add(symtab[sym].st_value, name, size, type); + + hasSymbols = true; + } + } + + return hasSymbols; +} diff --git a/src/core/loader/elf_types.h b/src/core/loader/elf.h similarity index 51% rename from src/core/loader/elf_types.h rename to src/core/loader/elf.h index f1bf3db72..2e6b80982 100644 --- a/src/core/loader/elf_types.h +++ b/src/core/loader/elf.h @@ -1,34 +1,34 @@ -// Copyright 2013 Dolphin Emulator Project +// Copyright 2013 Dolphin Emulator Project / Citra Emulator Project // Licensed under GPLv2 // Refer to the license.txt file included. #pragma once +#include "common/common.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, +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 +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 @@ -62,8 +62,6 @@ enum ElfMachine #define ELFDATA2LSB 1 #define ELFDATA2MSB 2 - - // Sections constants // Section indexes @@ -96,14 +94,13 @@ enum ElfMachine // Custom section types #define SHT_PSPREL 0x700000a0 - // Section flags enum ElfSectionFlags { - SHF_WRITE = 0x1, - SHF_ALLOC = 0x2, - SHF_EXECINSTR = 0x4, - SHF_MASKPROC = 0xF0000000, + SHF_WRITE = 0x1, + SHF_ALLOC = 0x2, + SHF_EXECINSTR = 0x4, + SHF_MASKPROC = 0xF0000000, }; // Symbol binding @@ -188,63 +185,58 @@ 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; +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; +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; +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; +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; }; #define ELF32_ST_BIND(i) ((i)>>4) @@ -252,30 +244,88 @@ struct Elf32_Sym #define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) // Relocation entries -struct Elf32_Rel -{ - Elf32_Addr r_offset; - Elf32_Word r_info; +struct Elf32_Rel { + Elf32_Addr r_offset; + Elf32_Word r_info; }; -struct Elf32_Rela -{ - Elf32_Addr r_offset; - Elf32_Word r_info; - Elf32_Sword r_addend; +struct Elf32_Rela { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; }; #define ELF32_R_SYM(i) ((i)>>8) #define ELF32_R_TYPE(i) ((unsigned char)(i)) #define ELF32_R_INFO(s,t) (((s)<<8 )+(unsigned char)(t)) - -struct Elf32_Dyn -{ - Elf32_Sword d_tag; - union - { - Elf32_Word d_val; - Elf32_Addr d_ptr; - } d_un; +struct Elf32_Dyn { + Elf32_Sword d_tag; + union { + Elf32_Word d_val; + Elf32_Addr d_ptr; + } d_un; +}; + +enum KnownElfTypes { + KNOWNELF_PSP = 0, + KNOWNELF_DS = 1, + KNOWNELF_GBA = 2, + KNOWNELF_GC = 3, +}; + +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; + } }; diff --git a/src/core/loader/elf_reader.cpp b/src/core/loader/elf_reader.cpp deleted file mode 100644 index 123747f8e..000000000 --- a/src/core/loader/elf_reader.cpp +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#include - -#include "common/common.h" - -#include "common/symbols.h" -#include "core/mem_map.h" -#include "core/loader/elf_reader.h" - -//void bswap(Elf32_Word &w) {w = Common::swap32(w);} -//void bswap(Elf32_Half &w) {w = Common::swap16(w);} - -#define bswap(w) w // Dirty bswap disable for now... 3DS is little endian, anyway - -static void byteswapHeader(Elf32_Ehdr &ELF_H) -{ - bswap(ELF_H.e_type); - bswap(ELF_H.e_machine); - bswap(ELF_H.e_ehsize); - bswap(ELF_H.e_phentsize); - bswap(ELF_H.e_phnum); - bswap(ELF_H.e_shentsize); - bswap(ELF_H.e_shnum); - bswap(ELF_H.e_shstrndx); - bswap(ELF_H.e_version); - bswap(ELF_H.e_entry); - bswap(ELF_H.e_phoff); - bswap(ELF_H.e_shoff); - bswap(ELF_H.e_flags); -} - -static void byteswapSegment(Elf32_Phdr &sec) -{ - bswap(sec.p_align); - bswap(sec.p_filesz); - bswap(sec.p_flags); - bswap(sec.p_memsz); - bswap(sec.p_offset); - bswap(sec.p_paddr); - bswap(sec.p_vaddr); - bswap(sec.p_type); -} - -static void byteswapSection(Elf32_Shdr &sec) -{ - bswap(sec.sh_addr); - bswap(sec.sh_addralign); - bswap(sec.sh_entsize); - bswap(sec.sh_flags); - bswap(sec.sh_info); - bswap(sec.sh_link); - bswap(sec.sh_name); - bswap(sec.sh_offset); - bswap(sec.sh_size); - bswap(sec.sh_type); -} - -ElfReader::ElfReader(void *ptr) -{ - base = (char*)ptr; - base32 = (u32 *)ptr; - header = (Elf32_Ehdr*)ptr; - byteswapHeader(*header); - - segments = (Elf32_Phdr *)(base + header->e_phoff); - sections = (Elf32_Shdr *)(base + header->e_shoff); - - entryPoint = header->e_entry; - - LoadSymbols(); -} - -const char *ElfReader::GetSectionName(int section) const -{ - if (sections[section].sh_type == SHT_NULL) - return nullptr; - - int nameOffset = sections[section].sh_name; - char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); - - if (ptr) - return ptr + nameOffset; - else - return nullptr; -} - -bool ElfReader::LoadInto(u32 vaddr) -{ - DEBUG_LOG(MASTER_LOG,"String section: %i", header->e_shstrndx); - - // Should we relocate? - bRelocate = (header->e_type != ET_EXEC); - - if (bRelocate) - { - DEBUG_LOG(MASTER_LOG,"Relocatable module"); - entryPoint += vaddr; - } - else - { - DEBUG_LOG(MASTER_LOG,"Prerelocated executable"); - } - - INFO_LOG(MASTER_LOG,"%i segments:", header->e_phnum); - - // First pass : Get the bits into RAM - u32 segmentVAddr[32]; - - u32 baseAddress = bRelocate?vaddr:0; - - for (int i = 0; i < header->e_phnum; i++) - { - Elf32_Phdr *p = segments + i; - - INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); - - if (p->p_type == PT_LOAD) - { - segmentVAddr[i] = baseAddress + p->p_vaddr; - u32 writeAddr = segmentVAddr[i]; - - const u8 *src = GetSegmentPtr(i); - u8 *dst = Memory::GetPointer(writeAddr); - u32 srcSize = p->p_filesz; - u32 dstSize = p->p_memsz; - u32 *s = (u32*)src; - u32 *d = (u32*)dst; - for (int j = 0; j < (int)(srcSize + 3) / 4; j++) - { - *d++ = /*_byteswap_ulong*/(*s++); - } - if (srcSize < dstSize) - { - //memset(dst + srcSize, 0, dstSize-srcSize); //zero out bss - } - INFO_LOG(MASTER_LOG,"Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); - } - } - - - INFO_LOG(MASTER_LOG,"Done loading."); - return true; -} - -SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const -{ - for (int i = firstSection; i < header->e_shnum; i++) - { - const char *secname = GetSectionName(i); - - if (secname != nullptr && strcmp(name, secname) == 0) - return i; - } - return -1; -} - -bool ElfReader::LoadSymbols() -{ - bool hasSymbols = false; - SectionID sec = GetSectionByName(".symtab"); - if (sec != -1) - { - int stringSection = sections[sec].sh_link; - const char *stringBase = (const char *)GetSectionDataPtr(stringSection); - - //We have a symbol table! - Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); - int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); - for (int sym = 0; sym < numSymbols; sym++) - { - int size = symtab[sym].st_size; - if (size == 0) - continue; - - // int bind = symtab[sym].st_info >> 4; - int type = symtab[sym].st_info & 0xF; - - const char *name = stringBase + symtab[sym].st_name; - - Symbols::Add(symtab[sym].st_value, name, size, type); - - hasSymbols = true; - } - } - - return hasSymbols; -} diff --git a/src/core/loader/elf_reader.h b/src/core/loader/elf_reader.h deleted file mode 100644 index 6f0ad84b3..000000000 --- a/src/core/loader/elf_reader.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2013 Dolphin Emulator Project -// Licensed under GPLv2 -// Refer to the license.txt file included. - -#pragma once - -#include "core/loader/elf_types.h" - -enum KnownElfTypes -{ - KNOWNELF_PSP = 0, - KNOWNELF_DS = 1, - KNOWNELF_GBA = 2, - KNOWNELF_GC = 3, -}; - -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; - } -}; diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 41fa9c32e..d8060c0e6 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -6,7 +6,7 @@ #include "common/file_util.h" #include "core/loader/loader.h" -#include "core/loader/elf_reader.h" +#include "core/loader/elf.h" #include "core/loader/ncch.h" #include "core/system.h" #include "core/core.h" From 13bdaa6c609a8718d4ce6ca3ce5f1e16f4d7c600 Mon Sep 17 00:00:00 2001 From: bunnei Date: Mon, 16 Jun 2014 23:18:10 -0400 Subject: [PATCH 06/15] Loader: Cleaned up and removed unused code, refactored ELF namespace. --- src/core/loader/elf.cpp | 107 +++++++++++++--------------- src/core/loader/elf.h | 15 ++++ src/core/loader/loader.cpp | 142 +------------------------------------ src/core/loader/loader.h | 15 +--- 4 files changed, 70 insertions(+), 209 deletions(-) diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 153c30f51..f93354817 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -5,65 +5,17 @@ #include #include "common/common.h" - +#include "common/file_util.h" #include "common/symbols.h" + #include "core/mem_map.h" #include "core/loader/elf.h" +#include "core/hle/kernel/kernel.h" -//void bswap(Elf32_Word &w) {w = Common::swap32(w);} -//void bswap(Elf32_Half &w) {w = Common::swap16(w);} - -#define bswap(w) w // Dirty bswap disable for now... 3DS is little endian, anyway - -static void byteswapHeader(Elf32_Ehdr &ELF_H) -{ - bswap(ELF_H.e_type); - bswap(ELF_H.e_machine); - bswap(ELF_H.e_ehsize); - bswap(ELF_H.e_phentsize); - bswap(ELF_H.e_phnum); - bswap(ELF_H.e_shentsize); - bswap(ELF_H.e_shnum); - bswap(ELF_H.e_shstrndx); - bswap(ELF_H.e_version); - bswap(ELF_H.e_entry); - bswap(ELF_H.e_phoff); - bswap(ELF_H.e_shoff); - bswap(ELF_H.e_flags); -} - -static void byteswapSegment(Elf32_Phdr &sec) -{ - bswap(sec.p_align); - bswap(sec.p_filesz); - bswap(sec.p_flags); - bswap(sec.p_memsz); - bswap(sec.p_offset); - bswap(sec.p_paddr); - bswap(sec.p_vaddr); - bswap(sec.p_type); -} - -static void byteswapSection(Elf32_Shdr &sec) -{ - bswap(sec.sh_addr); - bswap(sec.sh_addralign); - bswap(sec.sh_entsize); - bswap(sec.sh_flags); - bswap(sec.sh_info); - bswap(sec.sh_link); - bswap(sec.sh_name); - bswap(sec.sh_offset); - bswap(sec.sh_size); - bswap(sec.sh_type); -} - -ElfReader::ElfReader(void *ptr) -{ +ElfReader::ElfReader(void *ptr) { base = (char*)ptr; base32 = (u32 *)ptr; header = (Elf32_Ehdr*)ptr; - byteswapHeader(*header); segments = (Elf32_Phdr *)(base + header->e_phoff); sections = (Elf32_Shdr *)(base + header->e_shoff); @@ -73,8 +25,7 @@ ElfReader::ElfReader(void *ptr) LoadSymbols(); } -const char *ElfReader::GetSectionName(int section) const -{ +const char *ElfReader::GetSectionName(int section) const { if (sections[section].sh_type == SHT_NULL) return nullptr; @@ -87,8 +38,7 @@ const char *ElfReader::GetSectionName(int section) const return nullptr; } -bool ElfReader::LoadInto(u32 vaddr) -{ +bool ElfReader::LoadInto(u32 vaddr) { DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); // Should we relocate? @@ -188,3 +138,48 @@ bool ElfReader::LoadSymbols() return hasSymbols; } + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Loader namespace + +namespace Loader { + +/** + * Loads an ELF file + * @param filename String filename of ELF file + * @param error_string Pointer to string to put error message if an error has occurred + * @return True on success, otherwise false + */ +bool Load_ELF(std::string& filename, std::string* error_string) { + std::string full_path = filename; + std::string path, file, extension; + SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); +#if EMU_PLATFORM == PLATFORM_WINDOWS + path = ReplaceAll(path, "/", "\\"); +#endif + File::IOFile f(filename, "rb"); + + if (f.IsOpen()) { + u32 size = (u32)f.GetSize(); + u8* buffer = new u8[size]; + ElfReader* elf_reader = NULL; + + f.ReadBytes(buffer, size); + + elf_reader = new ElfReader(buffer); + elf_reader->LoadInto(0x00100000); + + Kernel::LoadExec(elf_reader->GetEntryPoint()); + + delete[] buffer; + delete elf_reader; + } else { + *error_string = "Unable to open ELF file!"; + return false; + } + f.Close(); + + return true; +} + +} // namespace Loader diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 2e6b80982..708281478 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -329,3 +329,18 @@ public: return bRelocate; } }; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Loader namespace + +namespace Loader { + +/** + * Loads an ELF file + * @param filename String filename of ELF file + * @param error_string Pointer to string to put error message if an error has occurred + * @return True on success, otherwise false + */ +bool Load_ELF(std::string& filename, std::string* error_string); + +} // namespace Loader diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index d8060c0e6..1a647d8a5 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -2,98 +2,14 @@ // Licensed under GPLv2 // Refer to the license.txt file included. -#include "common/common_types.h" -#include "common/file_util.h" - #include "core/loader/loader.h" #include "core/loader/elf.h" #include "core/loader/ncch.h" -#include "core/system.h" -#include "core/core.h" -#include "core/hle/kernel/kernel.h" -#include "core/mem_map.h" //////////////////////////////////////////////////////////////////////////////////////////////////// -/// Loads a CTR ELF file -bool Load_ELF(std::string &filename) { - std::string full_path = filename; - std::string path, file, extension; - SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); -#if EMU_PLATFORM == PLATFORM_WINDOWS - path = ReplaceAll(path, "/", "\\"); -#endif - File::IOFile f(filename, "rb"); - - if (f.IsOpen()) { - u64 size = f.GetSize(); - u8* buffer = new u8[size]; - ElfReader* elf_reader = NULL; - - f.ReadBytes(buffer, size); - - elf_reader = new ElfReader(buffer); - elf_reader->LoadInto(0x00100000); - - Kernel::LoadExec(elf_reader->GetEntryPoint()); - - delete[] buffer; - delete elf_reader; - } else { - return false; - } - f.Close(); - - return true; -} - -/// Loads a CTR BIN file extracted from an ExeFS -bool Load_BIN(std::string &filename) { - std::string full_path = filename; - std::string path, file, extension; - SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); -#if EMU_PLATFORM == PLATFORM_WINDOWS - path = ReplaceAll(path, "/", "\\"); -#endif - File::IOFile f(filename, "rb"); - - if (f.IsOpen()) { - u64 size = f.GetSize(); - u8* buffer = new u8[size]; - - f.ReadBytes(buffer, size); - - u32 entry_point = 0x00100000; // Hardcoded, read from exheader - - const u8 *src = buffer; - u8 *dst = Memory::GetPointer(entry_point); - u32 srcSize = size; - u32 *s = (u32*)src; - u32 *d = (u32*)dst; - for (int j = 0; j < (int)(srcSize + 3) / 4; j++) - { - *d++ = (*s++); - } - - Kernel::LoadExec(entry_point); - - delete[] buffer; - } - else { - return false; - } - f.Close(); - - return true; -} - namespace Loader { -bool IsBootableDirectory() { - ERROR_LOG(TIME, "Unimplemented function!"); - return true; -} - /** * Identifies the type of a bootable file * @param filename String filename of bootable file @@ -107,15 +23,7 @@ FileType IdentifyFile(std::string &filename) { } std::string extension = filename.size() >= 5 ? filename.substr(filename.size() - 4) : ""; - if (File::IsDirectory(filename)) { - if (IsBootableDirectory()) { - return FILETYPE_DIRECTORY_CXI; - } - else { - return FILETYPE_NORMAL_DIRECTORY; - } - } - else if (!strcasecmp(extension.c_str(), ".elf")) { + if (!strcasecmp(extension.c_str(), ".elf")) { return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p } else if (!strcasecmp(extension.c_str(), ".axf")) { @@ -127,24 +35,6 @@ FileType IdentifyFile(std::string &filename) { else if (!strcasecmp(extension.c_str(), ".cci")) { return FILETYPE_CTR_CCI; // TODO(bunnei): Do some filetype checking :p } - else if (!strcasecmp(extension.c_str(), ".bin")) { - return FILETYPE_CTR_BIN; - } - else if (!strcasecmp(extension.c_str(), ".dat")) { - return FILETYPE_LAUNCHER_DAT; - } - else if (!strcasecmp(extension.c_str(), ".zip")) { - return FILETYPE_ARCHIVE_ZIP; - } - else if (!strcasecmp(extension.c_str(), ".rar")) { - return FILETYPE_ARCHIVE_RAR; - } - else if (!strcasecmp(extension.c_str(), ".r00")) { - return FILETYPE_ARCHIVE_RAR; - } - else if (!strcasecmp(extension.c_str(), ".r01")) { - return FILETYPE_ARCHIVE_RAR; - } return FILETYPE_UNKNOWN; } @@ -161,10 +51,7 @@ bool LoadFile(std::string &filename, std::string *error_string) { switch (IdentifyFile(filename)) { case FILETYPE_CTR_ELF: - return Load_ELF(filename); - - case FILETYPE_CTR_BIN: - return Load_BIN(filename); + return Loader::Load_ELF(filename, error_string); case FILETYPE_CTR_CXI: case FILETYPE_CTR_CCI: @@ -175,29 +62,6 @@ bool LoadFile(std::string &filename, std::string *error_string) { *error_string = "Error reading file"; break; - case FILETYPE_ARCHIVE_RAR: -#ifdef WIN32 - *error_string = "RAR file detected (Require WINRAR)"; -#else - *error_string = "RAR file detected (Require UnRAR)"; -#endif - break; - - case FILETYPE_ARCHIVE_ZIP: -#ifdef WIN32 - *error_string = "ZIP file detected (Require WINRAR)"; -#else - *error_string = "ZIP file detected (Require UnRAR)"; -#endif - break; - - case FILETYPE_NORMAL_DIRECTORY: - ERROR_LOG(LOADER, "Just a directory."); - *error_string = "Just a directory."; - break; - - case FILETYPE_UNKNOWN_BIN: - case FILETYPE_UNKNOWN_ELF: case FILETYPE_UNKNOWN: default: ERROR_LOG(LOADER, "Failed to identify file"); @@ -207,4 +71,4 @@ bool LoadFile(std::string &filename, std::string *error_string) { return false; } -} // namespace \ No newline at end of file +} // namespace Loader diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 9d4aaa874..979003553 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -7,6 +7,7 @@ #include "common/common.h" //////////////////////////////////////////////////////////////////////////////////////////////////// +// Loader namespace namespace Loader { @@ -19,23 +20,9 @@ enum FileType { FILETYPE_CTR_ELF, FILETYPE_CTR_BIN, - FILETYPE_LAUNCHER_DAT, - - FILETYPE_DIRECTORY_CXI, - - FILETYPE_UNKNOWN_BIN, - FILETYPE_UNKNOWN_ELF, - - FILETYPE_ARCHIVE_RAR, - FILETYPE_ARCHIVE_ZIP, - - FILETYPE_NORMAL_DIRECTORY, - FILETYPE_UNKNOWN }; -//////////////////////////////////////////////////////////////////////////////////////////////////// - /** * Identifies the type of a bootable file * @param filename String filename of bootable file From c144ed5058e144311c5919ba8b64105811736bed Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 17 Jun 2014 06:23:46 -0400 Subject: [PATCH 07/15] Elf: Removed unused macros, changed #include of "common.h" to just "common_types.h". --- src/core/loader/elf.h | 118 +----------------------------------------- 1 file changed, 1 insertion(+), 117 deletions(-) diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 708281478..24d2f91be 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -4,7 +4,7 @@ #pragma once -#include "common/common.h" +#include "common/common_types.h" // ELF Header Constants @@ -52,27 +52,8 @@ enum ElfMachine { #define ELFMAG2 'L' #define ELFMAG3 'F' -// File class -#define ELFCLASSNONE 0 -#define ELFCLASS32 1 -#define ELFCLASS64 2 - -// Encoding -#define ELFDATANONE 0 -#define ELFDATA2LSB 1 -#define ELFDATA2MSB 2 - // Sections constants -// Section indexes -#define SHN_UNDEF 0 -#define SHN_LORESERVE 0xFF00 -#define SHN_LOPROC 0xFF00 -#define SHN_HIPROC 0xFF1F -#define SHN_ABS 0xFFF1 -#define SHN_COMMON 0xFFF2 -#define SHN_HIRESERVE 0xFFFF - // Section types #define SHT_NULL 0 #define SHT_PROGBITS 1 @@ -91,9 +72,6 @@ enum ElfMachine { #define SHT_LOUSER 0x80000000 #define SHT_HIUSER 0xFFFFFFFF -// Custom section types -#define SHT_PSPREL 0x700000a0 - // Section flags enum ElfSectionFlags { @@ -103,38 +81,6 @@ enum ElfSectionFlags SHF_MASKPROC = 0xF0000000, }; -// Symbol binding -#define STB_LOCAL 0 -#define STB_GLOBAL 1 -#define STB_WEAK 2 -#define STB_LOPROC 13 -#define STB_HIPROC 15 - -// Symbol types -#define STT_NOTYPE 0 -#define STT_OBJECT 1 -#define STT_FUNC 2 -#define STT_SECTION 3 -#define STT_FILE 4 -#define STT_LOPROC 13 -#define STT_HIPROC 15 - -// Undefined name -#define STN_UNDEF 0 - -// Relocation types -#define R_386_NONE 0 -#define R_386_32 1 -#define R_386_PC32 2 -#define R_386_GOT32 3 -#define R_386_PLT32 4 -#define R_386_COPY 5 -#define R_386_GLOB_DAT 6 -#define R_386_JMP_SLOT 7 -#define R_386_RELATIVE 8 -#define R_386_GOTOFF 9 -#define R_386_GOTPC 10 - // Segment types #define PT_NULL 0 #define PT_LOAD 1 @@ -146,39 +92,6 @@ enum ElfSectionFlags #define PT_LOPROC 0x70000000 #define PT_HIPROC 0x7FFFFFFF -// Segment flags -#define PF_X 1 -#define PF_W 2 -#define PF_R 4 - -// Dynamic Array Tags -#define DT_NULL 0 -#define DT_NEEDED 1 -#define DT_PLTRELSZ 2 -#define DT_PLTGOT 3 -#define DT_HASH 4 -#define DT_STRTAB 5 -#define DT_SYMTAB 6 -#define DT_RELA 7 -#define DT_RELASZ 8 -#define DT_RELAENT 9 -#define DT_STRSZ 10 -#define DT_SYMENT 11 -#define DT_INIT 12 -#define DT_FINI 13 -#define DT_SONAME 14 -#define DT_RPATH 15 -#define DT_SYMBOLIC 16 -#define DT_REL 17 -#define DT_RELSZ 18 -#define DT_RELENT 19 -#define DT_PLTREL 20 -#define DT_DEBUG 21 -#define DT_TEXTREL 22 -#define DT_JMPREL 23 -#define DT_LOPROC 0x70000000 -#define DT_HIPROC 0x7FFFFFFF - typedef unsigned int Elf32_Addr; typedef unsigned short Elf32_Half; typedef unsigned int Elf32_Off; @@ -239,41 +152,12 @@ struct Elf32_Sym { Elf32_Half st_shndx; }; -#define ELF32_ST_BIND(i) ((i)>>4) -#define ELF32_ST_TYPE(i) ((i)&0xf) -#define ELF32_ST_INFO(b,t) (((b)<<4)+((t)&0xf)) - // Relocation entries struct Elf32_Rel { Elf32_Addr r_offset; Elf32_Word r_info; }; -struct Elf32_Rela { - Elf32_Addr r_offset; - Elf32_Word r_info; - Elf32_Sword r_addend; -}; - -#define ELF32_R_SYM(i) ((i)>>8) -#define ELF32_R_TYPE(i) ((unsigned char)(i)) -#define ELF32_R_INFO(s,t) (((s)<<8 )+(unsigned char)(t)) - -struct Elf32_Dyn { - Elf32_Sword d_tag; - union { - Elf32_Word d_val; - Elf32_Addr d_ptr; - } d_un; -}; - -enum KnownElfTypes { - KNOWNELF_PSP = 0, - KNOWNELF_DS = 1, - KNOWNELF_GBA = 2, - KNOWNELF_GC = 3, -}; - typedef int SectionID; class ElfReader { From 79a48082e2c7b6e61f38b442a66147a4e46f2911 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 17 Jun 2014 06:30:14 -0400 Subject: [PATCH 08/15] NCCH: Changed decompression to load .code directly into memory rather than an intermediate buffer. --- src/core/loader/ncch.cpp | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index a01411e11..23864d262 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -312,20 +312,14 @@ bool Load_NCCH(std::string& filename, std::string* error_string) { // Load compressed executable... if (i == 0 && is_compressed) { - u32 decompressed_size = LZSS_GetDecompressedSize(buffer, + u32 decompressed_size = LZSS_GetDecompressedSize(buffer, exefs_header.section[i].size); - u8* decompressed_buffer = new u8[decompressed_size]; - if (!LZSS_Decompress(buffer, exefs_header.section[i].size, decompressed_buffer, + if (!LZSS_Decompress(buffer, exefs_header.section[i].size, + Memory::GetPointer(exheader_header.codeset_info.text.address), decompressed_size, error_string)) { return false; } - // Load .code section into memory... - LoadBuffer(exheader_header.codeset_info.text.address, decompressed_buffer, - decompressed_size); - - delete[] decompressed_buffer; - // Load uncompressed executable... } else { // Load .code section into memory... From 7889cafc76ac99b8509fa3cd1558a09f8a7e5f91 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 18 Jun 2014 18:58:09 -0400 Subject: [PATCH 09/15] 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. --- src/citra/citra.cpp | 25 ++- src/citra_qt/main.cpp | 7 +- src/core/loader/elf.cpp | 318 ++++++++++++++++++++++++++++++------- src/core/loader/elf.h | 232 ++------------------------- src/core/loader/loader.cpp | 63 ++++---- src/core/loader/loader.h | 103 ++++++++++-- src/core/loader/ncch.cpp | 312 +++++++++++++----------------------- src/core/loader/ncch.h | 181 ++++++++++++++++++++- 8 files changed, 690 insertions(+), 551 deletions(-) diff --git a/src/citra/citra.cpp b/src/citra/citra.cpp index f6bb10f29..036af3735 100644 --- a/src/citra/citra.cpp +++ b/src/citra/citra.cpp @@ -16,28 +16,21 @@ /// Application entry point int __cdecl main(int argc, char **argv) { - std::string program_dir = File::GetCurrentDir(); - LogManager::Init(); + if (argc < 2) { + ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); + return -1; + } + + std::string boot_filename = argv[1]; EmuWindow_GLFW* emu_window = new EmuWindow_GLFW; System::Init(emu_window); - std::string boot_filename; - - if (argc < 2) { - ERROR_LOG(BOOT, "Failed to load ROM: No ROM specified"); - } - else { - boot_filename = argv[1]; - } - std::string error_str; - - bool res = Loader::LoadFile(boot_filename, &error_str); - - if (!res) { - ERROR_LOG(BOOT, "Failed to load ROM: %s", error_str.c_str()); + if (Loader::ResultStatus::Success != Loader::LoadFile(boot_filename)) { + ERROR_LOG(BOOT, "Failed to load ROM!"); + return -1; } Core::RunLoop(); diff --git a/src/citra_qt/main.cpp b/src/citra_qt/main.cpp index cff0b6a2a..0bcce7d16 100644 --- a/src/citra_qt/main.cpp +++ b/src/citra_qt/main.cpp @@ -135,11 +135,8 @@ void GMainWindow::BootGame(const char* filename) // Load a game or die... std::string boot_filename = filename; - std::string error_str; - bool res = Loader::LoadFile(boot_filename, &error_str); - - if (!res) { - ERROR_LOG(BOOT, "Failed to load ROM: %s", error_str.c_str()); + if (Loader::ResultStatus::Success != Loader::LoadFile(boot_filename)) { + ERROR_LOG(BOOT, "Failed to load ROM!"); } disasmWidget->Init(); 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 @@ // Refer to the license.txt file included. #include +#include #include "common/common.h" #include "common/file_util.h" @@ -12,6 +13,220 @@ #include "core/loader/elf.h" #include "core/hle/kernel/kernel.h" +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ELF Header Constants + +// File type +enum ElfType { + ET_NONE = 0, + ET_REL = 1, + ET_EXEC = 2, + ET_DYN = 3, + ET_CORE = 4, + ET_LOPROC = 0xFF00, + ET_HIPROC = 0xFFFF, +}; + +// Machine/Architecture +enum ElfMachine { + EM_NONE = 0, + EM_M32 = 1, + EM_SPARC = 2, + EM_386 = 3, + EM_68K = 4, + EM_88K = 5, + EM_860 = 7, + EM_MIPS = 8 +}; + +// File version +#define EV_NONE 0 +#define EV_CURRENT 1 + +// Identification index +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 +#define EI_PAD 7 +#define EI_NIDENT 16 + +// Magic number +#define ELFMAG0 0x7F +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' + +// Sections constants + +// Section types +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7FFFFFFF +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0xFFFFFFFF + +// Section flags +enum ElfSectionFlags +{ + SHF_WRITE = 0x1, + SHF_ALLOC = 0x2, + SHF_EXECINSTR = 0x4, + SHF_MASKPROC = 0xF0000000, +}; + +// Segment types +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7FFFFFFF + +typedef unsigned int Elf32_Addr; +typedef unsigned short Elf32_Half; +typedef unsigned int Elf32_Off; +typedef signed int Elf32_Sword; +typedef unsigned int Elf32_Word; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ELF file header + +struct Elf32_Ehdr { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +}; + +// Section header +struct Elf32_Shdr { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +}; + +// Segment header +struct Elf32_Phdr { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +}; + +// Symbol table entry +struct Elf32_Sym { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +}; + +// Relocation entries +struct Elf32_Rel { + Elf32_Addr r_offset; + Elf32_Word r_info; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ElfReader class + +typedef int SectionID; + +class ElfReader { +private: + char *base; + u32 *base32; + + Elf32_Ehdr *header; + Elf32_Phdr *segments; + Elf32_Shdr *sections; + + u32 *sectionAddrs; + bool relocate; + u32 entryPoint; + +public: + ElfReader(void *ptr); + ~ElfReader() { } + + u32 Read32(int off) const { return base32[off >> 2]; } + + // Quick accessors + ElfType GetType() const { return (ElfType)(header->e_type); } + ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } + u32 GetEntryPoint() const { return entryPoint; } + u32 GetFlags() const { return (u32)(header->e_flags); } + bool LoadInto(u32 vaddr); + bool LoadSymbols(); + + int GetNumSegments() const { return (int)(header->e_phnum); } + int GetNumSections() const { return (int)(header->e_shnum); } + const u8 *GetPtr(int offset) const { return (u8*)base + offset; } + const char *GetSectionName(int section) const; + const u8 *GetSectionDataPtr(int section) const { + if (section < 0 || section >= header->e_shnum) + return nullptr; + if (sections[section].sh_type != SHT_NOBITS) + return GetPtr(sections[section].sh_offset); + else + return nullptr; + } + bool IsCodeSection(int section) const { + return sections[section].sh_type == SHT_PROGBITS; + } + const u8 *GetSegmentPtr(int segment) { + return GetPtr(segments[segment].p_offset); + } + u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; } + int GetSectionSize(SectionID section) const { return sections[section].sh_size; } + SectionID GetSectionByName(const char *name, int firstSection = 0) const; //-1 for not found + + bool DidRelocate() { + return relocate; + } +}; + ElfReader::ElfReader(void *ptr) { base = (char*)ptr; base32 = (u32 *)ptr; @@ -29,28 +244,25 @@ const char *ElfReader::GetSectionName(int section) const { if (sections[section].sh_type == SHT_NULL) return nullptr; - int nameOffset = sections[section].sh_name; + int name_offset = sections[section].sh_name; char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); if (ptr) - return ptr + nameOffset; - else - return nullptr; + return ptr + name_offset; + + return nullptr; } bool ElfReader::LoadInto(u32 vaddr) { DEBUG_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); // Should we relocate? - bRelocate = (header->e_type != ET_EXEC); + relocate = (header->e_type != ET_EXEC); - if (bRelocate) - { + if (relocate) { DEBUG_LOG(MASTER_LOG, "Relocatable module"); entryPoint += vaddr; - } - else - { + } else { DEBUG_LOG(MASTER_LOG, "Prerelocated executable"); } @@ -58,17 +270,14 @@ bool ElfReader::LoadInto(u32 vaddr) { // First pass : Get the bits into RAM u32 segmentVAddr[32]; + u32 baseAddress = relocate ? vaddr : 0; - u32 baseAddress = bRelocate ? vaddr : 0; - - for (int i = 0; i < header->e_phnum; i++) - { + for (int i = 0; i < header->e_phnum; i++) { Elf32_Phdr *p = segments + i; INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); - if (p->p_type == PT_LOAD) - { + if (p->p_type == PT_LOAD) { segmentVAddr[i] = baseAddress + p->p_vaddr; u32 writeAddr = segmentVAddr[i]; @@ -78,27 +287,19 @@ bool ElfReader::LoadInto(u32 vaddr) { u32 dstSize = p->p_memsz; u32 *s = (u32*)src; u32 *d = (u32*)dst; - for (int j = 0; j < (int)(srcSize + 3) / 4; j++) - { - *d++ = /*_byteswap_ulong*/(*s++); - } - if (srcSize < dstSize) - { - //memset(dst + srcSize, 0, dstSize-srcSize); //zero out bss + for (int j = 0; j < (int)(srcSize + 3) / 4; j++) { + *d++ = (*s++); } INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); } } - INFO_LOG(MASTER_LOG, "Done loading."); return true; } -SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const -{ - for (int i = firstSection; i < header->e_shnum; i++) - { +SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const { + for (int i = firstSection; i < header->e_shnum; i++) { const char *secname = GetSectionName(i); if (secname != nullptr && strcmp(name, secname) == 0) @@ -107,25 +308,21 @@ SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const return -1; } -bool ElfReader::LoadSymbols() -{ +bool ElfReader::LoadSymbols() { bool hasSymbols = false; SectionID sec = GetSectionByName(".symtab"); - if (sec != -1) - { + if (sec != -1) { int stringSection = sections[sec].sh_link; const char *stringBase = (const char *)GetSectionDataPtr(stringSection); //We have a symbol table! Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); - for (int sym = 0; sym < numSymbols; sym++) - { + for (int sym = 0; sym < numSymbols; sym++) { int size = symtab[sym].st_size; if (size == 0) continue; - // int bind = symtab[sym].st_info >> 4; int type = symtab[sym].st_info & 0xF; const char *name = stringBase + symtab[sym].st_name; @@ -144,42 +341,41 @@ bool ElfReader::LoadSymbols() namespace Loader { +/// AppLoader_ELF constructor +AppLoader_ELF::AppLoader_ELF(std::string& filename) : is_loaded(false) { + this->filename = filename; +} + +/// AppLoader_NCCH destructor +AppLoader_ELF::~AppLoader_ELF() { +} + /** - * Loads an ELF file - * @param filename String filename of ELF file + * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) * @param error_string Pointer to string to put error message if an error has occurred + * @todo Move NCSD parsing out of here and create a separate function for loading these * @return True on success, otherwise false */ -bool Load_ELF(std::string& filename, std::string* error_string) { - std::string full_path = filename; - std::string path, file, extension; - SplitPath(ReplaceAll(full_path, "\\", "/"), &path, &file, &extension); -#if EMU_PLATFORM == PLATFORM_WINDOWS - path = ReplaceAll(path, "/", "\\"); -#endif - File::IOFile f(filename, "rb"); +const ResultStatus AppLoader_ELF::Load() { + INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str()); - if (f.IsOpen()) { - u32 size = (u32)f.GetSize(); - u8* buffer = new u8[size]; - ElfReader* elf_reader = NULL; + if (is_loaded) + return ResultStatus::ErrorAlreadyLoaded; - f.ReadBytes(buffer, size); + File::IOFile file(filename, "rb"); - elf_reader = new ElfReader(buffer); - elf_reader->LoadInto(0x00100000); + if (file.IsOpen()) { + u32 size = (u32)file.GetSize(); + std::unique_ptr buffer(new u8[size]); + file.ReadBytes(&buffer[0], size); - Kernel::LoadExec(elf_reader->GetEntryPoint()); - - delete[] buffer; - delete elf_reader; + ElfReader elf_reader(&buffer[0]); + elf_reader.LoadInto(0x00100000); + Kernel::LoadExec(elf_reader.GetEntryPoint()); } else { - *error_string = "Unable to open ELF file!"; - return false; + return ResultStatus::Error; } - f.Close(); - - return true; + return ResultStatus::Success; } } // namespace Loader 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 @@ #pragma once #include "common/common_types.h" - -// ELF Header Constants - -// File type -enum ElfType { - ET_NONE = 0, - ET_REL = 1, - ET_EXEC = 2, - ET_DYN = 3, - ET_CORE = 4, - ET_LOPROC = 0xFF00, - ET_HIPROC = 0xFFFF, -}; - -// Machine/Architecture -enum ElfMachine { - EM_NONE = 0, - EM_M32 = 1, - EM_SPARC = 2, - EM_386 = 3, - EM_68K = 4, - EM_88K = 5, - EM_860 = 7, - EM_MIPS = 8 -}; - -// File version -#define EV_NONE 0 -#define EV_CURRENT 1 - -// Identification index -#define EI_MAG0 0 -#define EI_MAG1 1 -#define EI_MAG2 2 -#define EI_MAG3 3 -#define EI_CLASS 4 -#define EI_DATA 5 -#define EI_VERSION 6 -#define EI_PAD 7 -#define EI_NIDENT 16 - -// Magic number -#define ELFMAG0 0x7F -#define ELFMAG1 'E' -#define ELFMAG2 'L' -#define ELFMAG3 'F' - -// Sections constants - -// Section types -#define SHT_NULL 0 -#define SHT_PROGBITS 1 -#define SHT_SYMTAB 2 -#define SHT_STRTAB 3 -#define SHT_RELA 4 -#define SHT_HASH 5 -#define SHT_DYNAMIC 6 -#define SHT_NOTE 7 -#define SHT_NOBITS 8 -#define SHT_REL 9 -#define SHT_SHLIB 10 -#define SHT_DYNSYM 11 -#define SHT_LOPROC 0x70000000 -#define SHT_HIPROC 0x7FFFFFFF -#define SHT_LOUSER 0x80000000 -#define SHT_HIUSER 0xFFFFFFFF - -// Section flags -enum ElfSectionFlags -{ - SHF_WRITE = 0x1, - SHF_ALLOC = 0x2, - SHF_EXECINSTR = 0x4, - SHF_MASKPROC = 0xF0000000, -}; - -// Segment types -#define PT_NULL 0 -#define PT_LOAD 1 -#define PT_DYNAMIC 2 -#define PT_INTERP 3 -#define PT_NOTE 4 -#define PT_SHLIB 5 -#define PT_PHDR 6 -#define PT_LOPROC 0x70000000 -#define PT_HIPROC 0x7FFFFFFF - -typedef unsigned int Elf32_Addr; -typedef unsigned short Elf32_Half; -typedef unsigned int Elf32_Off; -typedef signed int Elf32_Sword; -typedef unsigned int Elf32_Word; - -// ELF file header -struct Elf32_Ehdr { - unsigned char e_ident[EI_NIDENT]; - Elf32_Half e_type; - Elf32_Half e_machine; - Elf32_Word e_version; - Elf32_Addr e_entry; - Elf32_Off e_phoff; - Elf32_Off e_shoff; - Elf32_Word e_flags; - Elf32_Half e_ehsize; - Elf32_Half e_phentsize; - Elf32_Half e_phnum; - Elf32_Half e_shentsize; - Elf32_Half e_shnum; - Elf32_Half e_shstrndx; -}; - -// Section header -struct Elf32_Shdr { - Elf32_Word sh_name; - Elf32_Word sh_type; - Elf32_Word sh_flags; - Elf32_Addr sh_addr; - Elf32_Off sh_offset; - Elf32_Word sh_size; - Elf32_Word sh_link; - Elf32_Word sh_info; - Elf32_Word sh_addralign; - Elf32_Word sh_entsize; -}; - -// Segment header -struct Elf32_Phdr { - Elf32_Word p_type; - Elf32_Off p_offset; - Elf32_Addr p_vaddr; - Elf32_Addr p_paddr; - Elf32_Word p_filesz; - Elf32_Word p_memsz; - Elf32_Word p_flags; - Elf32_Word p_align; -}; - -// Symbol table entry -struct Elf32_Sym { - Elf32_Word st_name; - Elf32_Addr st_value; - Elf32_Word st_size; - unsigned char st_info; - unsigned char st_other; - Elf32_Half st_shndx; -}; - -// Relocation entries -struct Elf32_Rel { - Elf32_Addr r_offset; - Elf32_Word r_info; -}; - -typedef int SectionID; - -class ElfReader { -private: - char *base; - u32 *base32; - - Elf32_Ehdr *header; - Elf32_Phdr *segments; - Elf32_Shdr *sections; - - u32 *sectionAddrs; - bool bRelocate; - u32 entryPoint; - -public: - ElfReader(void *ptr); - ~ElfReader() { } - - u32 Read32(int off) const { return base32[off >> 2]; } - - // Quick accessors - ElfType GetType() const { return (ElfType)(header->e_type); } - ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } - u32 GetEntryPoint() const { return entryPoint; } - u32 GetFlags() const { return (u32)(header->e_flags); } - bool LoadInto(u32 vaddr); - bool LoadSymbols(); - - int GetNumSegments() const { return (int)(header->e_phnum); } - int GetNumSections() const { return (int)(header->e_shnum); } - const u8 *GetPtr(int offset) const { return (u8*)base + offset; } - const char *GetSectionName(int section) const; - const u8 *GetSectionDataPtr(int section) const { - if (section < 0 || section >= header->e_shnum) - return nullptr; - if (sections[section].sh_type != SHT_NOBITS) - return GetPtr(sections[section].sh_offset); - else - return nullptr; - } - bool IsCodeSection(int section) const { - return sections[section].sh_type == SHT_PROGBITS; - } - const u8 *GetSegmentPtr(int segment) { - return GetPtr(segments[segment].p_offset); - } - u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; } - int GetSectionSize(SectionID section) const { return sections[section].sh_size; } - SectionID GetSectionByName(const char *name, int firstSection = 0) const; //-1 for not found - - bool DidRelocate() { - return bRelocate; - } -}; +#include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace namespace Loader { -/** - * Loads an ELF file - * @param filename String filename of ELF file - * @param error_string Pointer to string to put error message if an error has occurred - * @return True on success, otherwise false - */ -bool Load_ELF(std::string& filename, std::string* error_string); +/// Loads an ELF/AXF file +class AppLoader_ELF : public AppLoader { +public: + AppLoader_ELF(std::string& filename); + ~AppLoader_ELF(); + + /** + * Load the bootable file + * @return ResultStatus result of function + */ + const ResultStatus Load(); + +private: + std::string filename; + bool is_loaded; +}; } // namespace Loader 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 @@ // Licensed under GPLv2 // Refer to the license.txt file included. +#include + #include "core/loader/loader.h" #include "core/loader/elf.h" #include "core/loader/ncch.h" @@ -16,59 +18,60 @@ namespace Loader { * @todo (ShizZy) this function sucks... make it actually check file contents etc. * @return FileType of file */ -FileType IdentifyFile(std::string &filename) { +const FileType IdentifyFile(const std::string &filename) { if (filename.size() == 0) { ERROR_LOG(LOADER, "invalid filename %s", filename.c_str()); - return FILETYPE_ERROR; + return FileType::Error; } std::string extension = filename.size() >= 5 ? filename.substr(filename.size() - 4) : ""; if (!strcasecmp(extension.c_str(), ".elf")) { - return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p + return FileType::ELF; // TODO(bunnei): Do some filetype checking :p } else if (!strcasecmp(extension.c_str(), ".axf")) { - return FILETYPE_CTR_ELF; // TODO(bunnei): Do some filetype checking :p + return FileType::ELF; // TODO(bunnei): Do some filetype checking :p } else if (!strcasecmp(extension.c_str(), ".cxi")) { - return FILETYPE_CTR_CXI; // TODO(bunnei): Do some filetype checking :p + return FileType::CXI; // TODO(bunnei): Do some filetype checking :p } else if (!strcasecmp(extension.c_str(), ".cci")) { - return FILETYPE_CTR_CCI; // TODO(bunnei): Do some filetype checking :p + return FileType::CCI; // TODO(bunnei): Do some filetype checking :p } - return FILETYPE_UNKNOWN; + return FileType::Unknown; } /** * Identifies and loads a bootable file * @param filename String filename of bootable file - * @param error_string Point to string to put error message if an error has occurred - * @return True on success, otherwise false + * @return ResultStatus result of function */ -bool LoadFile(std::string &filename, std::string *error_string) { - INFO_LOG(LOADER, "Identifying file..."); +const ResultStatus LoadFile(std::string& filename) { + INFO_LOG(LOADER, "Loading file %s...", filename.c_str()); - // Note that this can modify filename! switch (IdentifyFile(filename)) { - case FILETYPE_CTR_ELF: - return Loader::Load_ELF(filename, error_string); - - case FILETYPE_CTR_CXI: - case FILETYPE_CTR_CCI: - return Loader::Load_NCCH(filename, error_string); - - case FILETYPE_ERROR: - ERROR_LOG(LOADER, "Could not read file"); - *error_string = "Error reading file"; - break; - - case FILETYPE_UNKNOWN: - default: - ERROR_LOG(LOADER, "Failed to identify file"); - *error_string = " Failed to identify file"; - break; + // Standard ELF file format... + case FileType::ELF: { + return AppLoader_ELF(filename).Load(); } - return false; + + // NCCH/NCSD container formats... + case FileType::CXI: + case FileType::CCI: { + return AppLoader_NCCH(filename).Load(); + } + + // Error occurred durring IdentifyFile... + case FileType::Error: + + // IdentifyFile could know identify file type... + case FileType::Unknown: + + default: + return ResultStatus::ErrorInvalidFormat; + } + + return ResultStatus::Error; } } // namespace Loader 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 @@ #pragma once +#include + #include "common/common.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -11,16 +13,94 @@ namespace Loader { -enum FileType { - FILETYPE_ERROR, +/// File types supported by CTR +enum class FileType { + Error, + Unknown, + CCI, + CXI, + CIA, + ELF, +}; - FILETYPE_CTR_CCI, - FILETYPE_CTR_CIA, - FILETYPE_CTR_CXI, - FILETYPE_CTR_ELF, - FILETYPE_CTR_BIN, +/// Return type for functions in Loader namespace +enum class ResultStatus { + Success, + Error, + ErrorInvalidFormat, + ErrorNotImplemented, + ErrorNotLoaded, + ErrorAlreadyLoaded, +}; - FILETYPE_UNKNOWN +/// Interface for loading an application +class AppLoader : NonCopyable { +public: + AppLoader() { } + virtual ~AppLoader() { } + + /** + * Load the application + * @return ResultStatus result of function + */ + virtual const ResultStatus Load() = 0; + + /** + * Get the code (typically .code section) of the application + * @param error ResultStatus result of function + * @return Reference to code buffer + */ + virtual const std::vector& 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& 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& 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& 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& GetRomFs(ResultStatus error) const { + error = ResultStatus::ErrorNotImplemented; + return romfs; + } + +protected: + std::vector code; ///< ExeFS .code section + std::vector icon; ///< ExeFS .icon section + std::vector banner; ///< ExeFS .banner section + std::vector logo; ///< ExeFS .logo section + std::vector romfs; ///< RomFs archive }; /** @@ -28,14 +108,13 @@ enum FileType { * @param filename String filename of bootable file * @return FileType of file */ -FileType IdentifyFile(std::string &filename); +const FileType IdentifyFile(const std::string &filename); /** * Identifies and loads a bootable file * @param filename String filename of bootable file - * @param error_string Point to string to put error message if an error has occurred - * @return True on success, otherwise false + * @return ResultStatus result of function */ -bool LoadFile(std::string &filename, std::string *error_string); +const ResultStatus LoadFile(std::string& filename); } // namespace 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,142 +2,14 @@ // Licensed under GPLv2 // Refer to the license.txt file included. +#include + #include "common/file_util.h" #include "core/loader/ncch.h" #include "core/hle/kernel/kernel.h" #include "core/mem_map.h" -//////////////////////////////////////////////////////////////////////////////////////////////////// -/// NCCH header (Note: "NCCH" appears to be a publically unknown acronym) - -struct NCCH_Header { - u8 signature[0x100]; - char magic[4]; - u32 content_size; - u8 partition_id[8]; - u16 maker_code; - u16 version; - u8 reserved_0[4]; - u8 program_id[8]; - u8 temp_flag; - u8 reserved_1[0x2f]; - u8 product_code[0x10]; - u8 extended_header_hash[0x20]; - u32 extended_header_size; - u8 reserved_2[4]; - u8 flags[8]; - u32 plain_region_offset; - u32 plain_region_size; - u8 reserved_3[8]; - u32 exefs_offset; - u32 exefs_size; - u32 exefs_hash_region_size; - u8 reserved_4[4]; - u32 romfs_offset; - u32 romfs_size; - u32 romfs_hash_region_size; - u8 reserved_5[4]; - u8 exefs_super_block_hash[0x20]; - u8 romfs_super_block_hash[0x20]; -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ExeFS (executable file system) headers - -typedef struct { - char name[8]; - u32 offset; - u32 size; -} ExeFs_SectionHeader; - -typedef struct { - ExeFs_SectionHeader section[8]; - u8 reserved[0x80]; - u8 hashes[8][0x20]; -} ExeFs_Header; - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// ExHeader (executable file system header) headers - -struct ExHeader_SystemInfoFlags{ - u8 reserved[5]; - u8 flag; - u8 remaster_version[2]; -} exheader_systeminfoflags; - -struct ExHeader_CodeSegmentInfo{ - u32 address; - u32 num_max_pages; - u32 code_size; -} exheader_codesegmentinfo; - -struct ExHeader_CodeSetInfo { - u8 name[8]; - ExHeader_SystemInfoFlags flags; - ExHeader_CodeSegmentInfo text; - u8 stacksize[4]; - ExHeader_CodeSegmentInfo ro; - u8 reserved[4]; - ExHeader_CodeSegmentInfo data; - u8 bsssize[4]; -}; - -struct ExHeader_DependencyList{ - u8 program_id[0x30][8]; -}; - -struct ExHeader_SystemInfo{ - u32 save_data_size; - u8 reserved[4]; - u8 jump_id[8]; - u8 reserved_2[0x30]; -}; - -struct ExHeader_StorageInfo{ - u8 ext_save_data_id[8]; - u8 system_save_data_id[8]; - u8 reserved[8]; - u8 access_info[7]; - u8 other_attributes; -}; - -struct ExHeader_ARM11_SystemLocalCaps{ - u8 program_id[8]; - u8 flags[8]; - u8 resource_limit_descriptor[0x10][2]; - ExHeader_StorageInfo storage_info; - u8 service_access_control[0x20][8]; - u8 reserved[0x1f]; - u8 resource_limit_category; -}; - -struct ExHeader_ARM11_KernelCaps{ - u8 descriptors[28][4]; - u8 reserved[0x10]; -}; - -struct ExHeader_ARM9_AccessControl{ - u8 descriptors[15]; - u8 descversion; -}; - -struct ExHeader_Header{ - ExHeader_CodeSetInfo codeset_info; - ExHeader_DependencyList dependency_list; - ExHeader_SystemInfo system_info; - ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; - ExHeader_ARM11_KernelCaps arm11_kernel_caps; - ExHeader_ARM9_AccessControl arm9_access_control; - struct { - u8 signature[0x100]; - u8 ncch_public_key_modulus[0x100]; - ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; - ExHeader_ARM11_KernelCaps arm11_kernel_caps; - ExHeader_ARM9_AccessControl arm9_access_control; - } access_desc; -}; - //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace @@ -163,11 +35,9 @@ u32 LZSS_GetDecompressedSize(u8* buffer, u32 size) { * @param compressed_size Size of compressed buffer * @param decompressed Decompressed buffer * @param decompressed_size Size of decompressed buffer - * @param error_string String populated with error message on failure * @return True on success, otherwise false */ -bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size, - std::string* error_string) { +bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 decompressed_size) { u8* footer = compressed + compressed_size - 8; u32 buffer_top_and_bottom = *(u32*)footer; u32 i, j; @@ -191,8 +61,8 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 break; if(control & 0x80) { + // Check if compression is out of bounds if(index < 2) { - *error_string = "Compression out of bounds"; return false; } index -= 2; @@ -202,22 +72,22 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 segment_offset &= 0x0FFF; segment_offset += 2; + // Check if compression is out of bounds if(out < segment_size) { - *error_string = "Compression out of bounds"; return false; } for(j = 0; j < segment_size; j++) { u8 data; + // Check if compression is out of bounds if(out + segment_offset >= decompressed_size) { - *error_string = "Compression out of bounds"; return false; } data = decompressed[out + segment_offset]; decompressed[--out] = data; } } else { + // Check if compression is out of bounds if(out < 1) { - *error_string = "Compression out of bounds"; return false; } decompressed[--out] = compressed[--index]; @@ -228,34 +98,96 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 return true; } -/** - * Load a data buffer into memory at the specified address - * @param addr Address to load memory into - * @param buffer Buffer of data to load into memory - * @param size Size of data to load into memory - * @todo Perhaps move this code somewhere more generic? - */ -void LoadBuffer(const u32 addr, const u8* const buffer, const int size) { - u32 *dst = (u32*)Memory::GetPointer(addr); - u32 *src = (u32*)buffer; - int size_aligned = (size + 3) / 4; +//////////////////////////////////////////////////////////////////////////////////////////////////// +// AppLoader_NCCH class - for (int j = 0; j < size_aligned; j++) { - *dst++ = (*src++); - } - return; +/// AppLoader_NCCH constructor +AppLoader_NCCH::AppLoader_NCCH(std::string& filename) { + this->filename = filename; + is_loaded = false; + is_compressed = false; + entry_point = 0; + ncch_offset = 0; + exefs_offset = 0; +} + +/// AppLoader_NCCH destructor +AppLoader_NCCH::~AppLoader_NCCH() { } +/** + * Loads .code section into memory for booting + * @return ResultStatus result of function + */ +const ResultStatus AppLoader_NCCH::LoadExec() const { + if (!is_loaded) + return ResultStatus::ErrorNotLoaded; + + for (std::vector::size_type i = 0; i != code.size(); i++) { + Memory::Write8(entry_point + i, code[i]); + } + Kernel::LoadExec(entry_point); + + return ResultStatus::Success; +} + +/** + * Reads an application section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) + * @param file Handle to file to read from + * @param name Name of section to read out of NCCH file + * @param buffer Buffer to read section into. + */ +const ResultStatus AppLoader_NCCH::LoadSection(File::IOFile& file, const char* name, + std::vector& buffer) { + // Iterate through the ExeFs archive until we find the .code file... + for (int i = 0; i < kExeFs_MaxSections; i++) { + INFO_LOG(LOADER, "ExeFS section %d:", i); + INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); + INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); + INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size); + + // Load the .code section (executable code)... + if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { + s64 section_offset = (exefs_header.section[i].offset + exefs_offset + + sizeof(ExeFs_Header) + ncch_offset); + file.Seek(section_offset, 0); + + // Section is compressed... + if (i == 0 && is_compressed) { + // Read compressed .code section... + std::unique_ptr temp_buffer(new u8[exefs_header.section[i].size]); + file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size); + + // Decompress .code section... + u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], exefs_header.section[i].size); + buffer.resize(decompressed_size); + if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0], + decompressed_size)) { + return ResultStatus::ErrorInvalidFormat; + } + // Section is uncompressed... + } else { + buffer.resize(exefs_header.section[i].size); + file.ReadBytes(&buffer[0], exefs_header.section[i].size); + } + return ResultStatus::Success; + } + } + return ResultStatus::Error; +} + /** * Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) - * @param filename String filename of NCCH file * @param error_string Pointer to string to put error message if an error has occurred * @todo Move NCSD parsing out of here and create a separate function for loading these * @return True on success, otherwise false */ -bool Load_NCCH(std::string& filename, std::string* error_string) { +const ResultStatus AppLoader_NCCH::Load() { INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); + if (is_loaded) + return ResultStatus::ErrorAlreadyLoaded; + File::IOFile file(filename, "rb"); if (file.IsOpen()) { @@ -263,80 +195,50 @@ bool Load_NCCH(std::string& filename, std::string* error_string) { file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); // Skip NCSD header and load first NCCH (NCSD is just a container of NCCH files)... - int ncch_off = 0; // Offset to NCCH header, can be 0 or after NCSD header - if (memcmp(&ncch_header.magic, "NCSD", 4) == 0) { + if (0 == memcmp(&ncch_header.magic, "NCSD", 4)) { WARN_LOG(LOADER, "Only loading the first (bootable) NCCH within the NCSD file!"); - ncch_off = 0x4000; - file.Seek(ncch_off, 0); + ncch_offset = 0x4000; + file.Seek(ncch_offset, 0); file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); } + // Verify we are loading the correct file type... - if (memcmp(&ncch_header.magic, "NCCH", 4) != 0) { - *error_string = "Invalid NCCH magic number (likely incorrect file type)"; - return false; - } + if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) + return ResultStatus::ErrorInvalidFormat; + // Read ExHeader - ExHeader_Header exheader_header; file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); - bool is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; + is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; + entry_point = exheader_header.codeset_info.text.address; INFO_LOG(LOADER, "Name: %s", exheader_header.codeset_info.name); INFO_LOG(LOADER, "Code compressed: %s", is_compressed ? "yes" : "no"); + INFO_LOG(LOADER, "Entry point: 0x%08X", entry_point); // Read ExeFS - u32 exefs_offset = ncch_header.exefs_offset * kExeFs_BlockSize; + exefs_offset = ncch_header.exefs_offset * kExeFs_BlockSize; u32 exefs_size = ncch_header.exefs_size * kExeFs_BlockSize; INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset); INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size); - ExeFs_Header exefs_header; - file.Seek(exefs_offset + ncch_off, 0); + file.Seek(exefs_offset + ncch_offset, 0); file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); - // Iterate through the ExeFs archive until we find the .code file... - for (int i = 0; i < kExeFs_MaxSections; i++) { - INFO_LOG(LOADER, "ExeFS section %d:", i); - INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); - INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); - INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size); + // TODO(bunnei): Check ResultStatus here... + LoadSection(file, ".code", code); + LoadSection(file, ".icon", icon); + LoadSection(file, ".banner", banner); + LoadSection(file, ".logo", logo); - // Load the .code section (executable code)... - if (strcmp((char*) exefs_header.section[i].name, ".code") == 0) { - file.Seek(exefs_header.section[i].offset + exefs_offset + sizeof(ExeFs_Header) + - ncch_off, 0); + is_loaded = true; // Set state to loaded - u8* buffer = new u8[exefs_header.section[i].size]; - file.ReadBytes(buffer, exefs_header.section[i].size); + LoadExec(); // Load the executable into memory for booting - // Load compressed executable... - if (i == 0 && is_compressed) { - u32 decompressed_size = LZSS_GetDecompressedSize(buffer, - exefs_header.section[i].size); - - if (!LZSS_Decompress(buffer, exefs_header.section[i].size, - Memory::GetPointer(exheader_header.codeset_info.text.address), - decompressed_size, error_string)) { - return false; - } - // Load uncompressed executable... - } else { - // Load .code section into memory... - LoadBuffer(exheader_header.codeset_info.text.address, buffer, - exefs_header.section[i].size); - } - delete[] buffer; - - // Setup kernel emulation to boot .code section... - Kernel::LoadExec(exheader_header.codeset_info.text.address); - - // No need to load the other files from ExeFS until we do something with them... - return true; - } - } + return ResultStatus::Success; } - return false; + return ResultStatus::Error; } } // namespace Loader 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 @@ #pragma once #include "common/common.h" +#include "common/file_util.h" + +#include "core/loader/loader.h" //////////////////////////////////////////////////////////////////////////////////////////////////// +/// NCCH header (Note: "NCCH" appears to be a publically unknown acronym) + +struct NCCH_Header { + u8 signature[0x100]; + char magic[4]; + u32 content_size; + u8 partition_id[8]; + u16 maker_code; + u16 version; + u8 reserved_0[4]; + u8 program_id[8]; + u8 temp_flag; + u8 reserved_1[0x2f]; + u8 product_code[0x10]; + u8 extended_header_hash[0x20]; + u32 extended_header_size; + u8 reserved_2[4]; + u8 flags[8]; + u32 plain_region_offset; + u32 plain_region_size; + u8 reserved_3[8]; + u32 exefs_offset; + u32 exefs_size; + u32 exefs_hash_region_size; + u8 reserved_4[4]; + u32 romfs_offset; + u32 romfs_size; + u32 romfs_hash_region_size; + u8 reserved_5[4]; + u8 exefs_super_block_hash[0x20]; + u8 romfs_super_block_hash[0x20]; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ExeFS (executable file system) headers + +typedef struct { + char name[8]; + u32 offset; + u32 size; +} ExeFs_SectionHeader; + +typedef struct { + ExeFs_SectionHeader section[8]; + u8 reserved[0x80]; + u8 hashes[8][0x20]; +} ExeFs_Header; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// ExHeader (executable file system header) headers + +struct ExHeader_SystemInfoFlags{ + u8 reserved[5]; + u8 flag; + u8 remaster_version[2]; +}; + +struct ExHeader_CodeSegmentInfo{ + u32 address; + u32 num_max_pages; + u32 code_size; +}; + +struct ExHeader_CodeSetInfo { + u8 name[8]; + ExHeader_SystemInfoFlags flags; + ExHeader_CodeSegmentInfo text; + u8 stacksize[4]; + ExHeader_CodeSegmentInfo ro; + u8 reserved[4]; + ExHeader_CodeSegmentInfo data; + u8 bsssize[4]; +}; + +struct ExHeader_DependencyList{ + u8 program_id[0x30][8]; +}; + +struct ExHeader_SystemInfo{ + u32 save_data_size; + u8 reserved[4]; + u8 jump_id[8]; + u8 reserved_2[0x30]; +}; + +struct ExHeader_StorageInfo{ + u8 ext_save_data_id[8]; + u8 system_save_data_id[8]; + u8 reserved[8]; + u8 access_info[7]; + u8 other_attributes; +}; + +struct ExHeader_ARM11_SystemLocalCaps{ + u8 program_id[8]; + u8 flags[8]; + u8 resource_limit_descriptor[0x10][2]; + ExHeader_StorageInfo storage_info; + u8 service_access_control[0x20][8]; + u8 reserved[0x1f]; + u8 resource_limit_category; +}; + +struct ExHeader_ARM11_KernelCaps{ + u8 descriptors[28][4]; + u8 reserved[0x10]; +}; + +struct ExHeader_ARM9_AccessControl{ + u8 descriptors[15]; + u8 descversion; +}; + +struct ExHeader_Header{ + ExHeader_CodeSetInfo codeset_info; + ExHeader_DependencyList dependency_list; + ExHeader_SystemInfo system_info; + ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; + ExHeader_ARM11_KernelCaps arm11_kernel_caps; + ExHeader_ARM9_AccessControl arm9_access_control; + struct { + u8 signature[0x100]; + u8 ncch_public_key_modulus[0x100]; + ExHeader_ARM11_SystemLocalCaps arm11_system_local_caps; + ExHeader_ARM11_KernelCaps arm11_kernel_caps; + ExHeader_ARM9_AccessControl arm9_access_control; + } access_desc; +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Loader namespace namespace Loader { -/** - * Loads an NCCH file (e.g. from a CCI or CXI) - * @param filename String filename of NCCH file - * @param error_string Pointer to string to put error message if an error has occurred - * @return True on success, otherwise false - */ -bool Load_NCCH(std::string& filename, std::string* error_string); +/// Loads an NCCH file (e.g. from a CCI, or the first NCCH in a CXI) +class AppLoader_NCCH : public AppLoader { +public: + AppLoader_NCCH(std::string& filename); + ~AppLoader_NCCH(); + + /** + * Load the application + * @return ResultStatus result of function + */ + const ResultStatus Load(); + +private: + + /** + * Reads an application section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) + * @param file Handle to file to read from + * @param name Name of section to read out of NCCH file + * @param buffer Buffer to read section into. + */ + const ResultStatus LoadSection(File::IOFile& file, const char* name, + std::vector& 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 From 3da2bc6830e05d943c4d131a3167c2df25bff344 Mon Sep 17 00:00:00 2001 From: bunnei Date: Wed, 18 Jun 2014 23:53:22 -0400 Subject: [PATCH 10/15] NCCH: Fixes reduce unnecessary logging and load logo/banner/etc. sections correctly. Loader: Added ErrorNotUsed ReturnStatus type to specify when something is not used. --- src/core/loader/loader.h | 1 + src/core/loader/ncch.cpp | 47 ++++++++++++++++++++++------------------ src/core/loader/ncch.h | 5 +++-- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 42caa29e6..38b3d4c99 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -30,6 +30,7 @@ enum class ResultStatus { ErrorInvalidFormat, ErrorNotImplemented, ErrorNotLoaded, + ErrorNotUsed, ErrorAlreadyLoaded, }; diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 765efcf65..4cf805ba0 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -15,8 +15,8 @@ namespace Loader { -const int kExeFs_MaxSections = 8; ///< Maximum number of sections (files) in an ExeFs -const int kExeFs_BlockSize = 0x200; ///< Size of ExeFS blocks (in bytes) +static const int kMaxSections = 8; ///< Maximum number of sections (files) in an ExeFs +static const int kBlockSize = 0x200; ///< Size of ExeFS blocks (in bytes) /** * Get the decompressed size of an LZSS compressed ExeFS file @@ -132,22 +132,24 @@ const ResultStatus AppLoader_NCCH::LoadExec() const { } /** - * Reads an application section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) + * Reads an application ExeFS 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, +const ResultStatus AppLoader_NCCH::LoadSectionExeFS(File::IOFile& file, const char* name, std::vector& buffer) { - // Iterate through the ExeFs archive until we find the .code file... - for (int i = 0; i < kExeFs_MaxSections; i++) { - INFO_LOG(LOADER, "ExeFS section %d:", i); - INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); - INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); - INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size); - // Load the .code section (executable code)... + // Iterate through the ExeFs archive until we find the .code file... + for (int i = 0; i < kMaxSections; i++) { + + // Load the specified section... if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { + INFO_LOG(LOADER, "ExeFS section %d:", i); + INFO_LOG(LOADER, " name: %s", exefs_header.section[i].name); + INFO_LOG(LOADER, " offset: 0x%08X", exefs_header.section[i].offset); + INFO_LOG(LOADER, " size: 0x%08X", exefs_header.section[i].size); + s64 section_offset = (exefs_header.section[i].offset + exefs_offset + sizeof(ExeFs_Header) + ncch_offset); file.Seek(section_offset, 0); @@ -173,7 +175,7 @@ const ResultStatus AppLoader_NCCH::LoadSection(File::IOFile& file, const char* n return ResultStatus::Success; } } - return ResultStatus::Error; + return ResultStatus::ErrorNotUsed; } /** @@ -206,7 +208,8 @@ const ResultStatus AppLoader_NCCH::Load() { if (0 != memcmp(&ncch_header.magic, "NCCH", 4)) return ResultStatus::ErrorInvalidFormat; - // Read ExHeader + // Read ExHeader... + file.ReadBytes(&exheader_header, sizeof(ExHeader_Header)); is_compressed = (exheader_header.codeset_info.flags.flag & 1) == 1; @@ -216,9 +219,10 @@ const ResultStatus AppLoader_NCCH::Load() { 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; + // Read ExeFS... + + exefs_offset = ncch_header.exefs_offset * kBlockSize; + u32 exefs_size = ncch_header.exefs_size * kBlockSize; INFO_LOG(LOADER, "ExeFS offset: 0x%08X", exefs_offset); INFO_LOG(LOADER, "ExeFS size: 0x%08X", exefs_size); @@ -226,11 +230,12 @@ const ResultStatus AppLoader_NCCH::Load() { 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); + // TODO(bunnei): Check ResultStatus of these... + + LoadSectionExeFS(file, ".code", code); + LoadSectionExeFS(file, "banner", banner); + LoadSectionExeFS(file, "icon", icon); + LoadSectionExeFS(file, "logo", logo); is_loaded = true; // Set state to loaded diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 3aae5417c..525a5aef5 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -159,12 +159,13 @@ public: private: /** - * Reads an application section of an NCCH file into AppLoader (e.g. .code, .logo, etc.) + * Reads an application ExeFS 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. + * @return ResultStatus result of function */ - const ResultStatus LoadSection(File::IOFile& file, const char* name, + const ResultStatus LoadSectionExeFS(File::IOFile& file, const char* name, std::vector& buffer); /** From a8c46485203d3ab00ef478bbf9daa7450df14dfd Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 19 Jun 2014 00:11:45 -0400 Subject: [PATCH 11/15] NCCH: Added RomFS loading. --- src/core/loader/ncch.cpp | 29 ++++++++++++++++++++++++++++- src/core/loader/ncch.h | 8 ++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 4cf805ba0..6423da8f9 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -178,6 +178,32 @@ const ResultStatus AppLoader_NCCH::LoadSectionExeFS(File::IOFile& file, const ch return ResultStatus::ErrorNotUsed; } +/** + * Reads RomFS of an NCCH file into AppLoader + * @param file Handle to file to read from + * @return ResultStatus result of function + */ +const ResultStatus AppLoader_NCCH::LoadRomFS(File::IOFile& file) { + // Check if the NCCH has a RomFS... + if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { + u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; + u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; + + INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset); + INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size); + + romfs.resize(romfs_size); + + file.Seek(romfs_offset, 0); + file.ReadBytes(&romfs[0], romfs_size); + + return ResultStatus::Success; + } else { + NOTICE_LOG(LOADER, "RomFS unused"); + } + return ResultStatus::ErrorNotUsed; +} + /** * 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 @@ -193,7 +219,6 @@ const ResultStatus AppLoader_NCCH::Load() { 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)... @@ -237,6 +262,8 @@ const ResultStatus AppLoader_NCCH::Load() { LoadSectionExeFS(file, "icon", icon); LoadSectionExeFS(file, "logo", logo); + LoadRomFS(file); + is_loaded = true; // Set state to loaded LoadExec(); // Load the executable into memory for booting diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 525a5aef5..939b144a6 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -168,6 +168,13 @@ private: const ResultStatus LoadSectionExeFS(File::IOFile& file, const char* name, std::vector& buffer); + /** + * Reads RomFS of an NCCH file into AppLoader + * @param file Handle to file to read from + * @return ResultStatus result of function + */ + const ResultStatus LoadRomFS(File::IOFile& file); + /** * Loads .code section into memory for booting * @return ResultStatus result of function @@ -182,6 +189,7 @@ private: u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header u32 exefs_offset; + NCCH_Header ncch_header; ExeFs_Header exefs_header; ExHeader_Header exheader_header; }; From 62b444cd17c17e6f8009d87609b620bcb51b43bd Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 19 Jun 2014 17:46:05 -0400 Subject: [PATCH 12/15] Loader: Refactored use of const. --- src/core/loader/elf.cpp | 4 ++-- src/core/loader/elf.h | 4 ++-- src/core/loader/loader.cpp | 4 ++-- src/core/loader/loader.h | 6 +++--- src/core/loader/ncch.cpp | 10 +++++----- src/core/loader/ncch.h | 11 +++++------ 6 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 065601546..d9e5e130f 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -342,7 +342,7 @@ bool ElfReader::LoadSymbols() { namespace Loader { /// AppLoader_ELF constructor -AppLoader_ELF::AppLoader_ELF(std::string& filename) : is_loaded(false) { +AppLoader_ELF::AppLoader_ELF(const std::string& filename) : is_loaded(false) { this->filename = filename; } @@ -356,7 +356,7 @@ AppLoader_ELF::~AppLoader_ELF() { * @todo Move NCSD parsing out of here and create a separate function for loading these * @return True on success, otherwise false */ -const ResultStatus AppLoader_ELF::Load() { +ResultStatus AppLoader_ELF::Load() { INFO_LOG(LOADER, "Loading ELF file %s...", filename.c_str()); if (is_loaded) diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index 3fb010113..d3cbf414d 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h @@ -15,14 +15,14 @@ namespace Loader { /// Loads an ELF/AXF file class AppLoader_ELF : public AppLoader { public: - AppLoader_ELF(std::string& filename); + AppLoader_ELF(const std::string& filename); ~AppLoader_ELF(); /** * Load the bootable file * @return ResultStatus result of function */ - const ResultStatus Load(); + ResultStatus Load(); private: std::string filename; diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index dd0863ff3..96cb81de0 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -18,7 +18,7 @@ namespace Loader { * @todo (ShizZy) this function sucks... make it actually check file contents etc. * @return FileType of file */ -const FileType IdentifyFile(const std::string &filename) { +FileType IdentifyFile(const std::string &filename) { if (filename.size() == 0) { ERROR_LOG(LOADER, "invalid filename %s", filename.c_str()); return FileType::Error; @@ -45,7 +45,7 @@ const FileType IdentifyFile(const std::string &filename) { * @param filename String filename of bootable file * @return ResultStatus result of function */ -const ResultStatus LoadFile(std::string& filename) { +ResultStatus LoadFile(const std::string& filename) { INFO_LOG(LOADER, "Loading file %s...", filename.c_str()); switch (IdentifyFile(filename)) { diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 38b3d4c99..002af1f60 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -44,7 +44,7 @@ public: * Load the application * @return ResultStatus result of function */ - virtual const ResultStatus Load() = 0; + virtual ResultStatus Load() = 0; /** * Get the code (typically .code section) of the application @@ -109,13 +109,13 @@ protected: * @param filename String filename of bootable file * @return FileType of file */ -const FileType IdentifyFile(const std::string &filename); +FileType IdentifyFile(const std::string &filename); /** * Identifies and loads a bootable file * @param filename String filename of bootable file * @return ResultStatus result of function */ -const ResultStatus LoadFile(std::string& filename); +ResultStatus LoadFile(const std::string& filename); } // namespace diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index 6423da8f9..a4922c2c0 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -102,7 +102,7 @@ bool LZSS_Decompress(u8* compressed, u32 compressed_size, u8* decompressed, u32 // AppLoader_NCCH class /// AppLoader_NCCH constructor -AppLoader_NCCH::AppLoader_NCCH(std::string& filename) { +AppLoader_NCCH::AppLoader_NCCH(const std::string& filename) { this->filename = filename; is_loaded = false; is_compressed = false; @@ -119,7 +119,7 @@ AppLoader_NCCH::~AppLoader_NCCH() { * Loads .code section into memory for booting * @return ResultStatus result of function */ -const ResultStatus AppLoader_NCCH::LoadExec() const { +ResultStatus AppLoader_NCCH::LoadExec() const { if (!is_loaded) return ResultStatus::ErrorNotLoaded; @@ -137,7 +137,7 @@ const ResultStatus AppLoader_NCCH::LoadExec() const { * @param name Name of section to read out of NCCH file * @param buffer Buffer to read section into. */ -const ResultStatus AppLoader_NCCH::LoadSectionExeFS(File::IOFile& file, const char* name, +ResultStatus AppLoader_NCCH::LoadSectionExeFS(File::IOFile& file, const char* name, std::vector& buffer) { // Iterate through the ExeFs archive until we find the .code file... @@ -183,7 +183,7 @@ const ResultStatus AppLoader_NCCH::LoadSectionExeFS(File::IOFile& file, const ch * @param file Handle to file to read from * @return ResultStatus result of function */ -const ResultStatus AppLoader_NCCH::LoadRomFS(File::IOFile& file) { +ResultStatus AppLoader_NCCH::LoadRomFS(File::IOFile& file) { // Check if the NCCH has a RomFS... if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; @@ -210,7 +210,7 @@ const ResultStatus AppLoader_NCCH::LoadRomFS(File::IOFile& file) { * @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() { +ResultStatus AppLoader_NCCH::Load() { INFO_LOG(LOADER, "Loading NCCH file %s...", filename.c_str()); if (is_loaded) diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 939b144a6..126eb4c80 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -147,14 +147,14 @@ 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(const std::string& filename); ~AppLoader_NCCH(); /** * Load the application * @return ResultStatus result of function */ - const ResultStatus Load(); + ResultStatus Load(); private: @@ -165,21 +165,20 @@ private: * @param buffer Buffer to read section into. * @return ResultStatus result of function */ - const ResultStatus LoadSectionExeFS(File::IOFile& file, const char* name, - std::vector& buffer); + ResultStatus LoadSectionExeFS(File::IOFile& file, const char* name, std::vector& buffer); /** * Reads RomFS of an NCCH file into AppLoader * @param file Handle to file to read from * @return ResultStatus result of function */ - const ResultStatus LoadRomFS(File::IOFile& file); + ResultStatus LoadRomFS(File::IOFile& file); /** * Loads .code section into memory for booting * @return ResultStatus result of function */ - const ResultStatus LoadExec() const; + ResultStatus LoadExec() const; std::string filename; bool is_loaded; From cedc28dcc654961c419ad916cf56f18b0f94e7f5 Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 19 Jun 2014 17:52:15 -0400 Subject: [PATCH 13/15] ELF: Refactored LoadInto(..) to use memcpy, removed unnecessary code. --- src/core/loader/elf.cpp | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index d9e5e130f..76c9d6d54 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -265,35 +265,24 @@ bool ElfReader::LoadInto(u32 vaddr) { } else { DEBUG_LOG(MASTER_LOG, "Prerelocated executable"); } - INFO_LOG(MASTER_LOG, "%i segments:", header->e_phnum); // First pass : Get the bits into RAM - u32 segmentVAddr[32]; - u32 baseAddress = relocate ? vaddr : 0; + u32 segment_addr[32]; + u32 base_addr = relocate ? vaddr : 0; for (int i = 0; i < header->e_phnum; i++) { Elf32_Phdr *p = segments + i; - - INFO_LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); + 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) { - segmentVAddr[i] = baseAddress + p->p_vaddr; - u32 writeAddr = segmentVAddr[i]; - - const u8 *src = GetSegmentPtr(i); - u8 *dst = Memory::GetPointer(writeAddr); - u32 srcSize = p->p_filesz; - u32 dstSize = p->p_memsz; - u32 *s = (u32*)src; - u32 *d = (u32*)dst; - for (int j = 0; j < (int)(srcSize + 3) / 4; j++) { - *d++ = (*s++); - } - INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); + segment_addr[i] = base_addr + p->p_vaddr; + memcpy(Memory::GetPointer(segment_addr[i]), GetSegmentPtr(i), p->p_filesz); + INFO_LOG(MASTER_LOG, "Loadable Segment Copied to %08x, size %08x", segment_addr[i], + p->p_memsz); } } - INFO_LOG(MASTER_LOG, "Done loading."); return true; } From 6aebd4ac9808c189c25185af8fb245c6d282bad0 Mon Sep 17 00:00:00 2001 From: bunnei Date: Tue, 24 Jun 2014 18:51:31 -0400 Subject: [PATCH 14/15] MemMap: Added a WriteBlock function to write a buffer of data to memory. --- src/core/mem_map.h | 2 ++ src/core/mem_map_funcs.cpp | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/core/mem_map.h b/src/core/mem_map.h index 12d497ef3..d5899e4bb 100644 --- a/src/core/mem_map.h +++ b/src/core/mem_map.h @@ -139,6 +139,8 @@ void Write8(const u32 addr, const u8 data); void Write16(const u32 addr, const u16 data); void Write32(const u32 addr, const u32 data); +void WriteBlock(const u32 addr, const u8* data, const int size); + u8* GetPointer(const u32 Address); /** diff --git a/src/core/mem_map_funcs.cpp b/src/core/mem_map_funcs.cpp index ab014a596..37913119e 100644 --- a/src/core/mem_map_funcs.cpp +++ b/src/core/mem_map_funcs.cpp @@ -293,4 +293,16 @@ void Write64(const u32 addr, const u64 data) { _Write(addr, data); } +void WriteBlock(const u32 addr, const u8* data, const int size) { + int offset = 0; + while (offset < (size & ~3)) + Write32(addr + offset, *(u32*)&data[offset += 4]); + + if (size & 2) + Write16(addr + offset, *(u16*)&data[offset += 2]); + + if (size & 1) + Write8(addr + offset, data[offset]); +} + } // namespace From a7f1c544909ee1034356666e04bea3a4b4609a95 Mon Sep 17 00:00:00 2001 From: bunnei Date: Sun, 22 Jun 2014 15:40:21 -0400 Subject: [PATCH 15/15] Loader: Refactored loading functions to only read data from binary if called. NCCH: Updated LoadExec to use Memory::WriteBlock function to load binary code. --- src/core/loader/loader.h | 16 ++--- src/core/loader/ncch.cpp | 142 ++++++++++++++++++++++++--------------- src/core/loader/ncch.h | 57 ++++++++++++---- 3 files changed, 141 insertions(+), 74 deletions(-) diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 002af1f60..95f16fcb1 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -51,37 +51,37 @@ public: * @param error ResultStatus result of function * @return Reference to code buffer */ - virtual const std::vector& GetCode(ResultStatus& error) const { + virtual const std::vector& ReadCode(ResultStatus& error) const { error = ResultStatus::ErrorNotImplemented; return code; } /** - * Get the icon (typically .icon section) of the application + * Get the icon (typically icon section) of the application * @param error ResultStatus result of function * @return Reference to icon buffer */ - virtual const std::vector& GetIcon(ResultStatus& error) const { + virtual const std::vector& ReadIcon(ResultStatus& error) const { error = ResultStatus::ErrorNotImplemented; return icon; } /** - * Get the banner (typically .banner section) of the application + * Get the banner (typically banner section) of the application * @param error ResultStatus result of function * @return Reference to banner buffer */ - virtual const std::vector& GetBanner(ResultStatus& error) const { + virtual const std::vector& ReadBanner(ResultStatus& error) const { error = ResultStatus::ErrorNotImplemented; return banner; } /** - * Get the logo (typically .logo section) of the application + * Get the logo (typically logo section) of the application * @param error ResultStatus result of function * @return Reference to logo buffer */ - virtual const std::vector& GetLogo(ResultStatus& error) const { + virtual const std::vector& ReadLogo(ResultStatus& error) const { error = ResultStatus::ErrorNotImplemented; return logo; } @@ -91,7 +91,7 @@ public: * @param error ResultStatus result of function * @return Reference to RomFs archive buffer */ - virtual const std::vector& GetRomFs(ResultStatus error) const { + virtual const std::vector& ReadRomFS(ResultStatus& error) const { error = ResultStatus::ErrorNotImplemented; return romfs; } diff --git a/src/core/loader/ncch.cpp b/src/core/loader/ncch.cpp index a4922c2c0..60505bdfa 100644 --- a/src/core/loader/ncch.cpp +++ b/src/core/loader/ncch.cpp @@ -113,36 +113,39 @@ AppLoader_NCCH::AppLoader_NCCH(const std::string& filename) { /// AppLoader_NCCH destructor AppLoader_NCCH::~AppLoader_NCCH() { + if (file.IsOpen()) + file.Close(); } /** * Loads .code section into memory for booting * @return ResultStatus result of function */ -ResultStatus AppLoader_NCCH::LoadExec() const { +ResultStatus AppLoader_NCCH::LoadExec() { if (!is_loaded) return ResultStatus::ErrorNotLoaded; - for (std::vector::size_type i = 0; i != code.size(); i++) { - Memory::Write8(entry_point + i, code[i]); + ResultStatus res; + code = ReadCode(res); + + if (ResultStatus::Success == res) { + Memory::WriteBlock(entry_point, &code[0], code.size()); + Kernel::LoadExec(entry_point); } - Kernel::LoadExec(entry_point); - - return ResultStatus::Success; + return res; } /** * Reads an application ExeFS 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. + * @param buffer Vector to read data into + * @param error ResultStatus result of function + * @return Reference to buffer of data that was read */ -ResultStatus AppLoader_NCCH::LoadSectionExeFS(File::IOFile& file, const char* name, - std::vector& buffer) { - +const std::vector& AppLoader_NCCH::LoadSectionExeFS(const char* name, std::vector& buffer, + ResultStatus& error) { // Iterate through the ExeFs archive until we find the .code file... for (int i = 0; i < kMaxSections; i++) { - // Load the specified section... if (strcmp((const char*)exefs_header.section[i].name, name) == 0) { INFO_LOG(LOADER, "ExeFS section %d:", i); @@ -161,49 +164,27 @@ ResultStatus AppLoader_NCCH::LoadSectionExeFS(File::IOFile& file, const char* na file.ReadBytes(&temp_buffer[0], exefs_header.section[i].size); // Decompress .code section... - u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], exefs_header.section[i].size); + u32 decompressed_size = LZSS_GetDecompressedSize(&temp_buffer[0], + exefs_header.section[i].size); buffer.resize(decompressed_size); - if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0], + if (!LZSS_Decompress(&temp_buffer[0], exefs_header.section[i].size, &buffer[0], decompressed_size)) { - return ResultStatus::ErrorInvalidFormat; + error = ResultStatus::ErrorInvalidFormat; + return buffer; } // Section is uncompressed... } else { buffer.resize(exefs_header.section[i].size); file.ReadBytes(&buffer[0], exefs_header.section[i].size); } - return ResultStatus::Success; + error = ResultStatus::Success; + return buffer; } } - return ResultStatus::ErrorNotUsed; + error = ResultStatus::ErrorNotUsed; + return buffer; } -/** - * Reads RomFS of an NCCH file into AppLoader - * @param file Handle to file to read from - * @return ResultStatus result of function - */ -ResultStatus AppLoader_NCCH::LoadRomFS(File::IOFile& file) { - // Check if the NCCH has a RomFS... - if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { - u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; - u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; - - INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset); - INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size); - - romfs.resize(romfs_size); - - file.Seek(romfs_offset, 0); - file.ReadBytes(&romfs[0], romfs_size); - - return ResultStatus::Success; - } else { - NOTICE_LOG(LOADER, "RomFS unused"); - } - return ResultStatus::ErrorNotUsed; -} - /** * 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 @@ -216,7 +197,7 @@ ResultStatus AppLoader_NCCH::Load() { if (is_loaded) return ResultStatus::ErrorAlreadyLoaded; - File::IOFile file(filename, "rb"); + file = File::IOFile(filename, "rb"); if (file.IsOpen()) { file.ReadBytes(&ncch_header, sizeof(NCCH_Header)); @@ -255,15 +236,6 @@ ResultStatus AppLoader_NCCH::Load() { file.Seek(exefs_offset + ncch_offset, 0); file.ReadBytes(&exefs_header, sizeof(ExeFs_Header)); - // TODO(bunnei): Check ResultStatus of these... - - LoadSectionExeFS(file, ".code", code); - LoadSectionExeFS(file, "banner", banner); - LoadSectionExeFS(file, "icon", icon); - LoadSectionExeFS(file, "logo", logo); - - LoadRomFS(file); - is_loaded = true; // Set state to loaded LoadExec(); // Load the executable into memory for booting @@ -273,4 +245,68 @@ ResultStatus AppLoader_NCCH::Load() { return ResultStatus::Error; } +/** + * Get the code (typically .code section) of the application + * @param error ResultStatus result of function + * @return Reference to code buffer + */ +const std::vector& AppLoader_NCCH::ReadCode(ResultStatus& error) { + return LoadSectionExeFS(".code", code, error); +} + +/** + * Get the icon (typically icon section) of the application + * @param error ResultStatus result of function + * @return Reference to icon buffer + */ +const std::vector& AppLoader_NCCH::ReadIcon(ResultStatus& error) { + return LoadSectionExeFS("icon", icon, error); +} + +/** + * Get the banner (typically banner section) of the application + * @param error ResultStatus result of function + * @return Reference to banner buffer + */ +const std::vector& AppLoader_NCCH::ReadBanner(ResultStatus& error) { + return LoadSectionExeFS("banner", banner, error); +} + +/** + * Get the logo (typically logo section) of the application + * @param error ResultStatus result of function + * @return Reference to logo buffer + */ +const std::vector& AppLoader_NCCH::ReadLogo(ResultStatus& error) { + return LoadSectionExeFS("logo", logo, error); +} + +/** + * Get the RomFs archive of the application + * @param error ResultStatus result of function + * @return Reference to RomFs archive buffer + */ +const std::vector& AppLoader_NCCH::ReadRomFS(ResultStatus& error) { + // Check if the NCCH has a RomFS... + if (ncch_header.romfs_offset != 0 && ncch_header.romfs_size != 0) { + u32 romfs_offset = ncch_offset + (ncch_header.romfs_offset * kBlockSize) + 0x1000; + u32 romfs_size = (ncch_header.romfs_size * kBlockSize) - 0x1000; + + INFO_LOG(LOADER, "RomFS offset: 0x%08X", romfs_offset); + INFO_LOG(LOADER, "RomFS size: 0x%08X", romfs_size); + + romfs.resize(romfs_size); + + file.Seek(romfs_offset, 0); + file.ReadBytes(&romfs[0], romfs_size); + + error = ResultStatus::Success; + return romfs; + } else { + NOTICE_LOG(LOADER, "RomFS unused"); + } + error = ResultStatus::ErrorNotUsed; + return romfs; +} + } // namespace Loader diff --git a/src/core/loader/ncch.h b/src/core/loader/ncch.h index 126eb4c80..bf65425a4 100644 --- a/src/core/loader/ncch.h +++ b/src/core/loader/ncch.h @@ -156,35 +156,66 @@ public: */ ResultStatus Load(); + /** + * Get the code (typically .code section) of the application + * @param error ResultStatus result of function + * @return Reference to code buffer + */ + const std::vector& ReadCode(ResultStatus& error); + + /** + * Get the icon (typically icon section) of the application + * @param error ResultStatus result of function + * @return Reference to icon buffer + */ + const std::vector& ReadIcon(ResultStatus& error); + + /** + * Get the banner (typically banner section) of the application + * @param error ResultStatus result of function + * @return Reference to banner buffer + */ + const std::vector& ReadBanner(ResultStatus& error); + + /** + * Get the logo (typically logo section) of the application + * @param error ResultStatus result of function + * @return Reference to logo buffer + */ + const std::vector& ReadLogo(ResultStatus& error); + + /** + * Get the RomFs archive of the application + * @param error ResultStatus result of function + * @return Reference to RomFs archive buffer + */ + const std::vector& ReadRomFS(ResultStatus& error); + private: /** * Reads an application ExeFS 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. - * @return ResultStatus result of function + * @param buffer Vector to read data into + * @param error ResultStatus result of function + * @return Reference to buffer of data that was read */ - ResultStatus LoadSectionExeFS(File::IOFile& file, const char* name, std::vector& buffer); - - /** - * Reads RomFS of an NCCH file into AppLoader - * @param file Handle to file to read from - * @return ResultStatus result of function - */ - ResultStatus LoadRomFS(File::IOFile& file); + const std::vector& LoadSectionExeFS(const char* name, std::vector& buffer, + ResultStatus& error); /** * Loads .code section into memory for booting * @return ResultStatus result of function */ - ResultStatus LoadExec() const; + ResultStatus LoadExec(); + File::IOFile file; std::string filename; + bool is_loaded; bool is_compressed; - u32 entry_point; + u32 entry_point; u32 ncch_offset; // Offset to NCCH header, can be 0 or after NCSD header u32 exefs_offset;