Update ldr files for ams 1.10
This commit is contained in:
@@ -1,299 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
#include "ldr_capabilities.hpp"
|
||||
#include "ldr_content_management.hpp"
|
||||
#include "ldr_development_manager.hpp"
|
||||
#include "ldr_meta.hpp"
|
||||
|
||||
namespace ams::ldr {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Convenience definitions. */
|
||||
constexpr size_t MetaCacheBufferSize = 0x8000;
|
||||
constexpr inline const char AtmosphereMetaPath[] = ENCODE_ATMOSPHERE_CODE_PATH("/main.npdm");
|
||||
constexpr inline const char SdOrBaseMetaPath[] = ENCODE_SD_OR_CODE_PATH("/main.npdm");
|
||||
constexpr inline const char BaseMetaPath[] = ENCODE_CODE_PATH("/main.npdm");
|
||||
|
||||
/* Types. */
|
||||
struct MetaCache {
|
||||
Meta meta;
|
||||
u8 buffer[MetaCacheBufferSize];
|
||||
};
|
||||
|
||||
/* Global storage. */
|
||||
ncm::ProgramId g_cached_program_id;
|
||||
cfg::OverrideStatus g_cached_override_status;
|
||||
MetaCache g_meta_cache;
|
||||
MetaCache g_original_meta_cache;
|
||||
|
||||
/* Helpers. */
|
||||
Result ValidateSubregion(size_t allowed_start, size_t allowed_end, size_t start, size_t size, size_t min_size = 0) {
|
||||
R_UNLESS(size >= min_size, ldr::ResultInvalidMeta());
|
||||
R_UNLESS(allowed_start <= start, ldr::ResultInvalidMeta());
|
||||
R_UNLESS(start <= allowed_end, ldr::ResultInvalidMeta());
|
||||
R_UNLESS(start + size <= allowed_end, ldr::ResultInvalidMeta());
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ValidateNpdm(const Npdm *npdm, size_t size) {
|
||||
/* Validate magic. */
|
||||
R_UNLESS(npdm->magic == Npdm::Magic, ldr::ResultInvalidMeta());
|
||||
|
||||
/* Validate flags. */
|
||||
constexpr u32 InvalidMetaFlagMask = 0x80000000;
|
||||
R_UNLESS(!(npdm->flags & InvalidMetaFlagMask), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Validate Acid extents. */
|
||||
R_TRY(ValidateSubregion(sizeof(Npdm), size, npdm->acid_offset, npdm->acid_size, sizeof(Acid)));
|
||||
|
||||
/* Validate Aci extends. */
|
||||
R_TRY(ValidateSubregion(sizeof(Npdm), size, npdm->aci_offset, npdm->aci_size, sizeof(Aci)));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ValidateAcid(const Acid *acid, size_t size) {
|
||||
/* Validate magic. */
|
||||
R_UNLESS(acid->magic == Acid::Magic, ldr::ResultInvalidMeta());
|
||||
|
||||
/* Validate that the acid is for production if not development. */
|
||||
if (!IsDevelopmentForAcidProductionCheck()) {
|
||||
R_UNLESS((acid->flags & Acid::AcidFlag_Production) != 0, ldr::ResultInvalidMeta());
|
||||
}
|
||||
|
||||
/* Validate that the acid version is correct. */
|
||||
constexpr u8 SupportedSdkMajorVersion = ams::svc::ConvertToSdkMajorVersion(ams::svc::SupportedKernelMajorVersion);
|
||||
if (acid->unknown_209 < SupportedSdkMajorVersion) {
|
||||
R_UNLESS(acid->version == 0, ldr::ResultInvalidMeta());
|
||||
R_UNLESS(acid->unknown_209 == 0, ldr::ResultInvalidMeta());
|
||||
}
|
||||
|
||||
/* Validate Fac, Sac, Kac. */
|
||||
R_TRY(ValidateSubregion(sizeof(Acid), size, acid->fac_offset, acid->fac_size));
|
||||
R_TRY(ValidateSubregion(sizeof(Acid), size, acid->sac_offset, acid->sac_size));
|
||||
R_TRY(ValidateSubregion(sizeof(Acid), size, acid->kac_offset, acid->kac_size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result ValidateAci(const Aci *aci, size_t size) {
|
||||
/* Validate magic. */
|
||||
R_UNLESS(aci->magic == Aci::Magic, ldr::ResultInvalidMeta());
|
||||
|
||||
/* Validate Fah, Sac, Kac. */
|
||||
R_TRY(ValidateSubregion(sizeof(Aci), size, aci->fah_offset, aci->fah_size));
|
||||
R_TRY(ValidateSubregion(sizeof(Aci), size, aci->sac_offset, aci->sac_size));
|
||||
R_TRY(ValidateSubregion(sizeof(Aci), size, aci->kac_offset, aci->kac_size));
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
const u8 *GetAcidSignatureModulus(ncm::ContentMetaPlatform platform, u8 key_generation, bool unk_unused) {
|
||||
return fssystem::GetAcidSignatureKeyModulus(platform, !IsDevelopmentForAcidSignatureCheck(), key_generation, unk_unused);
|
||||
}
|
||||
|
||||
size_t GetAcidSignatureModulusSize(ncm::ContentMetaPlatform platform, bool unk_unused) {
|
||||
return fssystem::GetAcidSignatureKeyModulusSize(platform, unk_unused);
|
||||
}
|
||||
|
||||
Result ValidateAcidSignature(Meta *meta, ncm::ContentMetaPlatform platform, bool unk_unused) {
|
||||
R_SUCCEED();
|
||||
/* Loader did not check signatures prior to 10.0.0. */
|
||||
if (hos::GetVersion() < hos::Version_10_0_0) {
|
||||
meta->check_verification_data = false;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
/* Get the signature key generation. */
|
||||
const auto signature_key_generation = meta->npdm->signature_key_generation;
|
||||
R_UNLESS(fssystem::IsValidSignatureKeyGeneration(platform, signature_key_generation), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Verify the signature. */
|
||||
const u8 *sig = meta->acid->signature;
|
||||
const size_t sig_size = sizeof(meta->acid->signature);
|
||||
const u8 *mod = GetAcidSignatureModulus(platform, signature_key_generation, unk_unused);
|
||||
const size_t mod_size = GetAcidSignatureModulusSize(platform, unk_unused);
|
||||
const u8 *exp = fssystem::GetAcidSignatureKeyPublicExponent();
|
||||
const size_t exp_size = fssystem::AcidSignatureKeyPublicExponentSize;
|
||||
const u8 *msg = meta->acid->modulus;
|
||||
const size_t msg_size = meta->acid->size;
|
||||
const bool is_signature_valid = crypto::VerifyRsa2048PssSha256(sig, sig_size, mod, mod_size, exp, exp_size, msg, msg_size);
|
||||
R_UNLESS(is_signature_valid || !IsEnabledProgramVerification(), ldr::ResultInvalidAcidSignature());
|
||||
|
||||
meta->check_verification_data = is_signature_valid;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadMetaFromFile(fs::FileHandle file, MetaCache *cache) {
|
||||
/* Reset cache. */
|
||||
cache->meta = {};
|
||||
|
||||
/* Read from file. */
|
||||
s64 npdm_size = 0;
|
||||
{
|
||||
/* Get file size. */
|
||||
R_TRY(fs::GetFileSize(std::addressof(npdm_size), file));
|
||||
|
||||
/* Read data into cache buffer. */
|
||||
R_UNLESS(npdm_size <= static_cast<s64>(MetaCacheBufferSize), ldr::ResultMetaOverflow());
|
||||
R_TRY(fs::ReadFile(file, 0, cache->buffer, npdm_size));
|
||||
}
|
||||
|
||||
/* Ensure size is big enough. */
|
||||
R_UNLESS(npdm_size >= static_cast<s64>(sizeof(Npdm)), ldr::ResultInvalidMeta());
|
||||
|
||||
/* Validate the meta. */
|
||||
{
|
||||
Meta *meta = std::addressof(cache->meta);
|
||||
|
||||
Npdm *npdm = reinterpret_cast<Npdm *>(cache->buffer);
|
||||
R_TRY(ValidateNpdm(npdm, npdm_size));
|
||||
|
||||
Acid *acid = reinterpret_cast<Acid *>(cache->buffer + npdm->acid_offset);
|
||||
Aci *aci = reinterpret_cast<Aci *>(cache->buffer + npdm->aci_offset);
|
||||
R_TRY(ValidateAcid(acid, npdm->acid_size));
|
||||
R_TRY(ValidateAci(aci, npdm->aci_size));
|
||||
|
||||
/* Set Meta members. */
|
||||
meta->npdm = npdm;
|
||||
meta->acid = acid;
|
||||
meta->aci = aci;
|
||||
|
||||
meta->acid_fac = reinterpret_cast<u8 *>(acid) + acid->fac_offset;
|
||||
meta->acid_sac = reinterpret_cast<u8 *>(acid) + acid->sac_offset;
|
||||
meta->acid_kac = reinterpret_cast<u8 *>(acid) + acid->kac_offset;
|
||||
|
||||
meta->aci_fah = reinterpret_cast<u8 *>(aci) + aci->fah_offset;
|
||||
meta->aci_sac = reinterpret_cast<u8 *>(aci) + aci->sac_offset;
|
||||
meta->aci_kac = reinterpret_cast<u8 *>(aci) + aci->kac_offset;
|
||||
|
||||
meta->modulus = acid->modulus;
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* API. */
|
||||
Result LoadMeta(Meta *out_meta, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status, ncm::ContentMetaPlatform platform, bool unk_unused) {
|
||||
/* Set the cached program id back to zero. */
|
||||
g_cached_program_id = {};
|
||||
|
||||
/* Try to load meta from file. */
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), AtmosphereMetaPath, fs::OpenMode_Read));
|
||||
{
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
R_TRY(LoadMetaFromFile(file, std::addressof(g_meta_cache)));
|
||||
}
|
||||
|
||||
/* Patch meta. Start by setting all program ids to the current program id. */
|
||||
Meta *meta = std::addressof(g_meta_cache.meta);
|
||||
meta->acid->program_id_min = loc.program_id;
|
||||
meta->acid->program_id_max = loc.program_id;
|
||||
meta->aci->program_id = loc.program_id;
|
||||
|
||||
/* For HBL, we need to copy some information from the base meta. */
|
||||
if (status.IsHbl()) {
|
||||
if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), SdOrBaseMetaPath, fs::OpenMode_Read))) {
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
|
||||
if (R_SUCCEEDED(LoadMetaFromFile(file, std::addressof(g_original_meta_cache)))) {
|
||||
Meta *o_meta = std::addressof(g_original_meta_cache.meta);
|
||||
|
||||
/* Fix pool partition. */
|
||||
if (hos::GetVersion() >= hos::Version_5_0_0) {
|
||||
meta->acid->flags = (meta->acid->flags & 0xFFFFFFC3) | (o_meta->acid->flags & 0x0000003C);
|
||||
}
|
||||
|
||||
/* Fix flags. */
|
||||
const u16 program_info_flags = MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(o_meta->aci_kac), o_meta->aci->kac_size / sizeof(util::BitPack32));
|
||||
UpdateProgramInfoFlag(program_info_flags, static_cast<util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32));
|
||||
UpdateProgramInfoFlag(program_info_flags, static_cast<util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
|
||||
}
|
||||
}
|
||||
|
||||
/* Perform address space override. */
|
||||
if (status.HasOverrideAddressSpace()) {
|
||||
/* Clear the existing address space. */
|
||||
meta->npdm->flags &= ~Npdm::MetaFlag_AddressSpaceTypeMask;
|
||||
|
||||
/* Set the new address space flag. */
|
||||
switch (status.GetOverrideAddressSpaceFlags()) {
|
||||
case cfg::impl::OverrideStatusFlag_AddressSpace32Bit: meta->npdm->flags |= (Npdm::AddressSpaceType_32Bit) << Npdm::MetaFlag_AddressSpaceTypeShift; break;
|
||||
case cfg::impl::OverrideStatusFlag_AddressSpace64BitDeprecated: meta->npdm->flags |= (Npdm::AddressSpaceType_64BitDeprecated) << Npdm::MetaFlag_AddressSpaceTypeShift; break;
|
||||
case cfg::impl::OverrideStatusFlag_AddressSpace32BitWithoutAlias: meta->npdm->flags |= (Npdm::AddressSpaceType_32BitWithoutAlias) << Npdm::MetaFlag_AddressSpaceTypeShift; break;
|
||||
case cfg::impl::OverrideStatusFlag_AddressSpace64Bit: meta->npdm->flags |= (Npdm::AddressSpaceType_64Bit) << Npdm::MetaFlag_AddressSpaceTypeShift; break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
/* When hbl is applet, adjust main thread priority. */
|
||||
if ((MakeProgramInfoFlag(static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)) & ProgramInfoFlag_ApplicationTypeMask) == ProgramInfoFlag_Applet) {
|
||||
constexpr auto HblMainThreadPriorityApplication = 44;
|
||||
constexpr auto HblMainThreadPriorityApplet = 40;
|
||||
if (meta->npdm->main_thread_priority == HblMainThreadPriorityApplication) {
|
||||
meta->npdm->main_thread_priority = HblMainThreadPriorityApplet;
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix the debug capabilities, to prevent needing a hbl recompilation. */
|
||||
FixDebugCapabilityForHbl(static_cast<util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32));
|
||||
FixDebugCapabilityForHbl(static_cast<util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
|
||||
} else if (hos::GetVersion() >= hos::Version_10_0_0) {
|
||||
/* If storage id is none, there is no base code filesystem, and thus it is impossible for us to validate. */
|
||||
/* However, if we're an application, we are guaranteed a base code filesystem. */
|
||||
if (static_cast<ncm::StorageId>(loc.storage_id) != ncm::StorageId::None || ncm::IsApplicationId(loc.program_id)) {
|
||||
R_TRY(fs::OpenFile(std::addressof(file), BaseMetaPath, fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
R_TRY(LoadMetaFromFile(file, std::addressof(g_original_meta_cache)));
|
||||
R_TRY(ValidateAcidSignature(std::addressof(g_original_meta_cache.meta), platform, unk_unused));
|
||||
meta->modulus = g_original_meta_cache.meta.modulus;
|
||||
meta->check_verification_data = g_original_meta_cache.meta.check_verification_data;
|
||||
}
|
||||
}
|
||||
|
||||
/* Pre-process the capabilities. */
|
||||
/* This is used to e.g. avoid passing memory region descriptor to older kernels. */
|
||||
PreProcessCapability(static_cast<util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32));
|
||||
PreProcessCapability(static_cast<util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32));
|
||||
|
||||
/* Set output. */
|
||||
g_cached_program_id = loc.program_id;
|
||||
g_cached_override_status = status;
|
||||
*out_meta = *meta;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadMetaFromCache(Meta *out_meta, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &status, ncm::ContentMetaPlatform platform) {
|
||||
if (g_cached_program_id != loc.program_id || g_cached_override_status != status) {
|
||||
R_RETURN(LoadMeta(out_meta, loc, status, platform, false));
|
||||
}
|
||||
*out_meta = g_meta_cache.meta;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void InvalidateMetaCache() {
|
||||
/* Set the cached program id back to zero. */
|
||||
g_cached_program_id = {};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,7 +22,6 @@
|
||||
#include "ldr_patcher.hpp"
|
||||
#include "ldr_process_creation.hpp"
|
||||
#include "ldr_ro_manager.hpp"
|
||||
#include "ldr_ro_manager.hpp"
|
||||
#include "oc/oc_loader.hpp"
|
||||
|
||||
namespace ams::ldr {
|
||||
@@ -31,21 +30,22 @@ namespace ams::ldr {
|
||||
|
||||
/* Convenience defines. */
|
||||
constexpr size_t SystemResourceSizeMax = 0x1FE00000;
|
||||
constexpr size_t AutoLoadModuleSizeMax = 0x800000000;
|
||||
|
||||
/* Types. */
|
||||
enum NsoIndex {
|
||||
Nso_Rtld = 0,
|
||||
Nso_Main = 1,
|
||||
Nso_Compat0 = 2,
|
||||
Nso_Compat1 = 3,
|
||||
Nso_Compat2 = 4,
|
||||
Nso_Compat3 = 5,
|
||||
Nso_Compat4 = 6,
|
||||
Nso_Compat5 = 7,
|
||||
Nso_Compat6 = 8,
|
||||
Nso_Compat7 = 9,
|
||||
Nso_Compat8 = 10,
|
||||
Nso_Compat9 = 11,
|
||||
Nso_Wkc0 = 2,
|
||||
Nso_Wkc1 = 3,
|
||||
Nso_Wkc2 = 4,
|
||||
Nso_Wkc3 = 5,
|
||||
Nso_Wkc4 = 6,
|
||||
Nso_Wkc5 = 7,
|
||||
Nso_Wkc6 = 8,
|
||||
Nso_Wkc7 = 9,
|
||||
Nso_Wkc8 = 10,
|
||||
Nso_Wkc9 = 11,
|
||||
Nso_SubSdk0 = 12,
|
||||
Nso_SubSdk1 = 13,
|
||||
Nso_SubSdk2 = 14,
|
||||
@@ -63,16 +63,16 @@ namespace ams::ldr {
|
||||
constexpr inline const char *NsoPaths[Nso_Count] = {
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/rtld"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/main"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat0"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat1"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat2"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat3"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat4"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat5"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat6"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat7"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat8"),
|
||||
ENCODE_ATMOSPHERE_CMPT_PATH("/compat9"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc0"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc1"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc2"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc3"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc4"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc5"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc6"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc7"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc8"),
|
||||
ENCODE_ATMOSPHERE_BDLL_PATH("/wkc9"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk0"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk1"),
|
||||
ENCODE_ATMOSPHERE_CODE_PATH("/subsdk2"),
|
||||
@@ -99,8 +99,15 @@ namespace ams::ldr {
|
||||
size_t nso_size[Nso_Count];
|
||||
};
|
||||
|
||||
struct AutoLoadModuleInfo {
|
||||
bool has_rtld;
|
||||
bool has_main;
|
||||
bool has_sdk;
|
||||
bool has_subsdk;
|
||||
bool has_nso[Nso_Count];
|
||||
};
|
||||
|
||||
/* Global NSO header cache. */
|
||||
bool g_has_nso[Nso_Count];
|
||||
NsoHeader g_nso_headers[Nso_Count];
|
||||
|
||||
/* Pcv/Ptm check cache */
|
||||
@@ -167,12 +174,30 @@ namespace ams::ldr {
|
||||
return static_cast<Acid::PoolPartition>((meta->acid->flags & Acid::AcidFlag_PoolPartitionMask) >> Acid::AcidFlag_PoolPartitionShift);
|
||||
}
|
||||
|
||||
Result LoadAutoLoadHeaders(NsoHeader *nso_headers, bool *has_nso) {
|
||||
Result LoadAutoLoadHeaders(NsoHeader *nso_headers, AutoLoadModuleInfo *ali, u32 acid_flags) {
|
||||
/* Clear NSOs. */
|
||||
std::memset(nso_headers, 0, sizeof(*nso_headers) * Nso_Count);
|
||||
std::memset(has_nso, 0, sizeof(*has_nso) * Nso_Count);
|
||||
*ali = {};
|
||||
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
/* Only load browser DLLs if acid flags say to do so. */
|
||||
switch (i) {
|
||||
case Nso_Wkc0:
|
||||
case Nso_Wkc1:
|
||||
case Nso_Wkc2:
|
||||
case Nso_Wkc3:
|
||||
case Nso_Wkc4:
|
||||
case Nso_Wkc5:
|
||||
case Nso_Wkc6:
|
||||
case Nso_Wkc7:
|
||||
case Nso_Wkc8:
|
||||
case Nso_Wkc9:
|
||||
if ((acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) == 0) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
fs::FileHandle file;
|
||||
if (R_SUCCEEDED(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read))) {
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
@@ -182,27 +207,82 @@ namespace ams::ldr {
|
||||
R_TRY(fs::ReadFile(std::addressof(read_size), file, 0, nso_headers + i, sizeof(*nso_headers)));
|
||||
R_UNLESS(read_size == sizeof(*nso_headers), ldr::ResultInvalidNso());
|
||||
|
||||
has_nso[i] = true;
|
||||
/* Note nso is present. */
|
||||
switch (i) {
|
||||
case Nso_Rtld:
|
||||
ali->has_rtld = true;
|
||||
break;
|
||||
case Nso_Main:
|
||||
ali->has_main = true;
|
||||
break;
|
||||
case Nso_SubSdk0:
|
||||
case Nso_SubSdk1:
|
||||
case Nso_SubSdk2:
|
||||
case Nso_SubSdk3:
|
||||
case Nso_SubSdk4:
|
||||
case Nso_SubSdk5:
|
||||
case Nso_SubSdk6:
|
||||
case Nso_SubSdk7:
|
||||
case Nso_SubSdk8:
|
||||
case Nso_SubSdk9:
|
||||
ali->has_subsdk = true;
|
||||
break;
|
||||
case Nso_Sdk:
|
||||
ali->has_sdk = true;
|
||||
break;
|
||||
}
|
||||
ali->has_nso[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CheckAutoLoad(const NsoHeader *nso_headers, const bool *has_nso) {
|
||||
Result CheckAutoLoad(const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, u32 acid_flags) {
|
||||
/* We must always have a main. */
|
||||
R_UNLESS(has_nso[Nso_Main], ldr::ResultInvalidNso());
|
||||
R_UNLESS(ali->has_main, ldr::ResultInvalidNso());
|
||||
|
||||
/* If we don't have an RTLD, we must only have a main. */
|
||||
if (!has_nso[Nso_Rtld]) {
|
||||
for (size_t i = Nso_Main + 1; i < Nso_Count; i++) {
|
||||
R_UNLESS(!has_nso[i], ldr::ResultInvalidNso());
|
||||
}
|
||||
/* All NSOs must not be --X. */
|
||||
/* This is "probably" not checked on Ounce? */
|
||||
for (size_t i = 0; i < Nso_Count; ++i) {
|
||||
R_UNLESS((nso_headers[i].flags & NsoHeader::Flag_PreventCodeReads) == 0, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* All NSOs must have zero text offset. */
|
||||
/* If we don't have an RTLD, we must only have a main. */
|
||||
const bool has_browser_dll = (acid_flags & Acid::AcidFlag_LoadBrowserCoreDll) != 0;
|
||||
if (!ali->has_rtld) {
|
||||
/* If don't have rtld, we must also not have sdk. */
|
||||
R_UNLESS(!ali->has_sdk, ldr::ResultInvalidNso());
|
||||
|
||||
/* We must also not have both subsdk and browser dll. */
|
||||
R_UNLESS(!(ali->has_subsdk && has_browser_dll), ldr::ResultInvalidNso());
|
||||
} else {
|
||||
/* If we have rtld, we must not have browser core dll. */
|
||||
R_UNLESS(!has_browser_dll, ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
/* Check NSO extents. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
/* Only validate the nsos we have. */
|
||||
if (!ali->has_nso[i]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* NSOs must have page-aligned segments. */
|
||||
R_UNLESS(util::IsAligned(nso_headers[i].text_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(nso_headers[i].ro_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
R_UNLESS(util::IsAligned(nso_headers[i].rw_dst_offset, os::MemoryPageSize), ldr::ResultInvalidNso());
|
||||
|
||||
/* NSOs must have zero text offset. */
|
||||
R_UNLESS(nso_headers[i].text_dst_offset == 0, ldr::ResultInvalidNso());
|
||||
|
||||
/* NSO .text must precede .rodata. */
|
||||
const size_t text_end = static_cast<size_t>(nso_headers[i].text_dst_offset) + static_cast<size_t>(nso_headers[i].text_size);
|
||||
R_UNLESS(text_end <= static_cast<size_t>(nso_headers[i].ro_dst_offset), ldr::ResultInvalidNso());
|
||||
|
||||
/* NSO .rodata must precede .rwdata. */
|
||||
const size_t ro_end = static_cast<size_t>(nso_headers[i].ro_dst_offset) + static_cast<size_t>(nso_headers[i].ro_size);
|
||||
R_UNLESS(ro_end <= static_cast<size_t>(nso_headers[i].rw_dst_offset), ldr::ResultInvalidNso());
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
@@ -273,11 +353,9 @@ namespace ams::ldr {
|
||||
/* Validate the kernel capabilities. */
|
||||
R_TRY(TestCapability(static_cast<const util::BitPack32 *>(meta->acid_kac), meta->acid->kac_size / sizeof(util::BitPack32), static_cast<const util::BitPack32 *>(meta->aci_kac), meta->aci->kac_size / sizeof(util::BitPack32)));
|
||||
|
||||
/* Check if NCA is PCV or PTM */
|
||||
g_is_pcv = meta->aci->program_id == ncm::SystemProgramId::Pcv;
|
||||
g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
|
||||
|
||||
/* Check if NCA is PCV or PTM */
|
||||
g_is_pcv = meta->aci->program_id == ncm::SystemProgramId::Pcv;
|
||||
g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
|
||||
/* If we have data to validate, validate it. */
|
||||
if (meta->check_verification_data) {
|
||||
@@ -445,7 +523,7 @@ g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
return rand % (max + 1);
|
||||
}
|
||||
|
||||
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument) {
|
||||
Result DecideAddressSpaceLayout(ProcessInfo *out, svc::CreateProcessParameter *out_param, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument) {
|
||||
/* Clear output. */
|
||||
out->args_address = 0;
|
||||
out->args_size = 0;
|
||||
@@ -457,22 +535,32 @@ g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
|
||||
/* Calculate base offsets. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (has_nso[i]) {
|
||||
if (ali->has_nso[i]) {
|
||||
out->nso_address[i] = total_size;
|
||||
const size_t text_end = nso_headers[i].text_dst_offset + nso_headers[i].text_size;
|
||||
const size_t ro_end = nso_headers[i].ro_dst_offset + nso_headers[i].ro_size;
|
||||
const size_t rw_end = nso_headers[i].rw_dst_offset + nso_headers[i].rw_size + nso_headers[i].bss_size;
|
||||
const size_t text_end = static_cast<size_t>(nso_headers[i].text_dst_offset) + static_cast<size_t>(nso_headers[i].text_size);
|
||||
const size_t ro_end = static_cast<size_t>(nso_headers[i].ro_dst_offset) + static_cast<size_t>(nso_headers[i].ro_size);
|
||||
const size_t rw_end = static_cast<size_t>(nso_headers[i].rw_dst_offset) + static_cast<size_t>(nso_headers[i].rw_size);
|
||||
out->nso_size[i] = text_end;
|
||||
out->nso_size[i] = std::max(out->nso_size[i], ro_end);
|
||||
out->nso_size[i] = std::max(out->nso_size[i], rw_end);
|
||||
out->nso_size[i] = util::AlignUp(out->nso_size[i], os::MemoryPageSize);
|
||||
out->nso_size[i] += static_cast<size_t>(nso_headers[i].bss_size);
|
||||
|
||||
const size_t aligned_up_size = util::AlignUp(out->nso_size[i], os::MemoryPageSize) & (AutoLoadModuleSizeMax - 1);
|
||||
R_UNLESS(out->nso_size[i] <= aligned_up_size, ldr::ResultInvalidNso());
|
||||
R_UNLESS(aligned_up_size > 0, ldr::ResultInvalidNso());
|
||||
|
||||
out->nso_size[i] = aligned_up_size;
|
||||
|
||||
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->nso_size[i]), ldr::ResultInvalidNso());
|
||||
total_size += out->nso_size[i];
|
||||
|
||||
if (!argument_allocated && argument != nullptr) {
|
||||
out->args_address = total_size;
|
||||
out->args_size = util::AlignUp(2 * sizeof(u32) + argument->argument_size * 2 + ArgumentStore::ArgumentBufferSize, os::MemoryPageSize);
|
||||
|
||||
R_UNLESS(util::CanAddWithoutOverflow(total_size, out->args_size), ldr::ResultInvalidNso());
|
||||
total_size += out->args_size;
|
||||
|
||||
argument_allocated = true;
|
||||
}
|
||||
}
|
||||
@@ -520,11 +608,13 @@ g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
/* Set out. */
|
||||
aslr_start += aslr_slide;
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (has_nso[i]) {
|
||||
if (ali->has_nso[i]) {
|
||||
R_UNLESS(util::CanAddWithoutOverflow(out->nso_address[i], aslr_start), ldr::ResultInvalidNso());
|
||||
out->nso_address[i] += aslr_start;
|
||||
}
|
||||
}
|
||||
if (out->args_address) {
|
||||
R_UNLESS(util::CanAddWithoutOverflow(out->args_address, aslr_start), ldr::ResultInvalidNso());
|
||||
out->args_address += aslr_start;
|
||||
}
|
||||
|
||||
@@ -567,7 +657,7 @@ g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size, bool prevent_code_reads) {
|
||||
Result LoadAutoLoadModule(os::NativeHandle process_handle, fs::FileHandle file, const NsoHeader *nso_header, uintptr_t nso_address, size_t nso_size) {
|
||||
/* Map and read data from file. */
|
||||
{
|
||||
/* Map the process memory. */
|
||||
@@ -586,9 +676,9 @@ g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
(nso_header->flags & NsoHeader::Flag_CheckHashRw) != 0, map_address + nso_header->rw_dst_offset, map_address + nso_size));
|
||||
|
||||
/* Clear unused space to zero. */
|
||||
const size_t text_end = nso_header->text_dst_offset + nso_header->text_size;
|
||||
const size_t ro_end = nso_header->ro_dst_offset + nso_header->ro_size;
|
||||
const size_t rw_end = nso_header->rw_dst_offset + nso_header->rw_size;
|
||||
const size_t text_end = static_cast<size_t>(nso_header->text_dst_offset) + static_cast<size_t>(nso_header->text_size);
|
||||
const size_t ro_end = static_cast<size_t>(nso_header->ro_dst_offset) + static_cast<size_t>(nso_header->ro_size);
|
||||
const size_t rw_end = static_cast<size_t>(nso_header->rw_dst_offset) + static_cast<size_t>(nso_header->rw_size);
|
||||
std::memset(reinterpret_cast<void *>(map_address + 0), 0, nso_header->text_dst_offset);
|
||||
std::memset(reinterpret_cast<void *>(map_address + text_end), 0, nso_header->ro_dst_offset - text_end);
|
||||
std::memset(reinterpret_cast<void *>(map_address + ro_end), 0, nso_header->rw_dst_offset - ro_end);
|
||||
@@ -601,11 +691,13 @@ g_is_ptm = meta->aci->program_id == ncm::SystemProgramId::Ptm;
|
||||
LocateAndApplyIpsPatchesToModule(nso_header->module_id, map_address, nso_size);
|
||||
|
||||
/* Apply PCV and PTM patches */
|
||||
if (g_is_pcv)
|
||||
oc::pcv::Patch(map_address, nso_size);
|
||||
if (g_is_ptm)
|
||||
oc::ptm::Patch(map_address, nso_size);
|
||||
if (g_is_pcv) {
|
||||
oc::pcv::Patch(map_address, nso_size);
|
||||
}
|
||||
|
||||
if (g_is_ptm) {
|
||||
oc::ptm::Patch(map_address, nso_size);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set permissions. */
|
||||
@@ -613,6 +705,7 @@ if (g_is_ptm)
|
||||
const size_t ro_size = util::AlignUp(nso_header->ro_size, os::MemoryPageSize);
|
||||
const size_t rw_size = util::AlignUp(nso_header->rw_size + nso_header->bss_size, os::MemoryPageSize);
|
||||
if (text_size) {
|
||||
const bool prevent_code_reads = (nso_header->flags & NsoHeader::Flag_PreventCodeReads);
|
||||
R_TRY(os::SetProcessMemoryPermission(process_handle, nso_address + nso_header->text_dst_offset, text_size, prevent_code_reads ? os::MemoryPermission_ExecuteOnly : os::MemoryPermission_ReadExecute));
|
||||
}
|
||||
if (ro_size) {
|
||||
@@ -625,15 +718,15 @@ if (g_is_ptm)
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result LoadAutoLoadModules(const ProcessInfo *process_info, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, bool prevent_code_reads) {
|
||||
Result LoadAutoLoadModules(const ProcessInfo *process_info, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument) {
|
||||
/* Load each NSO. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (has_nso[i]) {
|
||||
if (ali->has_nso[i]) {
|
||||
fs::FileHandle file;
|
||||
R_TRY(fs::OpenFile(std::addressof(file), GetNsoPath(i), fs::OpenMode_Read));
|
||||
ON_SCOPE_EXIT { fs::CloseFile(file); };
|
||||
|
||||
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i], prevent_code_reads));
|
||||
R_TRY(LoadAutoLoadModule(process_info->process_handle, file, nso_headers + i, process_info->nso_address[i], process_info->nso_size[i]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -660,13 +753,13 @@ if (g_is_ptm)
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const bool *has_nso, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
||||
Result CreateProcessAndLoadAutoLoadModules(ProcessInfo *out, const Meta *meta, const NsoHeader *nso_headers, const AutoLoadModuleInfo *ali, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit) {
|
||||
/* Get CreateProcessParameter. */
|
||||
svc::CreateProcessParameter param;
|
||||
R_TRY(GetCreateProcessParameter(std::addressof(param), meta, flags, resource_limit));
|
||||
|
||||
/* Decide on an NSO layout. */
|
||||
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, has_nso, argument));
|
||||
R_TRY(DecideAddressSpaceLayout(out, std::addressof(param), nso_headers, ali, argument));
|
||||
|
||||
/* Actually create process. */
|
||||
svc::Handle process_handle;
|
||||
@@ -677,7 +770,7 @@ if (g_is_ptm)
|
||||
ON_RESULT_FAILURE { svc::CloseHandle(process_handle); };
|
||||
|
||||
/* Load all auto load modules. */
|
||||
R_RETURN(LoadAutoLoadModules(out, nso_headers, has_nso, argument, (meta->npdm->flags & ldr::Npdm::MetaFlag_PreventCodeReads) != 0));
|
||||
R_RETURN(LoadAutoLoadModules(out, nso_headers, ali, argument));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -686,7 +779,7 @@ if (g_is_ptm)
|
||||
Result CreateProcess(os::NativeHandle *out, PinId pin_id, const ncm::ProgramLocation &loc, const cfg::OverrideStatus &override_status, const char *path, const ArgumentStore::Entry *argument, u32 flags, os::NativeHandle resource_limit, const ldr::ProgramAttributes &attrs) {
|
||||
/* Mount code. */
|
||||
AMS_UNUSED(path);
|
||||
ScopedCodeMount mount(loc, override_status, attrs);
|
||||
ScopedCodeMountForCode mount(loc, override_status, attrs);
|
||||
R_TRY(mount.GetResult());
|
||||
|
||||
/* Load meta, possibly from cache. */
|
||||
@@ -696,13 +789,35 @@ if (g_is_ptm)
|
||||
/* Validate meta. */
|
||||
R_TRY(ValidateMeta(std::addressof(meta), loc, mount.GetCodeVerificationData()));
|
||||
|
||||
/* If we should, load/validate the browser core dll. */
|
||||
util::optional<ScopedCodeMountForBrowserCoreDll> bdll_mount;
|
||||
if ((meta.acid->flags & Acid::AcidFlag_LoadBrowserCoreDll)) {
|
||||
/* NOTE: I'm unsure whether we should be getting a fresh override status (allowing for different override between main and bdll?) */
|
||||
/* or whether we should be using the main override status. Going to go with main, for sanity's sake. */
|
||||
/* Also noting that Nintendo always passes ProgramAttributes=0 here, but this "should" be different on Ounce? */
|
||||
/* Kind of unclear how to handle this without knowing what exactly is being ifdef'd. */
|
||||
const ncm::ProgramLocation bdll_loc = ncm::ProgramLocation::Make(ncm::SystemProgramId::BrowserCoreDll, ncm::StorageId::BuiltInSystem);
|
||||
const cfg::OverrideStatus bdll_override_status = override_status;
|
||||
const ldr::ProgramAttributes bdll_attrs = attrs;
|
||||
bdll_mount.emplace(bdll_loc, bdll_override_status, bdll_attrs);
|
||||
R_TRY(bdll_mount->GetResult());
|
||||
|
||||
/* Load browser dll meta, possibly from cache. */
|
||||
Meta bdll_meta;
|
||||
R_TRY(LoadMetaFromCacheForBrowserCoreDll(std::addressof(bdll_meta), bdll_loc, bdll_override_status, bdll_attrs.platform));
|
||||
|
||||
/* Validate browser dll meta. */
|
||||
R_TRY(ValidateMeta(std::addressof(bdll_meta), loc, mount.GetCodeVerificationData()));
|
||||
}
|
||||
|
||||
/* Load, validate NSO headers. */
|
||||
R_TRY(LoadAutoLoadHeaders(g_nso_headers, g_has_nso));
|
||||
R_TRY(CheckAutoLoad(g_nso_headers, g_has_nso));
|
||||
AutoLoadModuleInfo auto_load_info = {};
|
||||
R_TRY(LoadAutoLoadHeaders(g_nso_headers, std::addressof(auto_load_info), meta.acid->flags));
|
||||
R_TRY(CheckAutoLoad(g_nso_headers, std::addressof(auto_load_info), meta.acid->flags));
|
||||
|
||||
/* Actually create the process and load NSOs into process memory. */
|
||||
ProcessInfo info;
|
||||
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), g_nso_headers, g_has_nso, argument, flags, resource_limit));
|
||||
R_TRY(CreateProcessAndLoadAutoLoadModules(std::addressof(info), std::addressof(meta), g_nso_headers, std::addressof(auto_load_info), argument, flags, resource_limit));
|
||||
|
||||
/* Register NSOs with the RoManager. */
|
||||
{
|
||||
@@ -715,7 +830,7 @@ if (g_is_ptm)
|
||||
|
||||
/* Register all NSOs. */
|
||||
for (size_t i = 0; i < Nso_Count; i++) {
|
||||
if (g_has_nso[i]) {
|
||||
if (auto_load_info.has_nso[i]) {
|
||||
RoManager::GetInstance().AddNso(pin_id, g_nso_headers[i].module_id, info.nso_address[i], info.nso_size[i]);
|
||||
}
|
||||
}
|
||||
@@ -746,7 +861,7 @@ if (g_is_ptm)
|
||||
{
|
||||
AMS_UNUSED(path);
|
||||
|
||||
ScopedCodeMount mount(loc, attrs);
|
||||
ScopedCodeMountForCode mount(loc, attrs);
|
||||
R_TRY(mount.GetResult());
|
||||
R_TRY(LoadMeta(std::addressof(meta), loc, mount.GetOverrideStatus(), attrs.platform, false));
|
||||
if (out_status != nullptr) {
|
||||
|
||||
Reference in New Issue
Block a user