1 // Copyright 2018 yuzu emulator team
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include <utility>
6 #include <vector>
7 
8 #include "common/common_funcs.h"
9 #include "common/common_types.h"
10 #include "common/file_util.h"
11 #include "common/logging/log.h"
12 #include "common/swap.h"
13 #include "core/core.h"
14 #include "core/file_sys/control_metadata.h"
15 #include "core/file_sys/romfs_factory.h"
16 #include "core/file_sys/vfs_offset.h"
17 #include "core/hle/kernel/code_set.h"
18 #include "core/hle/kernel/memory/page_table.h"
19 #include "core/hle/kernel/process.h"
20 #include "core/hle/kernel/thread.h"
21 #include "core/hle/service/filesystem/filesystem.h"
22 #include "core/loader/nro.h"
23 #include "core/loader/nso.h"
24 #include "core/memory.h"
25 #include "core/settings.h"
26 
27 namespace Loader {
28 
29 struct NroSegmentHeader {
30     u32_le offset;
31     u32_le size;
32 };
33 static_assert(sizeof(NroSegmentHeader) == 0x8, "NroSegmentHeader has incorrect size.");
34 
35 struct NroHeader {
36     INSERT_PADDING_BYTES(0x4);
37     u32_le module_header_offset;
38     INSERT_PADDING_BYTES(0x8);
39     u32_le magic;
40     INSERT_PADDING_BYTES(0x4);
41     u32_le file_size;
42     INSERT_PADDING_BYTES(0x4);
43     std::array<NroSegmentHeader, 3> segments; // Text, RoData, Data (in that order)
44     u32_le bss_size;
45     INSERT_PADDING_BYTES(0x44);
46 };
47 static_assert(sizeof(NroHeader) == 0x80, "NroHeader has incorrect size.");
48 
49 struct ModHeader {
50     u32_le magic;
51     u32_le dynamic_offset;
52     u32_le bss_start_offset;
53     u32_le bss_end_offset;
54     u32_le unwind_start_offset;
55     u32_le unwind_end_offset;
56     u32_le module_offset; // Offset to runtime-generated module object. typically equal to .bss base
57 };
58 static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size.");
59 
60 struct AssetSection {
61     u64_le offset;
62     u64_le size;
63 };
64 static_assert(sizeof(AssetSection) == 0x10, "AssetSection has incorrect size.");
65 
66 struct AssetHeader {
67     u32_le magic;
68     u32_le format_version;
69     AssetSection icon;
70     AssetSection nacp;
71     AssetSection romfs;
72 };
73 static_assert(sizeof(AssetHeader) == 0x38, "AssetHeader has incorrect size.");
74 
AppLoader_NRO(FileSys::VirtualFile file)75 AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(file) {
76     NroHeader nro_header{};
77     if (file->ReadObject(&nro_header) != sizeof(NroHeader)) {
78         return;
79     }
80 
81     if (file->GetSize() >= nro_header.file_size + sizeof(AssetHeader)) {
82         const u64 offset = nro_header.file_size;
83         AssetHeader asset_header{};
84         if (file->ReadObject(&asset_header, offset) != sizeof(AssetHeader)) {
85             return;
86         }
87 
88         if (asset_header.format_version != 0) {
89             LOG_WARNING(Loader,
90                         "NRO Asset Header has format {}, currently supported format is 0. If "
91                         "strange glitches occur with metadata, check NRO assets.",
92                         asset_header.format_version);
93         }
94 
95         if (asset_header.magic != Common::MakeMagic('A', 'S', 'E', 'T')) {
96             return;
97         }
98 
99         if (asset_header.nacp.size > 0) {
100             nacp = std::make_unique<FileSys::NACP>(std::make_shared<FileSys::OffsetVfsFile>(
101                 file, asset_header.nacp.size, offset + asset_header.nacp.offset, "Control.nacp"));
102         }
103 
104         if (asset_header.romfs.size > 0) {
105             romfs = std::make_shared<FileSys::OffsetVfsFile>(
106                 file, asset_header.romfs.size, offset + asset_header.romfs.offset, "game.romfs");
107         }
108 
109         if (asset_header.icon.size > 0) {
110             icon_data = file->ReadBytes(asset_header.icon.size, offset + asset_header.icon.offset);
111         }
112     }
113 }
114 
115 AppLoader_NRO::~AppLoader_NRO() = default;
116 
IdentifyType(const FileSys::VirtualFile & file)117 FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) {
118     // Read NSO header
119     NroHeader nro_header{};
120     if (sizeof(NroHeader) != file->ReadObject(&nro_header)) {
121         return FileType::Error;
122     }
123     if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) {
124         return FileType::NRO;
125     }
126     return FileType::Error;
127 }
128 
PageAlignSize(u32 size)129 static constexpr u32 PageAlignSize(u32 size) {
130     return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
131 }
132 
LoadNroImpl(Kernel::Process & process,const std::vector<u8> & data,const std::string & name)133 static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
134                         const std::string& name) {
135     if (data.size() < sizeof(NroHeader)) {
136         return {};
137     }
138 
139     // Read NSO header
140     NroHeader nro_header{};
141     std::memcpy(&nro_header, data.data(), sizeof(NroHeader));
142     if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) {
143         return {};
144     }
145 
146     // Build program image
147     Kernel::PhysicalMemory program_image(PageAlignSize(nro_header.file_size));
148     std::memcpy(program_image.data(), data.data(), program_image.size());
149     if (program_image.size() != PageAlignSize(nro_header.file_size)) {
150         return {};
151     }
152 
153     Kernel::CodeSet codeset;
154     for (std::size_t i = 0; i < nro_header.segments.size(); ++i) {
155         codeset.segments[i].addr = nro_header.segments[i].offset;
156         codeset.segments[i].offset = nro_header.segments[i].offset;
157         codeset.segments[i].size = PageAlignSize(nro_header.segments[i].size);
158     }
159 
160     if (!Settings::values.program_args.empty()) {
161         const auto arg_data = Settings::values.program_args;
162         codeset.DataSegment().size += NSO_ARGUMENT_DATA_ALLOCATION_SIZE;
163         NSOArgumentHeader args_header{
164             NSO_ARGUMENT_DATA_ALLOCATION_SIZE, static_cast<u32_le>(arg_data.size()), {}};
165         const auto end_offset = program_image.size();
166         program_image.resize(static_cast<u32>(program_image.size()) +
167                              NSO_ARGUMENT_DATA_ALLOCATION_SIZE);
168         std::memcpy(program_image.data() + end_offset, &args_header, sizeof(NSOArgumentHeader));
169         std::memcpy(program_image.data() + end_offset + sizeof(NSOArgumentHeader), arg_data.data(),
170                     arg_data.size());
171     }
172 
173     // Default .bss to NRO header bss size if MOD0 section doesn't exist
174     u32 bss_size{PageAlignSize(nro_header.bss_size)};
175 
176     // Read MOD header
177     ModHeader mod_header{};
178     std::memcpy(&mod_header, program_image.data() + nro_header.module_header_offset,
179                 sizeof(ModHeader));
180 
181     const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')};
182     if (has_mod_header) {
183         // Resize program image to include .bss section and page align each section
184         bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset);
185     }
186 
187     codeset.DataSegment().size += bss_size;
188     program_image.resize(static_cast<u32>(program_image.size()) + bss_size);
189 
190     // Setup the process code layout
191     if (process.LoadFromMetadata(FileSys::ProgramMetadata::GetDefault(), program_image.size())
192             .IsError()) {
193         return false;
194     }
195 
196     // Load codeset for current process
197     codeset.memory = std::move(program_image);
198     process.LoadModule(std::move(codeset), process.PageTable().GetCodeRegionStart());
199 
200     return true;
201 }
202 
LoadNro(Kernel::Process & process,const FileSys::VfsFile & file)203 bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& file) {
204     return LoadNroImpl(process, file.ReadAllBytes(), file.GetName());
205 }
206 
Load(Kernel::Process & process,Core::System & system)207 AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process, Core::System& system) {
208     if (is_loaded) {
209         return {ResultStatus::ErrorAlreadyLoaded, {}};
210     }
211 
212     if (!LoadNro(process, *file)) {
213         return {ResultStatus::ErrorLoadingNRO, {}};
214     }
215 
216     if (romfs != nullptr) {
217         system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
218             *this, system.GetContentProvider(), system.GetFileSystemController()));
219     }
220 
221     is_loaded = true;
222     return {ResultStatus::Success,
223             LoadParameters{Kernel::THREADPRIO_DEFAULT, Core::Memory::DEFAULT_STACK_SIZE}};
224 }
225 
ReadIcon(std::vector<u8> & buffer)226 ResultStatus AppLoader_NRO::ReadIcon(std::vector<u8>& buffer) {
227     if (icon_data.empty()) {
228         return ResultStatus::ErrorNoIcon;
229     }
230 
231     buffer = icon_data;
232     return ResultStatus::Success;
233 }
234 
ReadProgramId(u64 & out_program_id)235 ResultStatus AppLoader_NRO::ReadProgramId(u64& out_program_id) {
236     if (nacp == nullptr) {
237         return ResultStatus::ErrorNoControl;
238     }
239 
240     out_program_id = nacp->GetTitleId();
241     return ResultStatus::Success;
242 }
243 
ReadRomFS(FileSys::VirtualFile & dir)244 ResultStatus AppLoader_NRO::ReadRomFS(FileSys::VirtualFile& dir) {
245     if (romfs == nullptr) {
246         return ResultStatus::ErrorNoRomFS;
247     }
248 
249     dir = romfs;
250     return ResultStatus::Success;
251 }
252 
ReadTitle(std::string & title)253 ResultStatus AppLoader_NRO::ReadTitle(std::string& title) {
254     if (nacp == nullptr) {
255         return ResultStatus::ErrorNoControl;
256     }
257 
258     title = nacp->GetApplicationName();
259     return ResultStatus::Success;
260 }
261 
ReadControlData(FileSys::NACP & control)262 ResultStatus AppLoader_NRO::ReadControlData(FileSys::NACP& control) {
263     if (nacp == nullptr) {
264         return ResultStatus::ErrorNoControl;
265     }
266 
267     control = *nacp;
268     return ResultStatus::Success;
269 }
270 
IsRomFSUpdatable() const271 bool AppLoader_NRO::IsRomFSUpdatable() const {
272     return false;
273 }
274 
275 } // namespace Loader
276