Loader: Add Nso Header loading, loadset validation in CreateProcess

This commit is contained in:
Michael Scire 2018-04-23 20:05:22 -06:00
parent 16439fd336
commit 73b6225d2e
3 changed files with 176 additions and 2 deletions

View 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;
}

View 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();
};

View File

@ -5,40 +5,54 @@
#include "ldr_launch_queue.hpp"
#include "ldr_content_management.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) {
NpdmUtils::NpdmInfo info;
Registration::Process *target_process;
Result rc;
/* Get the process from the registration queue. */
target_process = Registration::get_process(index);
if (target_process == NULL) {
return 0x1009;
}
/* Mount the title's exefs. */
rc = ContentManagement::MountCodeForTidSid(&target_process->tid_sid);
if (R_FAILED(rc)) {
return rc;
}
/* Load the process's NPDM. */
rc = NpdmUtils::LoadNpdmFromCache(target_process->tid_sid.title_id, &info);
if (R_FAILED(rc)) {
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) {
rc = 0x1209;
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));
if (R_FAILED(rc)) {
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. */