diff options
| author | 2023-11-30 09:20:55 -0500 | |
|---|---|---|
| committer | 2023-11-30 09:20:55 -0500 | |
| commit | 57a391e71db13ade7a3d96f59d53781eff18d2ac (patch) | |
| tree | 0b4223de40a2d77598ac9095b1374353c2e9da7c /src/core/loader | |
| parent | Merge pull request #12223 from liamwhite/fruit-company (diff) | |
| parent | core: Rename patcher file (diff) | |
| download | yuzu-57a391e71db13ade7a3d96f59d53781eff18d2ac.tar.gz yuzu-57a391e71db13ade7a3d96f59d53781eff18d2ac.tar.xz yuzu-57a391e71db13ade7a3d96f59d53781eff18d2ac.zip | |
Merge pull request #12074 from GPUCode/yuwu-on-the-metal
Implement Native Code Execution (NCE)
Diffstat (limited to 'src/core/loader')
| -rw-r--r-- | src/core/loader/deconstructed_rom_directory.cpp | 63 | ||||
| -rw-r--r-- | src/core/loader/kip.cpp | 3 | ||||
| -rw-r--r-- | src/core/loader/nro.cpp | 63 | ||||
| -rw-r--r-- | src/core/loader/nro.h | 2 | ||||
| -rw-r--r-- | src/core/loader/nso.cpp | 67 | ||||
| -rw-r--r-- | src/core/loader/nso.h | 7 |
6 files changed, 174 insertions, 31 deletions
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 5c36b71e5..60ee78e89 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | 3 | ||
| 4 | #include <cstring> | 4 | #include <cstring> |
| 5 | #include "common/logging/log.h" | 5 | #include "common/logging/log.h" |
| 6 | #include "common/settings.h" | ||
| 6 | #include "core/core.h" | 7 | #include "core/core.h" |
| 7 | #include "core/file_sys/content_archive.h" | 8 | #include "core/file_sys/content_archive.h" |
| 8 | #include "core/file_sys/control_metadata.h" | 9 | #include "core/file_sys/control_metadata.h" |
| @@ -14,6 +15,10 @@ | |||
| 14 | #include "core/loader/deconstructed_rom_directory.h" | 15 | #include "core/loader/deconstructed_rom_directory.h" |
| 15 | #include "core/loader/nso.h" | 16 | #include "core/loader/nso.h" |
| 16 | 17 | ||
| 18 | #ifdef HAS_NCE | ||
| 19 | #include "core/arm/nce/patcher.h" | ||
| 20 | #endif | ||
| 21 | |||
| 17 | namespace Loader { | 22 | namespace Loader { |
| 18 | 23 | ||
| 19 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, | 24 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file_, |
| @@ -124,21 +129,43 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 124 | } | 129 | } |
| 125 | metadata.Print(); | 130 | metadata.Print(); |
| 126 | 131 | ||
| 127 | const auto static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", | 132 | // Enable NCE only for programs with 39-bit address space. |
| 128 | "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", | 133 | const bool is_39bit = |
| 129 | "subsdk8", "subsdk9", "sdk"}; | 134 | metadata.GetAddressSpaceType() == FileSys::ProgramAddressSpaceType::Is39Bit; |
| 135 | Settings::SetNceEnabled(is_39bit); | ||
| 136 | |||
| 137 | const std::array static_modules = {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", | ||
| 138 | "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", | ||
| 139 | "subsdk8", "subsdk9", "sdk"}; | ||
| 130 | 140 | ||
| 131 | // Use the NSO module loader to figure out the code layout | ||
| 132 | std::size_t code_size{}; | 141 | std::size_t code_size{}; |
| 133 | for (const auto& module : static_modules) { | 142 | |
| 143 | // Define an nce patch context for each potential module. | ||
| 144 | #ifdef HAS_NCE | ||
| 145 | std::array<Core::NCE::Patcher, 13> module_patchers; | ||
| 146 | #endif | ||
| 147 | |||
| 148 | const auto GetPatcher = [&](size_t i) -> Core::NCE::Patcher* { | ||
| 149 | #ifdef HAS_NCE | ||
| 150 | if (Settings::IsNceEnabled()) { | ||
| 151 | return &module_patchers[i]; | ||
| 152 | } | ||
| 153 | #endif | ||
| 154 | return nullptr; | ||
| 155 | }; | ||
| 156 | |||
| 157 | // Use the NSO module loader to figure out the code layout | ||
| 158 | for (size_t i = 0; i < static_modules.size(); i++) { | ||
| 159 | const auto& module = static_modules[i]; | ||
| 134 | const FileSys::VirtualFile module_file{dir->GetFile(module)}; | 160 | const FileSys::VirtualFile module_file{dir->GetFile(module)}; |
| 135 | if (!module_file) { | 161 | if (!module_file) { |
| 136 | continue; | 162 | continue; |
| 137 | } | 163 | } |
| 138 | 164 | ||
| 139 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; | 165 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; |
| 140 | const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( | 166 | const auto tentative_next_load_addr = |
| 141 | process, system, *module_file, code_size, should_pass_arguments, false); | 167 | AppLoader_NSO::LoadModule(process, system, *module_file, code_size, |
| 168 | should_pass_arguments, false, {}, GetPatcher(i)); | ||
| 142 | if (!tentative_next_load_addr) { | 169 | if (!tentative_next_load_addr) { |
| 143 | return {ResultStatus::ErrorLoadingNSO, {}}; | 170 | return {ResultStatus::ErrorLoadingNSO, {}}; |
| 144 | } | 171 | } |
| @@ -146,8 +173,18 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 146 | code_size = *tentative_next_load_addr; | 173 | code_size = *tentative_next_load_addr; |
| 147 | } | 174 | } |
| 148 | 175 | ||
| 176 | // Enable direct memory mapping in case of NCE. | ||
| 177 | const u64 fastmem_base = [&]() -> size_t { | ||
| 178 | if (Settings::IsNceEnabled()) { | ||
| 179 | auto& buffer = system.DeviceMemory().buffer; | ||
| 180 | buffer.EnableDirectMappedAddress(); | ||
| 181 | return reinterpret_cast<u64>(buffer.VirtualBasePointer()); | ||
| 182 | } | ||
| 183 | return 0; | ||
| 184 | }(); | ||
| 185 | |||
| 149 | // Setup the process code layout | 186 | // Setup the process code layout |
| 150 | if (process.LoadFromMetadata(metadata, code_size, is_hbl).IsError()) { | 187 | if (process.LoadFromMetadata(metadata, code_size, fastmem_base, is_hbl).IsError()) { |
| 151 | return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; | 188 | return {ResultStatus::ErrorUnableToParseKernelMetadata, {}}; |
| 152 | } | 189 | } |
| 153 | 190 | ||
| @@ -157,7 +194,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 157 | VAddr next_load_addr{base_address}; | 194 | VAddr next_load_addr{base_address}; |
| 158 | const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), | 195 | const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(), |
| 159 | system.GetContentProvider()}; | 196 | system.GetContentProvider()}; |
| 160 | for (const auto& module : static_modules) { | 197 | for (size_t i = 0; i < static_modules.size(); i++) { |
| 198 | const auto& module = static_modules[i]; | ||
| 161 | const FileSys::VirtualFile module_file{dir->GetFile(module)}; | 199 | const FileSys::VirtualFile module_file{dir->GetFile(module)}; |
| 162 | if (!module_file) { | 200 | if (!module_file) { |
| 163 | continue; | 201 | continue; |
| @@ -165,15 +203,16 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect | |||
| 165 | 203 | ||
| 166 | const VAddr load_addr{next_load_addr}; | 204 | const VAddr load_addr{next_load_addr}; |
| 167 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; | 205 | const bool should_pass_arguments = std::strcmp(module, "rtld") == 0; |
| 168 | const auto tentative_next_load_addr = AppLoader_NSO::LoadModule( | 206 | const auto tentative_next_load_addr = |
| 169 | process, system, *module_file, load_addr, should_pass_arguments, true, pm); | 207 | AppLoader_NSO::LoadModule(process, system, *module_file, load_addr, |
| 208 | should_pass_arguments, true, pm, GetPatcher(i)); | ||
| 170 | if (!tentative_next_load_addr) { | 209 | if (!tentative_next_load_addr) { |
| 171 | return {ResultStatus::ErrorLoadingNSO, {}}; | 210 | return {ResultStatus::ErrorLoadingNSO, {}}; |
| 172 | } | 211 | } |
| 173 | 212 | ||
| 174 | next_load_addr = *tentative_next_load_addr; | 213 | next_load_addr = *tentative_next_load_addr; |
| 175 | modules.insert_or_assign(load_addr, module); | 214 | modules.insert_or_assign(load_addr, module); |
| 176 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 215 | LOG_DEBUG(Loader, "loaded module {} @ {:#X}", module, load_addr); |
| 177 | } | 216 | } |
| 178 | 217 | ||
| 179 | // Find the RomFS by searching for a ".romfs" file in this directory | 218 | // Find the RomFS by searching for a ".romfs" file in this directory |
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp index bf56a08b4..cd6982921 100644 --- a/src/core/loader/kip.cpp +++ b/src/core/loader/kip.cpp | |||
| @@ -91,7 +91,8 @@ AppLoader::LoadResult AppLoader_KIP::Load(Kernel::KProcess& process, | |||
| 91 | 91 | ||
| 92 | // Setup the process code layout | 92 | // Setup the process code layout |
| 93 | if (process | 93 | if (process |
| 94 | .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), false) | 94 | .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), 0, |
| 95 | false) | ||
| 95 | .IsError()) { | 96 | .IsError()) { |
| 96 | return {ResultStatus::ErrorNotInitialized, {}}; | 97 | return {ResultStatus::ErrorNotInitialized, {}}; |
| 97 | } | 98 | } |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 69f1a54ed..e74697cda 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -22,6 +22,10 @@ | |||
| 22 | #include "core/loader/nso.h" | 22 | #include "core/loader/nso.h" |
| 23 | #include "core/memory.h" | 23 | #include "core/memory.h" |
| 24 | 24 | ||
| 25 | #ifdef HAS_NCE | ||
| 26 | #include "core/arm/nce/patcher.h" | ||
| 27 | #endif | ||
| 28 | |||
| 25 | namespace Loader { | 29 | namespace Loader { |
| 26 | 30 | ||
| 27 | struct NroSegmentHeader { | 31 | struct NroSegmentHeader { |
| @@ -139,7 +143,8 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 139 | return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); | 143 | return static_cast<u32>((size + Core::Memory::YUZU_PAGEMASK) & ~Core::Memory::YUZU_PAGEMASK); |
| 140 | } | 144 | } |
| 141 | 145 | ||
| 142 | static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) { | 146 | static bool LoadNroImpl(Core::System& system, Kernel::KProcess& process, |
| 147 | const std::vector<u8>& data) { | ||
| 143 | if (data.size() < sizeof(NroHeader)) { | 148 | if (data.size() < sizeof(NroHeader)) { |
| 144 | return {}; | 149 | return {}; |
| 145 | } | 150 | } |
| @@ -194,14 +199,61 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) | |||
| 194 | 199 | ||
| 195 | codeset.DataSegment().size += bss_size; | 200 | codeset.DataSegment().size += bss_size; |
| 196 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); | 201 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); |
| 202 | size_t image_size = program_image.size(); | ||
| 203 | |||
| 204 | #ifdef HAS_NCE | ||
| 205 | const auto& code = codeset.CodeSegment(); | ||
| 206 | |||
| 207 | // NROs always have a 39-bit address space. | ||
| 208 | Settings::SetNceEnabled(true); | ||
| 209 | |||
| 210 | // Create NCE patcher | ||
| 211 | Core::NCE::Patcher patch{}; | ||
| 212 | |||
| 213 | if (Settings::IsNceEnabled()) { | ||
| 214 | // Patch SVCs and MRS calls in the guest code | ||
| 215 | patch.PatchText(program_image, code); | ||
| 216 | |||
| 217 | // We only support PostData patching for NROs. | ||
| 218 | ASSERT(patch.GetPatchMode() == Core::NCE::PatchMode::PostData); | ||
| 219 | |||
| 220 | // Update patch section. | ||
| 221 | auto& patch_segment = codeset.PatchSegment(); | ||
| 222 | patch_segment.addr = image_size; | ||
| 223 | patch_segment.size = static_cast<u32>(patch.GetSectionSize()); | ||
| 224 | |||
| 225 | // Add patch section size to the module size. | ||
| 226 | image_size += patch_segment.size; | ||
| 227 | } | ||
| 228 | #endif | ||
| 229 | |||
| 230 | // Enable direct memory mapping in case of NCE. | ||
| 231 | const u64 fastmem_base = [&]() -> size_t { | ||
| 232 | if (Settings::IsNceEnabled()) { | ||
| 233 | auto& buffer = system.DeviceMemory().buffer; | ||
| 234 | buffer.EnableDirectMappedAddress(); | ||
| 235 | return reinterpret_cast<u64>(buffer.VirtualBasePointer()); | ||
| 236 | } | ||
| 237 | return 0; | ||
| 238 | }(); | ||
| 197 | 239 | ||
| 198 | // Setup the process code layout | 240 | // Setup the process code layout |
| 199 | if (process | 241 | if (process |
| 200 | .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size(), false) | 242 | .LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), image_size, fastmem_base, |
| 243 | false) | ||
| 201 | .IsError()) { | 244 | .IsError()) { |
| 202 | return false; | 245 | return false; |
| 203 | } | 246 | } |
| 204 | 247 | ||
| 248 | // Relocate code patch and copy to the program_image if running under NCE. | ||
| 249 | // This needs to be after LoadFromMetadata so we can use the process entry point. | ||
| 250 | #ifdef HAS_NCE | ||
| 251 | if (Settings::IsNceEnabled()) { | ||
| 252 | patch.RelocateAndCopy(process.GetEntryPoint(), code, program_image, | ||
| 253 | &process.GetPostHandlers()); | ||
| 254 | } | ||
| 255 | #endif | ||
| 256 | |||
| 205 | // Load codeset for current process | 257 | // Load codeset for current process |
| 206 | codeset.memory = std::move(program_image); | 258 | codeset.memory = std::move(program_image); |
| 207 | process.LoadModule(std::move(codeset), process.GetEntryPoint()); | 259 | process.LoadModule(std::move(codeset), process.GetEntryPoint()); |
| @@ -209,8 +261,9 @@ static bool LoadNroImpl(Kernel::KProcess& process, const std::vector<u8>& data) | |||
| 209 | return true; | 261 | return true; |
| 210 | } | 262 | } |
| 211 | 263 | ||
| 212 | bool AppLoader_NRO::LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file) { | 264 | bool AppLoader_NRO::LoadNro(Core::System& system, Kernel::KProcess& process, |
| 213 | return LoadNroImpl(process, nro_file.ReadAllBytes()); | 265 | const FileSys::VfsFile& nro_file) { |
| 266 | return LoadNroImpl(system, process, nro_file.ReadAllBytes()); | ||
| 214 | } | 267 | } |
| 215 | 268 | ||
| 216 | AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) { | 269 | AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::System& system) { |
| @@ -218,7 +271,7 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::KProcess& process, Core::S | |||
| 218 | return {ResultStatus::ErrorAlreadyLoaded, {}}; | 271 | return {ResultStatus::ErrorAlreadyLoaded, {}}; |
| 219 | } | 272 | } |
| 220 | 273 | ||
| 221 | if (!LoadNro(process, *file)) { | 274 | if (!LoadNro(system, process, *file)) { |
| 222 | return {ResultStatus::ErrorLoadingNRO, {}}; | 275 | return {ResultStatus::ErrorLoadingNRO, {}}; |
| 223 | } | 276 | } |
| 224 | 277 | ||
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 8de6eebc6..d2928cba0 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -54,7 +54,7 @@ public: | |||
| 54 | bool IsRomFSUpdatable() const override; | 54 | bool IsRomFSUpdatable() const override; |
| 55 | 55 | ||
| 56 | private: | 56 | private: |
| 57 | bool LoadNro(Kernel::KProcess& process, const FileSys::VfsFile& nro_file); | 57 | bool LoadNro(Core::System& system, Kernel::KProcess& process, const FileSys::VfsFile& nro_file); |
| 58 | 58 | ||
| 59 | std::vector<u8> icon_data; | 59 | std::vector<u8> icon_data; |
| 60 | std::unique_ptr<FileSys::NACP> nacp; | 60 | std::unique_ptr<FileSys::NACP> nacp; |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 1350da8dc..b053a0d14 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -20,6 +20,10 @@ | |||
| 20 | #include "core/loader/nso.h" | 20 | #include "core/loader/nso.h" |
| 21 | #include "core/memory.h" | 21 | #include "core/memory.h" |
| 22 | 22 | ||
| 23 | #ifdef HAS_NCE | ||
| 24 | #include "core/arm/nce/patcher.h" | ||
| 25 | #endif | ||
| 26 | |||
| 23 | namespace Loader { | 27 | namespace Loader { |
| 24 | namespace { | 28 | namespace { |
| 25 | struct MODHeader { | 29 | struct MODHeader { |
| @@ -72,7 +76,8 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& in_file) { | |||
| 72 | std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, | 76 | std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core::System& system, |
| 73 | const FileSys::VfsFile& nso_file, VAddr load_base, | 77 | const FileSys::VfsFile& nso_file, VAddr load_base, |
| 74 | bool should_pass_arguments, bool load_into_process, | 78 | bool should_pass_arguments, bool load_into_process, |
| 75 | std::optional<FileSys::PatchManager> pm) { | 79 | std::optional<FileSys::PatchManager> pm, |
| 80 | Core::NCE::Patcher* patch) { | ||
| 76 | if (nso_file.GetSize() < sizeof(NSOHeader)) { | 81 | if (nso_file.GetSize() < sizeof(NSOHeader)) { |
| 77 | return std::nullopt; | 82 | return std::nullopt; |
| 78 | } | 83 | } |
| @@ -86,6 +91,16 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 86 | return std::nullopt; | 91 | return std::nullopt; |
| 87 | } | 92 | } |
| 88 | 93 | ||
| 94 | // Allocate some space at the beginning if we are patching in PreText mode. | ||
| 95 | const size_t module_start = [&]() -> size_t { | ||
| 96 | #ifdef HAS_NCE | ||
| 97 | if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::PreText) { | ||
| 98 | return patch->GetSectionSize(); | ||
| 99 | } | ||
| 100 | #endif | ||
| 101 | return 0; | ||
| 102 | }(); | ||
| 103 | |||
| 89 | // Build program image | 104 | // Build program image |
| 90 | Kernel::CodeSet codeset; | 105 | Kernel::CodeSet codeset; |
| 91 | Kernel::PhysicalMemory program_image; | 106 | Kernel::PhysicalMemory program_image; |
| @@ -95,11 +110,12 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 95 | if (nso_header.IsSegmentCompressed(i)) { | 110 | if (nso_header.IsSegmentCompressed(i)) { |
| 96 | data = DecompressSegment(data, nso_header.segments[i]); | 111 | data = DecompressSegment(data, nso_header.segments[i]); |
| 97 | } | 112 | } |
| 98 | program_image.resize(nso_header.segments[i].location + static_cast<u32>(data.size())); | 113 | program_image.resize(module_start + nso_header.segments[i].location + |
| 99 | std::memcpy(program_image.data() + nso_header.segments[i].location, data.data(), | 114 | static_cast<u32>(data.size())); |
| 100 | data.size()); | 115 | std::memcpy(program_image.data() + module_start + nso_header.segments[i].location, |
| 101 | codeset.segments[i].addr = nso_header.segments[i].location; | 116 | data.data(), data.size()); |
| 102 | codeset.segments[i].offset = nso_header.segments[i].location; | 117 | codeset.segments[i].addr = module_start + nso_header.segments[i].location; |
| 118 | codeset.segments[i].offset = module_start + nso_header.segments[i].location; | ||
| 103 | codeset.segments[i].size = nso_header.segments[i].size; | 119 | codeset.segments[i].size = nso_header.segments[i].size; |
| 104 | } | 120 | } |
| 105 | 121 | ||
| @@ -118,7 +134,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 118 | } | 134 | } |
| 119 | 135 | ||
| 120 | codeset.DataSegment().size += nso_header.segments[2].bss_size; | 136 | codeset.DataSegment().size += nso_header.segments[2].bss_size; |
| 121 | const u32 image_size{ | 137 | u32 image_size{ |
| 122 | PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)}; | 138 | PageAlignSize(static_cast<u32>(program_image.size()) + nso_header.segments[2].bss_size)}; |
| 123 | program_image.resize(image_size); | 139 | program_image.resize(image_size); |
| 124 | 140 | ||
| @@ -129,15 +145,44 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::KProcess& process, Core:: | |||
| 129 | // Apply patches if necessary | 145 | // Apply patches if necessary |
| 130 | const auto name = nso_file.GetName(); | 146 | const auto name = nso_file.GetName(); |
| 131 | if (pm && (pm->HasNSOPatch(nso_header.build_id, name) || Settings::values.dump_nso)) { | 147 | if (pm && (pm->HasNSOPatch(nso_header.build_id, name) || Settings::values.dump_nso)) { |
| 132 | std::vector<u8> pi_header(sizeof(NSOHeader) + program_image.size()); | 148 | std::span<u8> patchable_section(program_image.data() + module_start, |
| 149 | program_image.size() - module_start); | ||
| 150 | std::vector<u8> pi_header(sizeof(NSOHeader) + patchable_section.size()); | ||
| 133 | std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); | 151 | std::memcpy(pi_header.data(), &nso_header, sizeof(NSOHeader)); |
| 134 | std::memcpy(pi_header.data() + sizeof(NSOHeader), program_image.data(), | 152 | std::memcpy(pi_header.data() + sizeof(NSOHeader), patchable_section.data(), |
| 135 | program_image.size()); | 153 | patchable_section.size()); |
| 136 | 154 | ||
| 137 | pi_header = pm->PatchNSO(pi_header, name); | 155 | pi_header = pm->PatchNSO(pi_header, name); |
| 138 | 156 | ||
| 139 | std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), program_image.data()); | 157 | std::copy(pi_header.begin() + sizeof(NSOHeader), pi_header.end(), patchable_section.data()); |
| 158 | } | ||
| 159 | |||
| 160 | #ifdef HAS_NCE | ||
| 161 | // If we are computing the process code layout and using nce backend, patch. | ||
| 162 | const auto& code = codeset.CodeSegment(); | ||
| 163 | if (patch && patch->GetPatchMode() == Core::NCE::PatchMode::None) { | ||
| 164 | // Patch SVCs and MRS calls in the guest code | ||
| 165 | patch->PatchText(program_image, code); | ||
| 166 | |||
| 167 | // Add patch section size to the module size. | ||
| 168 | image_size += static_cast<u32>(patch->GetSectionSize()); | ||
| 169 | } else if (patch) { | ||
| 170 | // Relocate code patch and copy to the program_image. | ||
| 171 | patch->RelocateAndCopy(load_base, code, program_image, &process.GetPostHandlers()); | ||
| 172 | |||
| 173 | // Update patch section. | ||
| 174 | auto& patch_segment = codeset.PatchSegment(); | ||
| 175 | patch_segment.addr = | ||
| 176 | patch->GetPatchMode() == Core::NCE::PatchMode::PreText ? 0 : image_size; | ||
| 177 | patch_segment.size = static_cast<u32>(patch->GetSectionSize()); | ||
| 178 | |||
| 179 | // Add patch section size to the module size. In PreText mode image_size | ||
| 180 | // already contains the patch segment as part of module_start. | ||
| 181 | if (patch->GetPatchMode() == Core::NCE::PatchMode::PostData) { | ||
| 182 | image_size += patch_segment.size; | ||
| 183 | } | ||
| 140 | } | 184 | } |
| 185 | #endif | ||
| 141 | 186 | ||
| 142 | // If we aren't actually loading (i.e. just computing the process code layout), we are done | 187 | // If we aren't actually loading (i.e. just computing the process code layout), we are done |
| 143 | if (!load_into_process) { | 188 | if (!load_into_process) { |
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 0b53b4ecd..29b86ed4c 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -15,6 +15,10 @@ namespace Core { | |||
| 15 | class System; | 15 | class System; |
| 16 | } | 16 | } |
| 17 | 17 | ||
| 18 | namespace Core::NCE { | ||
| 19 | class Patcher; | ||
| 20 | } | ||
| 21 | |||
| 18 | namespace Kernel { | 22 | namespace Kernel { |
| 19 | class KProcess; | 23 | class KProcess; |
| 20 | } | 24 | } |
| @@ -88,7 +92,8 @@ public: | |||
| 88 | static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system, | 92 | static std::optional<VAddr> LoadModule(Kernel::KProcess& process, Core::System& system, |
| 89 | const FileSys::VfsFile& nso_file, VAddr load_base, | 93 | const FileSys::VfsFile& nso_file, VAddr load_base, |
| 90 | bool should_pass_arguments, bool load_into_process, | 94 | bool should_pass_arguments, bool load_into_process, |
| 91 | std::optional<FileSys::PatchManager> pm = {}); | 95 | std::optional<FileSys::PatchManager> pm = {}, |
| 96 | Core::NCE::Patcher* patch = nullptr); | ||
| 92 | 97 | ||
| 93 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; | 98 | LoadResult Load(Kernel::KProcess& process, Core::System& system) override; |
| 94 | 99 | ||