mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-18 01:46:47 +00:00
Loader: Add Nso Header loading, loadset validation in CreateProcess
This commit is contained in:
parent
16439fd336
commit
73b6225d2e
3 changed files with 176 additions and 2 deletions
87
stratosphere/loader/source/ldr_nso.cpp
Normal file
87
stratosphere/loader/source/ldr_nso.cpp
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
#include <switch.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cstdio>
|
||||||
|
#include "ldr_nso.hpp"
|
||||||
|
|
||||||
|
static NsoUtils::NsoHeader g_nso_headers[NSO_NUM_MAX] = {0};
|
||||||
|
static bool g_nso_present[NSO_NUM_MAX] = {0};
|
||||||
|
|
||||||
|
void NsoUtils::GetNsoCodePath(char *content_path, unsigned int index) {
|
||||||
|
std::fill(content_path, content_path + FS_MAX_PATH, 0);
|
||||||
|
snprintf(content_path, FS_MAX_PATH, "code:/%s", NsoUtils::GetNsoFileName(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
void NsoUtils::GetNsoSdPath(char *content_path, u64 title_id, unsigned int index) {
|
||||||
|
std::fill(content_path, content_path + FS_MAX_PATH, 0);
|
||||||
|
snprintf(content_path, FS_MAX_PATH, "sdmc:/atmosphere/titles/%016lx/exefs/%s", title_id, NsoUtils::GetNsoFileName(index));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NsoUtils::IsNsoPresent(unsigned int index) {
|
||||||
|
return g_nso_present[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NsoUtils::LoadNsoHeaders(u64 title_id) {
|
||||||
|
char nso_path[FS_MAX_PATH] = {0};
|
||||||
|
FILE *f_nso;
|
||||||
|
|
||||||
|
/* Zero out the cache. */
|
||||||
|
std::fill(g_nso_present, g_nso_present + NSO_NUM_MAX, false);
|
||||||
|
std::fill(g_nso_headers, g_nso_headers + NSO_NUM_MAX, (const NsoUtils::NsoHeader &){0});
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < NSO_NUM_MAX; i++) {
|
||||||
|
GetNsoSdPath(nso_path, title_id, i);
|
||||||
|
if ((f_nso = fopen(nso_path, "rb")) != NULL) {
|
||||||
|
if (fread(&g_nso_headers[i], sizeof(NsoUtils::NsoHeader), 1, f_nso) != sizeof(NsoUtils::NsoHeader)) {
|
||||||
|
return 0xA09;
|
||||||
|
}
|
||||||
|
g_nso_present[i] = true;
|
||||||
|
fclose(f_nso);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
GetNsoCodePath(nso_path, i);
|
||||||
|
if ((f_nso = fopen(nso_path, "rb")) != NULL) {
|
||||||
|
if (fread(&g_nso_headers[i], sizeof(NsoUtils::NsoHeader), 1, f_nso) != sizeof(NsoUtils::NsoHeader)) {
|
||||||
|
return 0xA09;
|
||||||
|
}
|
||||||
|
g_nso_present[i] = true;
|
||||||
|
fclose(f_nso);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (1 < i && i < 12) {
|
||||||
|
/* If we failed to open a subsdk, there are no more subsdks. */
|
||||||
|
i = 12;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result NsoUtils::ValidateNsoLoadSet() {
|
||||||
|
/* We *must* have a "main" NSO. */
|
||||||
|
if (!g_nso_present[1]) {
|
||||||
|
return 0xA09;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Behavior switches depending on whether we have an rtld. */
|
||||||
|
if (g_nso_present[0]) {
|
||||||
|
/* If we have an rtld, dst offset for .text must be 0 for all other NSOs. */
|
||||||
|
for (unsigned int i = 0; i < NSO_NUM_MAX; i++) {
|
||||||
|
if (g_nso_present[i] && g_nso_headers[i].segments[0].dst_offset != 0) {
|
||||||
|
return 0xA09;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* If we don't have an rtld, we must ONLY have a main. */
|
||||||
|
for (unsigned int i = 2; i < NSO_NUM_MAX; i++) {
|
||||||
|
if (g_nso_present[i]) {
|
||||||
|
return 0xA09;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* That main's .text must be at dst_offset 0. */
|
||||||
|
if (g_nso_headers[1].segments[0].dst_offset != 0) {
|
||||||
|
return 0xA09;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0x0;
|
||||||
|
}
|
73
stratosphere/loader/source/ldr_nso.hpp
Normal file
73
stratosphere/loader/source/ldr_nso.hpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
|
||||||
|
#define MAGIC_NSO0 0x304F534E
|
||||||
|
#define NSO_NUM_MAX 13
|
||||||
|
|
||||||
|
class NsoUtils {
|
||||||
|
public:
|
||||||
|
struct NsoSegment {
|
||||||
|
u32 file_offset;
|
||||||
|
u32 dst_offset;
|
||||||
|
u32 decomp_size;
|
||||||
|
u32 align_or_total_size;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NsoHeader {
|
||||||
|
u32 magic;
|
||||||
|
u32 _0x4;
|
||||||
|
u32 _0x8;
|
||||||
|
u32 flags;
|
||||||
|
NsoSegment segments[3];
|
||||||
|
u8 build_id[0x20];
|
||||||
|
u32 compressed_sizes[3];
|
||||||
|
u8 _0x6C[0x24];
|
||||||
|
u64 dynstr_extents;
|
||||||
|
u64 dynsym_extents;
|
||||||
|
u8 section_hashes[3][0x20];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static_assert(sizeof(NsoHeader) == 0x100, "Incorrectly defined NsoHeader!");
|
||||||
|
|
||||||
|
static const char *GetNsoFileName(unsigned int index) {
|
||||||
|
switch (index) {
|
||||||
|
case 0:
|
||||||
|
return "rtld";
|
||||||
|
case 1:
|
||||||
|
return "main";
|
||||||
|
case 2:
|
||||||
|
return "subsdk0";
|
||||||
|
case 3:
|
||||||
|
return "subsdk1";
|
||||||
|
case 4:
|
||||||
|
return "subsdk2";
|
||||||
|
case 5:
|
||||||
|
return "subsdk3";
|
||||||
|
case 6:
|
||||||
|
return "subsdk4";
|
||||||
|
case 7:
|
||||||
|
return "subsdk5";
|
||||||
|
case 8:
|
||||||
|
return "subsdk6";
|
||||||
|
case 9:
|
||||||
|
return "subsdk7";
|
||||||
|
case 10:
|
||||||
|
return "subsdk8";
|
||||||
|
case 11:
|
||||||
|
return "subsdk9";
|
||||||
|
case 12:
|
||||||
|
return "sdk";
|
||||||
|
default:
|
||||||
|
/* TODO: Panic. */
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GetNsoCodePath(char *content_path, unsigned int index);
|
||||||
|
static void GetNsoSdPath(char *content_path, u64 title_id, unsigned int index);
|
||||||
|
|
||||||
|
static bool IsNsoPresent(unsigned int index);
|
||||||
|
static Result LoadNsoHeaders(u64 title_id);
|
||||||
|
static Result ValidateNsoLoadSet();
|
||||||
|
};
|
|
@ -5,40 +5,54 @@
|
||||||
#include "ldr_launch_queue.hpp"
|
#include "ldr_launch_queue.hpp"
|
||||||
#include "ldr_content_management.hpp"
|
#include "ldr_content_management.hpp"
|
||||||
#include "ldr_npdm.hpp"
|
#include "ldr_npdm.hpp"
|
||||||
|
#include "ldr_nso.hpp"
|
||||||
|
|
||||||
Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nca_path, LaunchQueue::LaunchItem *launch_item, u64 flags, Handle reslimit_h) {
|
Result ProcessCreation::CreateProcess(Handle *out_process_h, u64 index, char *nca_path, LaunchQueue::LaunchItem *launch_item, u64 flags, Handle reslimit_h) {
|
||||||
NpdmUtils::NpdmInfo info;
|
NpdmUtils::NpdmInfo info;
|
||||||
Registration::Process *target_process;
|
Registration::Process *target_process;
|
||||||
Result rc;
|
Result rc;
|
||||||
|
|
||||||
|
/* Get the process from the registration queue. */
|
||||||
target_process = Registration::get_process(index);
|
target_process = Registration::get_process(index);
|
||||||
if (target_process == NULL) {
|
if (target_process == NULL) {
|
||||||
return 0x1009;
|
return 0x1009;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Mount the title's exefs. */
|
||||||
rc = ContentManagement::MountCodeForTidSid(&target_process->tid_sid);
|
rc = ContentManagement::MountCodeForTidSid(&target_process->tid_sid);
|
||||||
if (R_FAILED(rc)) {
|
if (R_FAILED(rc)) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Load the process's NPDM. */
|
||||||
rc = NpdmUtils::LoadNpdmFromCache(target_process->tid_sid.title_id, &info);
|
rc = NpdmUtils::LoadNpdmFromCache(target_process->tid_sid.title_id, &info);
|
||||||
if (R_FAILED(rc)) {
|
if (R_FAILED(rc)) {
|
||||||
goto CREATE_PROCESS_END;
|
goto CREATE_PROCESS_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Validate the title we're loading is what we expect. */
|
||||||
if (info.aci0->title_id < info.acid->title_id_range_min || info.aci0->title_id > info.acid->title_id_range_max) {
|
if (info.aci0->title_id < info.acid->title_id_range_min || info.aci0->title_id > info.acid->title_id_range_max) {
|
||||||
rc = 0x1209;
|
rc = 0x1209;
|
||||||
goto CREATE_PROCESS_END;
|
goto CREATE_PROCESS_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Validate that the ACI0 Kernel Capabilities are valid and restricted by the ACID Kernel Capabilities. */
|
||||||
rc = NpdmUtils::ValidateCapabilities((u32 *)info.acid_kac, info.acid->kac_size/sizeof(u32), (u32 *)info.aci0_kac, info.aci0->kac_size/sizeof(u32));
|
rc = NpdmUtils::ValidateCapabilities((u32 *)info.acid_kac, info.acid->kac_size/sizeof(u32), (u32 *)info.aci0_kac, info.aci0->kac_size/sizeof(u32));
|
||||||
if (R_FAILED(rc)) {
|
if (R_FAILED(rc)) {
|
||||||
goto CREATE_PROCESS_END;
|
goto CREATE_PROCESS_END;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: Read in all NSO headers, see what NSOs are present. */
|
/* Read in all NSO headers, see what NSOs are present. */
|
||||||
|
rc = NsoUtils::LoadNsoHeaders(info.aci0->title_id);
|
||||||
|
if (R_FAILED(rc)) {
|
||||||
|
goto CREATE_PROCESS_END;
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: Validate that the set of NSOs to be loaded is correct. */
|
/* Validate that the set of NSOs to be loaded is correct. */
|
||||||
|
rc = NsoUtils::ValidateNsoLoadSet();
|
||||||
|
if (R_FAILED(rc)) {
|
||||||
|
goto CREATE_PROCESS_END;
|
||||||
|
}
|
||||||
|
|
||||||
/* TODO: Create the CreateProcessInfo. */
|
/* TODO: Create the CreateProcessInfo. */
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue