summaryrefslogtreecommitdiffstats
path: root/src/core/loader
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/loader')
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp40
-rw-r--r--src/core/loader/deconstructed_rom_directory.h2
-rw-r--r--src/core/loader/elf.cpp18
-rw-r--r--src/core/loader/elf.h2
-rw-r--r--src/core/loader/linker.cpp147
-rw-r--r--src/core/loader/linker.h36
-rw-r--r--src/core/loader/loader.h19
-rw-r--r--src/core/loader/nax.cpp30
-rw-r--r--src/core/loader/nax.h2
-rw-r--r--src/core/loader/nca.cpp26
-rw-r--r--src/core/loader/nca.h2
-rw-r--r--src/core/loader/nro.cpp17
-rw-r--r--src/core/loader/nro.h6
-rw-r--r--src/core/loader/nso.cpp130
-rw-r--r--src/core/loader/nso.h45
-rw-r--r--src/core/loader/nsp.cpp38
-rw-r--r--src/core/loader/nsp.h2
-rw-r--r--src/core/loader/xci.cpp28
-rw-r--r--src/core/loader/xci.h4
19 files changed, 232 insertions, 362 deletions
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 07aa7a1cd..10b13fb1d 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -86,25 +86,29 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua
return FileType::Error;
}
-ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process) {
+AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
+ Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
if (dir == nullptr) {
- if (file == nullptr)
- return ResultStatus::ErrorNullFile;
+ if (file == nullptr) {
+ return {ResultStatus::ErrorNullFile, {}};
+ }
+
dir = file->GetContainingDirectory();
}
// Read meta to determine title ID
FileSys::VirtualFile npdm = dir->GetFile("main.npdm");
- if (npdm == nullptr)
- return ResultStatus::ErrorMissingNPDM;
+ if (npdm == nullptr) {
+ return {ResultStatus::ErrorMissingNPDM, {}};
+ }
- ResultStatus result = metadata.Load(npdm);
+ const ResultStatus result = metadata.Load(npdm);
if (result != ResultStatus::Success) {
- return result;
+ return {result, {}};
}
if (override_update) {
@@ -114,23 +118,24 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
// Reread in case PatchExeFS affected the main.npdm
npdm = dir->GetFile("main.npdm");
- if (npdm == nullptr)
- return ResultStatus::ErrorMissingNPDM;
+ if (npdm == nullptr) {
+ return {ResultStatus::ErrorMissingNPDM, {}};
+ }
- ResultStatus result2 = metadata.Load(npdm);
+ const ResultStatus result2 = metadata.Load(npdm);
if (result2 != ResultStatus::Success) {
- return result2;
+ return {result2, {}};
}
metadata.Print();
const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()};
if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit ||
arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) {
- return ResultStatus::Error32BitISA;
+ return {ResultStatus::Error32BitISA, {}};
}
if (process.LoadFromMetadata(metadata).IsError()) {
- return ResultStatus::ErrorUnableToParseKernelMetadata;
+ return {ResultStatus::ErrorUnableToParseKernelMetadata, {}};
}
const FileSys::PatchManager pm(metadata.GetTitleID());
@@ -150,7 +155,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
const auto tentative_next_load_addr =
AppLoader_NSO::LoadModule(process, *module_file, load_addr, should_pass_arguments, pm);
if (!tentative_next_load_addr) {
- return ResultStatus::ErrorLoadingNSO;
+ return {ResultStatus::ErrorLoadingNSO, {}};
}
next_load_addr = *tentative_next_load_addr;
@@ -159,8 +164,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
GDBStub::RegisterModule(module, load_addr, next_load_addr - 1, false);
}
- process.Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize());
-
// Find the RomFS by searching for a ".romfs" file in this directory
const auto& files = dir->GetFiles();
const auto romfs_iter =
@@ -175,7 +178,8 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load(Kernel::Process& process)
}
is_loaded = true;
- return ResultStatus::Success;
+ return {ResultStatus::Success,
+ LoadParameters{metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()}};
}
ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 1615cb5a8..1a65c16a4 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -37,7 +37,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 6057c7f26..6d4b02375 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -9,6 +9,7 @@
#include "common/common_types.h"
#include "common/file_util.h"
#include "common/logging/log.h"
+#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/elf.h"
@@ -340,7 +341,7 @@ Kernel::CodeSet ElfReader::LoadInto(VAddr vaddr) {
}
codeset.entrypoint = base_addr + header->e_entry;
- codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
+ codeset.memory = std::move(program_image);
LOG_DEBUG(Loader, "Done loading.");
@@ -381,13 +382,15 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
- if (is_loaded)
- return ResultStatus::ErrorAlreadyLoaded;
+AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) {
+ if (is_loaded) {
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
+ }
std::vector<u8> buffer = file->ReadAllBytes();
- if (buffer.size() != file->GetSize())
- return ResultStatus::ErrorIncorrectELFFileSize;
+ if (buffer.size() != file->GetSize()) {
+ return {ResultStatus::ErrorIncorrectELFFileSize, {}};
+ }
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
ElfReader elf_reader(&buffer[0]);
@@ -395,10 +398,9 @@ ResultStatus AppLoader_ELF::Load(Kernel::Process& process) {
const VAddr entry_point = codeset.entrypoint;
process.LoadModule(std::move(codeset), entry_point);
- process.Run(entry_point, 48, Memory::DEFAULT_STACK_SIZE);
is_loaded = true;
- return ResultStatus::Success;
+ return {ResultStatus::Success, LoadParameters{48, Memory::DEFAULT_STACK_SIZE}};
}
} // namespace Loader
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index a2d33021c..7ef7770a6 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -26,7 +26,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
};
} // namespace Loader
diff --git a/src/core/loader/linker.cpp b/src/core/loader/linker.cpp
deleted file mode 100644
index 57ca8c3ee..000000000
--- a/src/core/loader/linker.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <vector>
-
-#include "common/common_funcs.h"
-#include "common/logging/log.h"
-#include "common/swap.h"
-#include "core/loader/linker.h"
-#include "core/memory.h"
-
-namespace Loader {
-
-enum class RelocationType : u32 { ABS64 = 257, GLOB_DAT = 1025, JUMP_SLOT = 1026, RELATIVE = 1027 };
-
-enum DynamicType : u32 {
- DT_NULL = 0,
- DT_PLTRELSZ = 2,
- DT_STRTAB = 5,
- DT_SYMTAB = 6,
- DT_RELA = 7,
- DT_RELASZ = 8,
- DT_STRSZ = 10,
- DT_JMPREL = 23,
-};
-
-struct Elf64_Rela {
- u64_le offset;
- RelocationType type;
- u32_le symbol;
- s64_le addend;
-};
-static_assert(sizeof(Elf64_Rela) == 0x18, "Elf64_Rela has incorrect size.");
-
-struct Elf64_Dyn {
- u64_le tag;
- u64_le value;
-};
-static_assert(sizeof(Elf64_Dyn) == 0x10, "Elf64_Dyn has incorrect size.");
-
-struct Elf64_Sym {
- u32_le name;
- INSERT_PADDING_BYTES(0x2);
- u16_le shndx;
- u64_le value;
- u64_le size;
-};
-static_assert(sizeof(Elf64_Sym) == 0x18, "Elf64_Sym has incorrect size.");
-
-void Linker::WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
- u64 relocation_offset, u64 size, VAddr load_base) {
- for (u64 i = 0; i < size; i += sizeof(Elf64_Rela)) {
- Elf64_Rela rela;
- std::memcpy(&rela, &program_image[relocation_offset + i], sizeof(Elf64_Rela));
-
- const Symbol& symbol = symbols[rela.symbol];
- switch (rela.type) {
- case RelocationType::RELATIVE: {
- const u64 value = load_base + rela.addend;
- if (!symbol.name.empty()) {
- exports[symbol.name] = value;
- }
- std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
- break;
- }
- case RelocationType::JUMP_SLOT:
- case RelocationType::GLOB_DAT:
- if (!symbol.value) {
- imports[symbol.name] = {rela.offset + load_base, 0};
- } else {
- exports[symbol.name] = symbol.value;
- std::memcpy(&program_image[rela.offset], &symbol.value, sizeof(u64));
- }
- break;
- case RelocationType::ABS64:
- if (!symbol.value) {
- imports[symbol.name] = {rela.offset + load_base, rela.addend};
- } else {
- const u64 value = symbol.value + rela.addend;
- exports[symbol.name] = value;
- std::memcpy(&program_image[rela.offset], &value, sizeof(u64));
- }
- break;
- default:
- LOG_CRITICAL(Loader, "Unknown relocation type: {}", static_cast<int>(rela.type));
- break;
- }
- }
-}
-
-void Linker::Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base) {
- std::map<u64, u64> dynamic;
- while (dynamic_section_offset < program_image.size()) {
- Elf64_Dyn dyn;
- std::memcpy(&dyn, &program_image[dynamic_section_offset], sizeof(Elf64_Dyn));
- dynamic_section_offset += sizeof(Elf64_Dyn);
-
- if (dyn.tag == DT_NULL) {
- break;
- }
- dynamic[dyn.tag] = dyn.value;
- }
-
- u64 offset = dynamic[DT_SYMTAB];
- std::vector<Symbol> symbols;
- while (offset < program_image.size()) {
- Elf64_Sym sym;
- std::memcpy(&sym, &program_image[offset], sizeof(Elf64_Sym));
- offset += sizeof(Elf64_Sym);
-
- if (sym.name >= dynamic[DT_STRSZ]) {
- break;
- }
-
- std::string name = reinterpret_cast<char*>(&program_image[dynamic[DT_STRTAB] + sym.name]);
- if (sym.value) {
- exports[name] = load_base + sym.value;
- symbols.emplace_back(std::move(name), load_base + sym.value);
- } else {
- symbols.emplace_back(std::move(name), 0);
- }
- }
-
- if (dynamic.find(DT_RELA) != dynamic.end()) {
- WriteRelocations(program_image, symbols, dynamic[DT_RELA], dynamic[DT_RELASZ], load_base);
- }
-
- if (dynamic.find(DT_JMPREL) != dynamic.end()) {
- WriteRelocations(program_image, symbols, dynamic[DT_JMPREL], dynamic[DT_PLTRELSZ],
- load_base);
- }
-}
-
-void Linker::ResolveImports() {
- // Resolve imports
- for (const auto& import : imports) {
- const auto& search = exports.find(import.first);
- if (search != exports.end()) {
- Memory::Write64(import.second.ea, search->second + import.second.addend);
- } else {
- LOG_ERROR(Loader, "Unresolved import: {}", import.first);
- }
- }
-}
-
-} // namespace Loader
diff --git a/src/core/loader/linker.h b/src/core/loader/linker.h
deleted file mode 100644
index 107625837..000000000
--- a/src/core/loader/linker.h
+++ /dev/null
@@ -1,36 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <map>
-#include <string>
-#include "common/common_types.h"
-
-namespace Loader {
-
-class Linker {
-protected:
- struct Symbol {
- Symbol(std::string&& name, u64 value) : name(std::move(name)), value(value) {}
- std::string name;
- u64 value;
- };
-
- struct Import {
- VAddr ea;
- s64 addend;
- };
-
- void WriteRelocations(std::vector<u8>& program_image, const std::vector<Symbol>& symbols,
- u64 relocation_offset, u64 size, VAddr load_base);
- void Relocate(std::vector<u8>& program_image, u32 dynamic_section_offset, VAddr load_base);
-
- void ResolveImports();
-
- std::map<std::string, Import> imports;
- std::map<std::string, VAddr> exports;
-};
-
-} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index bb925f4a6..869406b75 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -131,6 +131,12 @@ std::ostream& operator<<(std::ostream& os, ResultStatus status);
/// Interface for loading an application
class AppLoader : NonCopyable {
public:
+ struct LoadParameters {
+ s32 main_thread_priority;
+ u64 main_thread_stack_size;
+ };
+ using LoadResult = std::pair<ResultStatus, std::optional<LoadParameters>>;
+
explicit AppLoader(FileSys::VirtualFile file);
virtual ~AppLoader();
@@ -145,18 +151,7 @@ public:
* @param process The newly created process.
* @return The status result of the operation.
*/
- virtual ResultStatus Load(Kernel::Process& process) = 0;
-
- /**
- * Loads the system mode that this application needs.
- * This function defaults to 2 (96MB allocated to the application) if it can't read the
- * information.
- * @returns A pair with the optional system mode, and and the status.
- */
- virtual std::pair<std::optional<u32>, ResultStatus> LoadKernelSystemMode() {
- // 96MB allocated to the application.
- return std::make_pair(2, ResultStatus::Success);
- }
+ virtual LoadResult Load(Kernel::Process& process) = 0;
/**
* Get the code (typically .code section) of the application
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index 93a970d10..34efef09a 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -41,31 +41,37 @@ FileType AppLoader_NAX::GetFileType() const {
return IdentifyTypeImpl(*nax);
}
-ResultStatus AppLoader_NAX::Load(Kernel::Process& process) {
+AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
- if (nax->GetStatus() != ResultStatus::Success)
- return nax->GetStatus();
+ const auto nax_status = nax->GetStatus();
+ if (nax_status != ResultStatus::Success) {
+ return {nax_status, {}};
+ }
const auto nca = nax->AsNCA();
if (nca == nullptr) {
- if (!Core::Crypto::KeyManager::KeyFileExists(false))
- return ResultStatus::ErrorMissingProductionKeyFile;
- return ResultStatus::ErrorNAXInconvertibleToNCA;
+ if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
+ return {ResultStatus::ErrorMissingProductionKeyFile, {}};
+ }
+
+ return {ResultStatus::ErrorNAXInconvertibleToNCA, {}};
}
- if (nca->GetStatus() != ResultStatus::Success)
- return nca->GetStatus();
+ const auto nca_status = nca->GetStatus();
+ if (nca_status != ResultStatus::Success) {
+ return {nca_status, {}};
+ }
const auto result = nca_loader->Load(process);
- if (result != ResultStatus::Success)
+ if (result.first != ResultStatus::Success) {
return result;
+ }
is_loaded = true;
-
- return ResultStatus::Success;
+ return result;
}
ResultStatus AppLoader_NAX::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index f40079574..00f1659c1 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -33,7 +33,7 @@ public:
FileType GetFileType() const override;
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index ce8196fcf..b3f8f1083 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -30,36 +30,38 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-ResultStatus AppLoader_NCA::Load(Kernel::Process& process) {
+AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
const auto result = nca->GetStatus();
if (result != ResultStatus::Success) {
- return result;
+ return {result, {}};
}
- if (nca->GetType() != FileSys::NCAContentType::Program)
- return ResultStatus::ErrorNCANotProgram;
+ if (nca->GetType() != FileSys::NCAContentType::Program) {
+ return {ResultStatus::ErrorNCANotProgram, {}};
+ }
const auto exefs = nca->GetExeFS();
-
- if (exefs == nullptr)
- return ResultStatus::ErrorNoExeFS;
+ if (exefs == nullptr) {
+ return {ResultStatus::ErrorNoExeFS, {}};
+ }
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
const auto load_result = directory_loader->Load(process);
- if (load_result != ResultStatus::Success)
+ if (load_result.first != ResultStatus::Success) {
return load_result;
+ }
- if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0)
+ if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
+ }
is_loaded = true;
-
- return ResultStatus::Success;
+ return load_result;
}
ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) {
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index b9f077468..94f0ed677 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -33,7 +33,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 4fad0c0dd..6a0ca389b 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -14,6 +14,7 @@
#include "core/file_sys/romfs_factory.h"
#include "core/file_sys/vfs_offset.h"
#include "core/gdbstub/gdbstub.h"
+#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -186,7 +187,7 @@ static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
// Load codeset for current process
- codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
+ codeset.memory = std::move(program_image);
process.LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
@@ -200,25 +201,25 @@ bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& fi
return LoadNroImpl(process, file.ReadAllBytes(), file.GetName(), load_base);
}
-ResultStatus AppLoader_NRO::Load(Kernel::Process& process) {
+AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
// Load NRO
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
if (!LoadNro(process, *file, base_address)) {
- return ResultStatus::ErrorLoadingNRO;
+ return {ResultStatus::ErrorLoadingNRO, {}};
}
- if (romfs != nullptr)
+ if (romfs != nullptr) {
Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
-
- process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
+ }
is_loaded = true;
- return ResultStatus::Success;
+ return {ResultStatus::Success,
+ LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
}
ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 013d629c0..1ffdae805 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -4,10 +4,10 @@
#pragma once
+#include <memory>
#include <string>
#include <vector>
#include "common/common_types.h"
-#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace FileSys {
@@ -21,7 +21,7 @@ class Process;
namespace Loader {
/// Loads an NRO file
-class AppLoader_NRO final : public AppLoader, Linker {
+class AppLoader_NRO final : public AppLoader {
public:
explicit AppLoader_NRO(FileSys::VirtualFile file);
~AppLoader_NRO() override;
@@ -37,7 +37,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 6ded0b707..80090b792 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -4,13 +4,17 @@
#include <cinttypes>
#include <vector>
-#include <lz4.h>
+
#include "common/common_funcs.h"
#include "common/file_util.h"
+#include "common/hex_util.h"
#include "common/logging/log.h"
+#include "common/lz4_compression.h"
#include "common/swap.h"
+#include "core/core.h"
#include "core/file_sys/patch_manager.h"
#include "core/gdbstub/gdbstub.h"
+#include "core/hle/kernel/code_set.h"
#include "core/hle/kernel/process.h"
#include "core/hle/kernel/vm_manager.h"
#include "core/loader/nso.h"
@@ -18,36 +22,8 @@
#include "core/settings.h"
namespace Loader {
-
-struct NsoSegmentHeader {
- u32_le offset;
- u32_le location;
- u32_le size;
- union {
- u32_le alignment;
- u32_le bss_size;
- };
-};
-static_assert(sizeof(NsoSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
-
-struct NsoHeader {
- u32_le magic;
- u32_le version;
- INSERT_PADDING_WORDS(1);
- u8 flags;
- std::array<NsoSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
- std::array<u8, 0x20> build_id;
- std::array<u32_le, 3> segments_compressed_size;
-
- bool IsSegmentCompressed(size_t segment_num) const {
- ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
- return ((flags >> segment_num) & 1);
- }
-};
-static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size.");
-static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable.");
-
-struct ModHeader {
+namespace {
+struct MODHeader {
u32_le magic;
u32_le dynamic_offset;
u32_le bss_start_offset;
@@ -56,7 +32,28 @@ struct ModHeader {
u32_le eh_frame_hdr_end_offset;
u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
};
-static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
+static_assert(sizeof(MODHeader) == 0x1c, "MODHeader has incorrect size.");
+
+std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
+ const NSOSegmentHeader& header) {
+ const std::vector<u8> uncompressed_data =
+ Common::Compression::DecompressDataLZ4(compressed_data, header.size);
+
+ ASSERT_MSG(uncompressed_data.size() == header.size, "{} != {}", header.size,
+ uncompressed_data.size());
+
+ return uncompressed_data;
+}
+
+constexpr u32 PageAlignSize(u32 size) {
+ return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
+}
+} // Anonymous namespace
+
+bool NSOHeader::IsSegmentCompressed(size_t segment_num) const {
+ ASSERT_MSG(segment_num < 3, "Invalid segment {}", segment_num);
+ return ((flags >> segment_num) & 1) != 0;
+}
AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {}
@@ -73,38 +70,22 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::NSO;
}
-static std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
- const NsoSegmentHeader& header) {
- std::vector<u8> uncompressed_data(header.size);
- const int bytes_uncompressed =
- LZ4_decompress_safe(reinterpret_cast<const char*>(compressed_data.data()),
- reinterpret_cast<char*>(uncompressed_data.data()),
- static_cast<int>(compressed_data.size()), header.size);
-
- ASSERT_MSG(bytes_uncompressed == static_cast<int>(header.size) &&
- bytes_uncompressed == static_cast<int>(uncompressed_data.size()),
- "{} != {} != {}", bytes_uncompressed, header.size, uncompressed_data.size());
-
- return uncompressed_data;
-}
-
-static constexpr u32 PageAlignSize(u32 size) {
- return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK;
-}
-
std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
const FileSys::VfsFile& file, VAddr load_base,
bool should_pass_arguments,
std::optional<FileSys::PatchManager> pm) {
- if (file.GetSize() < sizeof(NsoHeader))
+ if (file.GetSize() < sizeof(NSOHeader)) {
return {};
+ }
- NsoHeader nso_header{};
- if (sizeof(NsoHeader) != file.ReadObject(&nso_header))
+ NSOHeader nso_header{};
+ if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
return {};
+ }
- if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0'))
+ if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
return {};
+ }
// Build program image
Kernel::CodeSet codeset;
@@ -140,10 +121,10 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32));
// Read MOD header
- ModHeader mod_header{};
+ MODHeader mod_header{};
// Default .bss to size in segment header if MOD0 section doesn't exist
u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)};
- std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader));
+ std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(MODHeader));
const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
if (has_mod_header) {
// Resize program image to include .bss section and page align each section
@@ -155,17 +136,29 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
// Apply patches if necessary
if (pm && (pm->HasNSOPatch(nso_header.build_id) || Settings::values.dump_nso)) {
- std::vector<u8> pi_header(program_image.size() + 0x100);
- std::memcpy(pi_header.data(), &nso_header, sizeof(NsoHeader));
- std::memcpy(pi_header.data() + 0x100, program_image.data(), program_image.size());
+ std::vector<u8> pi_header;
+ pi_header.insert(pi_header.begin(), reinterpret_cast<u8*>(&nso_header),
+ reinterpret_cast<u8*>(&nso_header) + sizeof(NSOHeader));
+ pi_header.insert(pi_header.begin() + sizeof(NSOHeader), program_image.begin(),
+ program_image.end());
- pi_header = pm->PatchNSO(pi_header);
+ pi_header = pm->PatchNSO(pi_header, file.GetName());
- std::memcpy(program_image.data(), pi_header.data() + 0x100, program_image.size());
+ std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.begin());
+ }
+
+ // Apply cheats if they exist and the program has a valid title ID
+ if (pm) {
+ auto& system = Core::System::GetInstance();
+ const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
+ if (!cheats.empty()) {
+ system.RegisterCheatList(cheats, Common::HexToString(nso_header.build_id), load_base,
+ load_base + program_image.size());
+ }
}
// Load codeset for current process
- codeset.memory = std::make_shared<std::vector<u8>>(std::move(program_image));
+ codeset.memory = std::move(program_image);
process.LoadModule(std::move(codeset), load_base);
// Register module with GDBStub
@@ -174,22 +167,21 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
return load_base + image_size;
}
-ResultStatus AppLoader_NSO::Load(Kernel::Process& process) {
+AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
// Load module
const VAddr base_address = process.VMManager().GetCodeRegionBaseAddress();
if (!LoadModule(process, *file, base_address, true)) {
- return ResultStatus::ErrorLoadingNSO;
+ return {ResultStatus::ErrorLoadingNSO, {}};
}
LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address);
- process.Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE);
-
is_loaded = true;
- return ResultStatus::Success;
+ return {ResultStatus::Success,
+ LoadParameters{Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE}};
}
} // namespace Loader
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index 135b6ea5a..fdce9191c 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -4,10 +4,12 @@
#pragma once
+#include <array>
#include <optional>
+#include <type_traits>
#include "common/common_types.h"
+#include "common/swap.h"
#include "core/file_sys/patch_manager.h"
-#include "core/loader/linker.h"
#include "core/loader/loader.h"
namespace Kernel {
@@ -16,6 +18,43 @@ class Process;
namespace Loader {
+struct NSOSegmentHeader {
+ u32_le offset;
+ u32_le location;
+ u32_le size;
+ union {
+ u32_le alignment;
+ u32_le bss_size;
+ };
+};
+static_assert(sizeof(NSOSegmentHeader) == 0x10, "NsoSegmentHeader has incorrect size.");
+
+struct NSOHeader {
+ using SHA256Hash = std::array<u8, 0x20>;
+
+ struct RODataRelativeExtent {
+ u32_le data_offset;
+ u32_le size;
+ };
+
+ u32_le magic;
+ u32_le version;
+ u32 reserved;
+ u32_le flags;
+ std::array<NSOSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
+ std::array<u8, 0x20> build_id;
+ std::array<u32_le, 3> segments_compressed_size;
+ std::array<u8, 0x1C> padding;
+ RODataRelativeExtent api_info_extent;
+ RODataRelativeExtent dynstr_extent;
+ RODataRelativeExtent dynsyn_extent;
+ std::array<SHA256Hash, 3> segment_hashes;
+
+ bool IsSegmentCompressed(size_t segment_num) const;
+};
+static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
+static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
+
constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
struct NSOArgumentHeader {
@@ -26,7 +65,7 @@ struct NSOArgumentHeader {
static_assert(sizeof(NSOArgumentHeader) == 0x20, "NSOArgumentHeader has incorrect size.");
/// Loads an NSO file
-class AppLoader_NSO final : public AppLoader, Linker {
+class AppLoader_NSO final : public AppLoader {
public:
explicit AppLoader_NSO(FileSys::VirtualFile file);
@@ -45,7 +84,7 @@ public:
VAddr load_base, bool should_pass_arguments,
std::optional<FileSys::PatchManager> pm = {});
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
};
} // namespace Loader
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 7da1f8960..ad56bbb38 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -72,37 +72,45 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-ResultStatus AppLoader_NSP::Load(Kernel::Process& process) {
+AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
- if (title_id == 0)
- return ResultStatus::ErrorNSPMissingProgramNCA;
+ if (title_id == 0) {
+ return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
+ }
- if (nsp->GetStatus() != ResultStatus::Success)
- return nsp->GetStatus();
+ const auto nsp_status = nsp->GetStatus();
+ if (nsp_status != ResultStatus::Success) {
+ return {nsp_status, {}};
+ }
- if (nsp->GetProgramStatus(title_id) != ResultStatus::Success)
- return nsp->GetProgramStatus(title_id);
+ const auto nsp_program_status = nsp->GetProgramStatus(title_id);
+ if (nsp_program_status != ResultStatus::Success) {
+ return {nsp_program_status, {}};
+ }
if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) {
- if (!Core::Crypto::KeyManager::KeyFileExists(false))
- return ResultStatus::ErrorMissingProductionKeyFile;
- return ResultStatus::ErrorNSPMissingProgramNCA;
+ if (!Core::Crypto::KeyManager::KeyFileExists(false)) {
+ return {ResultStatus::ErrorMissingProductionKeyFile, {}};
+ }
+
+ return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
}
const auto result = secondary_loader->Load(process);
- if (result != ResultStatus::Success)
+ if (result.first != ResultStatus::Success) {
return result;
+ }
FileSys::VirtualFile update_raw;
- if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
+ if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
+ }
is_loaded = true;
-
- return ResultStatus::Success;
+ return result;
}
ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& file) {
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 953a1b508..85e870bdf 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -35,7 +35,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 89f7bbf77..1e285a053 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -48,31 +48,35 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-ResultStatus AppLoader_XCI::Load(Kernel::Process& process) {
+AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
if (is_loaded) {
- return ResultStatus::ErrorAlreadyLoaded;
+ return {ResultStatus::ErrorAlreadyLoaded, {}};
}
- if (xci->GetStatus() != ResultStatus::Success)
- return xci->GetStatus();
+ if (xci->GetStatus() != ResultStatus::Success) {
+ return {xci->GetStatus(), {}};
+ }
- if (xci->GetProgramNCAStatus() != ResultStatus::Success)
- return xci->GetProgramNCAStatus();
+ if (xci->GetProgramNCAStatus() != ResultStatus::Success) {
+ return {xci->GetProgramNCAStatus(), {}};
+ }
- if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false))
- return ResultStatus::ErrorMissingProductionKeyFile;
+ if (!xci->HasProgramNCA() && !Core::Crypto::KeyManager::KeyFileExists(false)) {
+ return {ResultStatus::ErrorMissingProductionKeyFile, {}};
+ }
const auto result = nca_loader->Load(process);
- if (result != ResultStatus::Success)
+ if (result.first != ResultStatus::Success) {
return result;
+ }
FileSys::VirtualFile update_raw;
- if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr)
+ if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
Service::FileSystem::SetPackedUpdate(std::move(update_raw));
+ }
is_loaded = true;
-
- return ResultStatus::Success;
+ return result;
}
ResultStatus AppLoader_XCI::ReadRomFS(FileSys::VirtualFile& file) {
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index d6995b61e..ae7145b14 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -22,7 +22,7 @@ class AppLoader_NCA;
class AppLoader_XCI final : public AppLoader {
public:
explicit AppLoader_XCI(FileSys::VirtualFile file);
- ~AppLoader_XCI();
+ ~AppLoader_XCI() override;
/**
* Returns the type of the file
@@ -35,7 +35,7 @@ public:
return IdentifyType(file);
}
- ResultStatus Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;