| | |
| | |
| | |
| |
|
| | #include <algorithm> |
| | #include <vector> |
| | #include "common/logging/log.h" |
| | #include "core/core.h" |
| | #include "core/hle/kernel/process.h" |
| | #include "core/hle/kernel/resource_limit.h" |
| | #include "core/hle/service/fs/archive.h" |
| | #include "core/hle/service/fs/fs_user.h" |
| | #include "core/loader/3dsx.h" |
| | #include "core/memory.h" |
| |
|
| | namespace Loader { |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | enum THREEDSX_Error { ERROR_NONE = 0, ERROR_READ = 1, ERROR_FILE = 2, ERROR_ALLOC = 3 }; |
| |
|
| | static const u32 RELOCBUFSIZE = 512; |
| | static const unsigned int NUM_SEGMENTS = 3; |
| |
|
| | |
| | #pragma pack(1) |
| | struct THREEDSX_Header { |
| | u32 magic; |
| | u16 header_size, reloc_hdr_size; |
| | u32 format_ver; |
| | u32 flags; |
| |
|
| | |
| | |
| | u32 code_seg_size, rodata_seg_size, data_seg_size, bss_size; |
| | |
| | u32 smdh_offset, smdh_size; |
| | |
| | u32 fs_offset; |
| | }; |
| |
|
| | |
| | struct THREEDSX_RelocHdr { |
| | |
| | u32 cross_segment_absolute; |
| | |
| | |
| | u32 cross_segment_relative; |
| | |
| |
|
| | |
| | |
| | |
| | }; |
| |
|
| | |
| | struct THREEDSX_Reloc { |
| | u16 skip, patch; |
| | }; |
| | #pragma pack() |
| |
|
| | struct THREEloadinfo { |
| | u8* seg_ptrs[3]; |
| | u32 seg_addrs[3]; |
| | u32 seg_sizes[3]; |
| | }; |
| |
|
| | static u32 TranslateAddr(u32 addr, const THREEloadinfo* loadinfo, u32* offsets) { |
| | if (addr < offsets[0]) |
| | return loadinfo->seg_addrs[0] + addr; |
| | if (addr < offsets[1]) |
| | return loadinfo->seg_addrs[1] + addr - offsets[0]; |
| | return loadinfo->seg_addrs[2] + addr - offsets[1]; |
| | } |
| |
|
| | using Kernel::CodeSet; |
| |
|
| | static THREEDSX_Error Load3DSXFile(Core::System& system, FileUtil::IOFile& file, u32 base_addr, |
| | std::shared_ptr<CodeSet>* out_codeset) { |
| | if (!file.IsOpen()) |
| | return ERROR_FILE; |
| |
|
| | |
| | file.Seek(0, SEEK_SET); |
| |
|
| | THREEDSX_Header hdr; |
| | if (file.ReadBytes(&hdr, sizeof(hdr)) != sizeof(hdr)) |
| | return ERROR_READ; |
| |
|
| | THREEloadinfo loadinfo; |
| | |
| | loadinfo.seg_sizes[0] = (hdr.code_seg_size + 0xFFF) & ~0xFFF; |
| | loadinfo.seg_sizes[1] = (hdr.rodata_seg_size + 0xFFF) & ~0xFFF; |
| | loadinfo.seg_sizes[2] = (hdr.data_seg_size + 0xFFF) & ~0xFFF; |
| | |
| | if (loadinfo.seg_sizes[0] < hdr.code_seg_size || loadinfo.seg_sizes[1] < hdr.rodata_seg_size || |
| | loadinfo.seg_sizes[2] < hdr.data_seg_size) { |
| | return ERROR_READ; |
| | } |
| | u32 offsets[2] = {loadinfo.seg_sizes[0], loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1]}; |
| | u32 n_reloc_tables = hdr.reloc_hdr_size / sizeof(u32); |
| | std::vector<u8> program_image(loadinfo.seg_sizes[0] + loadinfo.seg_sizes[1] + |
| | loadinfo.seg_sizes[2]); |
| |
|
| | loadinfo.seg_addrs[0] = base_addr; |
| | loadinfo.seg_addrs[1] = loadinfo.seg_addrs[0] + loadinfo.seg_sizes[0]; |
| | loadinfo.seg_addrs[2] = loadinfo.seg_addrs[1] + loadinfo.seg_sizes[1]; |
| | loadinfo.seg_ptrs[0] = program_image.data(); |
| | loadinfo.seg_ptrs[1] = loadinfo.seg_ptrs[0] + loadinfo.seg_sizes[0]; |
| | loadinfo.seg_ptrs[2] = loadinfo.seg_ptrs[1] + loadinfo.seg_sizes[1]; |
| |
|
| | |
| | file.Seek(hdr.header_size, SEEK_SET); |
| |
|
| | |
| | std::vector<u32> relocs(n_reloc_tables * NUM_SEGMENTS); |
| | for (unsigned int current_segment = 0; current_segment < NUM_SEGMENTS; ++current_segment) { |
| | std::size_t size = n_reloc_tables * sizeof(u32); |
| | if (file.ReadBytes(&relocs[current_segment * n_reloc_tables], size) != size) |
| | return ERROR_READ; |
| | } |
| |
|
| | |
| | if (file.ReadBytes(loadinfo.seg_ptrs[0], hdr.code_seg_size) != hdr.code_seg_size) |
| | return ERROR_READ; |
| | if (file.ReadBytes(loadinfo.seg_ptrs[1], hdr.rodata_seg_size) != hdr.rodata_seg_size) |
| | return ERROR_READ; |
| | if (file.ReadBytes(loadinfo.seg_ptrs[2], hdr.data_seg_size - hdr.bss_size) != |
| | hdr.data_seg_size - hdr.bss_size) |
| | return ERROR_READ; |
| |
|
| | |
| | std::memset((char*)loadinfo.seg_ptrs[2] + hdr.data_seg_size - hdr.bss_size, 0, hdr.bss_size); |
| |
|
| | |
| | for (unsigned int current_segment = 0; current_segment < NUM_SEGMENTS; ++current_segment) { |
| | for (unsigned current_segment_reloc_table = 0; current_segment_reloc_table < n_reloc_tables; |
| | current_segment_reloc_table++) { |
| | u32 n_relocs = relocs[current_segment * n_reloc_tables + current_segment_reloc_table]; |
| | if (current_segment_reloc_table >= 2) { |
| | |
| | file.Seek(n_relocs * sizeof(THREEDSX_Reloc), SEEK_CUR); |
| | continue; |
| | } |
| | THREEDSX_Reloc reloc_table[RELOCBUFSIZE]; |
| |
|
| | u32* pos = (u32*)loadinfo.seg_ptrs[current_segment]; |
| | const u32* end_pos = pos + (loadinfo.seg_sizes[current_segment] / 4); |
| |
|
| | while (n_relocs) { |
| | u32 remaining = std::min(RELOCBUFSIZE, n_relocs); |
| | n_relocs -= remaining; |
| |
|
| | if (file.ReadBytes(reloc_table, remaining * sizeof(THREEDSX_Reloc)) != |
| | remaining * sizeof(THREEDSX_Reloc)) |
| | return ERROR_READ; |
| |
|
| | for (unsigned current_inprogress = 0; |
| | current_inprogress < remaining && pos < end_pos; current_inprogress++) { |
| | const auto& table = reloc_table[current_inprogress]; |
| | LOG_TRACE(Loader, "(t={},skip={},patch={})", current_segment_reloc_table, |
| | static_cast<u32>(table.skip), static_cast<u32>(table.patch)); |
| | pos += table.skip; |
| | s32 num_patches = table.patch; |
| | while (0 < num_patches && pos < end_pos) { |
| | u32 in_addr = base_addr + static_cast<u32>(reinterpret_cast<u8*>(pos) - |
| | program_image.data()); |
| | u32 orig_data = *pos; |
| | u32 sub_type = orig_data >> (32 - 4); |
| | u32 addr = TranslateAddr(orig_data & ~0xF0000000, &loadinfo, offsets); |
| | LOG_TRACE(Loader, "Patching {:08X} <-- rel({:08X},{}) ({:08X})", in_addr, |
| | addr, current_segment_reloc_table, *pos); |
| | switch (current_segment_reloc_table) { |
| | case 0: { |
| | if (sub_type != 0) |
| | return ERROR_READ; |
| | *pos = addr; |
| | break; |
| | } |
| | case 1: { |
| | u32 data = addr - in_addr; |
| | switch (sub_type) { |
| | case 0: |
| | *pos = data; |
| | break; |
| | case 1: |
| | *pos = data & ~(1U << 31); |
| | break; |
| | default: |
| | return ERROR_READ; |
| | } |
| | break; |
| | } |
| | default: |
| | break; |
| | } |
| | pos++; |
| | num_patches--; |
| | } |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | std::shared_ptr<CodeSet> code_set = system.Kernel().CreateCodeSet("", 0); |
| |
|
| | code_set->CodeSegment().offset = loadinfo.seg_ptrs[0] - program_image.data(); |
| | code_set->CodeSegment().addr = loadinfo.seg_addrs[0]; |
| | code_set->CodeSegment().size = loadinfo.seg_sizes[0]; |
| |
|
| | code_set->RODataSegment().offset = loadinfo.seg_ptrs[1] - program_image.data(); |
| | code_set->RODataSegment().addr = loadinfo.seg_addrs[1]; |
| | code_set->RODataSegment().size = loadinfo.seg_sizes[1]; |
| |
|
| | code_set->DataSegment().offset = loadinfo.seg_ptrs[2] - program_image.data(); |
| | code_set->DataSegment().addr = loadinfo.seg_addrs[2]; |
| | code_set->DataSegment().size = loadinfo.seg_sizes[2]; |
| |
|
| | code_set->entrypoint = code_set->CodeSegment().addr; |
| | code_set->memory = std::move(program_image); |
| |
|
| | LOG_DEBUG(Loader, "code size: {:#X}", loadinfo.seg_sizes[0]); |
| | LOG_DEBUG(Loader, "rodata size: {:#X}", loadinfo.seg_sizes[1]); |
| | LOG_DEBUG(Loader, "data size: {:#X} (including {:#X} of bss)", loadinfo.seg_sizes[2], |
| | hdr.bss_size); |
| |
|
| | *out_codeset = code_set; |
| | return ERROR_NONE; |
| | } |
| |
|
| | FileType AppLoader_THREEDSX::IdentifyType(FileUtil::IOFile& file) { |
| | u32 magic; |
| | file.Seek(0, SEEK_SET); |
| | if (1 != file.ReadArray<u32>(&magic, 1)) |
| | return FileType::Error; |
| |
|
| | if (MakeMagic('3', 'D', 'S', 'X') == magic) |
| | return FileType::THREEDSX; |
| |
|
| | return FileType::Error; |
| | } |
| |
|
| | ResultStatus AppLoader_THREEDSX::Load(std::shared_ptr<Kernel::Process>& process) { |
| | if (is_loaded) |
| | return ResultStatus::ErrorAlreadyLoaded; |
| |
|
| | if (!file.IsOpen()) |
| | return ResultStatus::Error; |
| |
|
| | std::shared_ptr<CodeSet> codeset; |
| | if (Load3DSXFile(system, file, Memory::PROCESS_IMAGE_VADDR, &codeset) != ERROR_NONE) |
| | return ResultStatus::Error; |
| | codeset->name = filename; |
| |
|
| | process = system.Kernel().CreateProcess(std::move(codeset)); |
| | process->Set3dsxKernelCaps(); |
| |
|
| | |
| | process->resource_limit = |
| | system.Kernel().ResourceLimit().GetForCategory(Kernel::ResourceLimitCategory::Application); |
| |
|
| | |
| | auto fs_user = system.ServiceManager().GetService<Service::FS::FS_USER>("fs:USER"); |
| | fs_user->RegisterProgramInfo(process->GetObjectId(), process->codeset->program_id, filepath); |
| |
|
| | process->Run(48, Kernel::DEFAULT_STACK_SIZE); |
| |
|
| | system.ArchiveManager().RegisterSelfNCCH(*this); |
| |
|
| | is_loaded = true; |
| | return ResultStatus::Success; |
| | } |
| |
|
| | ResultStatus AppLoader_THREEDSX::ReadRomFS(std::shared_ptr<FileSys::RomFSReader>& romfs_file) { |
| | if (!file.IsOpen()) |
| | return ResultStatus::Error; |
| |
|
| | |
| | file.Seek(0, SEEK_SET); |
| |
|
| | THREEDSX_Header hdr; |
| | if (file.ReadBytes(&hdr, sizeof(THREEDSX_Header)) != sizeof(THREEDSX_Header)) |
| | return ResultStatus::Error; |
| |
|
| | if (hdr.header_size != sizeof(THREEDSX_Header)) |
| | return ResultStatus::Error; |
| |
|
| | |
| | if (hdr.fs_offset != 0) { |
| | u32 romfs_offset = hdr.fs_offset; |
| | u32 romfs_size = static_cast<u32>(file.GetSize()) - hdr.fs_offset; |
| |
|
| | LOG_DEBUG(Loader, "RomFS offset: {:#010X}", romfs_offset); |
| | LOG_DEBUG(Loader, "RomFS size: {:#010X}", romfs_size); |
| |
|
| | |
| | FileUtil::IOFile romfs_file_inner(filepath, "rb"); |
| | if (!romfs_file_inner.IsOpen()) |
| | return ResultStatus::Error; |
| |
|
| | romfs_file = std::make_shared<FileSys::DirectRomFSReader>(std::move(romfs_file_inner), |
| | romfs_offset, romfs_size); |
| |
|
| | return ResultStatus::Success; |
| | } |
| | LOG_DEBUG(Loader, "3DSX has no RomFS"); |
| | return ResultStatus::ErrorNotUsed; |
| | } |
| |
|
| | ResultStatus AppLoader_THREEDSX::ReadIcon(std::vector<u8>& buffer) { |
| | if (!file.IsOpen()) |
| | return ResultStatus::Error; |
| |
|
| | |
| | file.Seek(0, SEEK_SET); |
| |
|
| | THREEDSX_Header hdr; |
| | if (file.ReadBytes(&hdr, sizeof(THREEDSX_Header)) != sizeof(THREEDSX_Header)) |
| | return ResultStatus::Error; |
| |
|
| | if (hdr.header_size != sizeof(THREEDSX_Header)) |
| | return ResultStatus::Error; |
| |
|
| | |
| | if (hdr.smdh_offset != 0) { |
| | file.Seek(hdr.smdh_offset, SEEK_SET); |
| | buffer.resize(hdr.smdh_size); |
| |
|
| | if (file.ReadBytes(buffer.data(), hdr.smdh_size) != hdr.smdh_size) |
| | return ResultStatus::Error; |
| |
|
| | return ResultStatus::Success; |
| | } |
| | return ResultStatus::ErrorNotUsed; |
| | } |
| |
|
| | } |
| |
|