mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
ro: implement loadnro/unloadnro
This commit is contained in:
parent
4ba6d8b24c
commit
13ded6bd1c
4 changed files with 330 additions and 25 deletions
|
@ -1 +1 @@
|
|||
Subproject commit 880bce9092fbef0bcbf101b8ec2e3d2c5af3fb98
|
||||
Subproject commit 4d6bf21f9ce6255086e649073b5e2fa7c26d1d27
|
|
@ -70,6 +70,7 @@ Result Registration::RegisterProcess(RoProcessContext **out_context, Handle proc
|
|||
/* Find a free process context. */
|
||||
for (size_t i = 0; i < Registration::MaxSessions; i++) {
|
||||
if (!g_process_contexts[i].in_use) {
|
||||
std::memset(&g_process_contexts[i], 0, sizeof(g_process_contexts[i]));
|
||||
g_process_contexts[i].process_id = process_id;
|
||||
g_process_contexts[i].process_handle = process_handle;
|
||||
g_process_contexts[i].in_use = true;
|
||||
|
@ -90,6 +91,7 @@ void Registration::UnregisterProcess(RoProcessContext *context) {
|
|||
UnmapNrr(context->process_handle, context->nrr_infos[i].header, context->nrr_infos[i].nrr_heap_address, context->nrr_infos[i].nrr_heap_size, context->nrr_infos[i].mapped_code_address);
|
||||
}
|
||||
}
|
||||
svcCloseHandle(context->process_handle);
|
||||
}
|
||||
std::memset(context, 0, sizeof(*context));
|
||||
}
|
||||
|
@ -193,6 +195,191 @@ Result Registration::UnloadNrr(RoProcessContext *context, u64 nrr_address) {
|
|||
return UnmapNrr(context->process_handle, nrr_info.header, nrr_info.nrr_heap_address, nrr_info.nrr_heap_size, nrr_info.mapped_code_address);
|
||||
}
|
||||
|
||||
|
||||
Result Registration::LoadNro(u64 *out_address, RoProcessContext *context, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
|
||||
/* Validate address/size. */
|
||||
if (nro_address & 0xFFF) {
|
||||
return ResultRoInvalidAddress;
|
||||
}
|
||||
if (nro_size == 0 || (nro_size & 0xFFF) || !(nro_address < nro_address + nro_size)) {
|
||||
return ResultRoInvalidSize;
|
||||
}
|
||||
if (bss_address & 0xFFF) {
|
||||
return ResultRoInvalidAddress;
|
||||
}
|
||||
if ((bss_size & 0xFFF) || (bss_size > 0 && !(bss_address < bss_address + bss_size))) {
|
||||
return ResultRoInvalidSize;
|
||||
}
|
||||
|
||||
const u64 total_size = nro_size + bss_size;
|
||||
if (total_size < nro_size || total_size < bss_size) {
|
||||
return ResultRoInvalidSize;
|
||||
}
|
||||
|
||||
/* Check we have space for a new NRO. */
|
||||
size_t slot = 0;
|
||||
for (slot = 0; slot < Registration::MaxNroInfos; slot++) {
|
||||
if (!context->nro_in_use[slot]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot == Registration::MaxNroInfos) {
|
||||
return ResultRoTooManyNro;
|
||||
}
|
||||
|
||||
NroInfo *nro_info = &context->nro_infos[slot];
|
||||
nro_info->nro_heap_address = nro_address;
|
||||
nro_info->nro_heap_size = nro_size;
|
||||
nro_info->bss_heap_address = bss_address;
|
||||
nro_info->bss_heap_size = bss_size;
|
||||
|
||||
/* Map the NRO. */
|
||||
Result rc = MapNro(&nro_info->base_address, context->process_handle, nro_address, nro_size, bss_address, bss_size);
|
||||
if (R_FAILED(rc)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Validate the NRO (parsing region extents). */
|
||||
u64 rx_size, ro_size, rw_size;
|
||||
if (R_FAILED((rc = ValidateNro(&nro_info->module_id, &rx_size, &ro_size, &rw_size, context, nro_info->base_address, nro_size, bss_size)))) {
|
||||
UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, nro_size, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Set NRO perms. */
|
||||
if (R_FAILED((rc = SetNroPerms(context->process_handle, nro_info->base_address, rx_size, ro_size, rw_size + bss_size)))) {
|
||||
UnmapNro(context->process_handle, nro_info->base_address, nro_address, bss_address, bss_size, rx_size + ro_size, rw_size);
|
||||
return rc;
|
||||
}
|
||||
|
||||
nro_info->code_size = rx_size + ro_size;
|
||||
nro_info->rw_size = rw_size;
|
||||
context->nro_in_use[slot] = true;
|
||||
*out_address = nro_info->base_address;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
bool Registration::IsNroHashPresent(RoProcessContext *context, const Sha256Hash *hash) {
|
||||
for (size_t i = 0; i < Registration::MaxNrrInfos; i++) {
|
||||
if (context->nrr_in_use[i]) {
|
||||
const Sha256Hash *nro_hashes = reinterpret_cast<const Sha256Hash *>(reinterpret_cast<uintptr_t>(context->nrr_infos[i].header) + context->nrr_infos[i].header->hash_offset);
|
||||
if (std::binary_search(nro_hashes, nro_hashes + context->nrr_infos[i].header->num_hashes, *hash)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Result Registration::ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, RoProcessContext *context, u64 base_address, u64 nro_size, u64 bss_size) {
|
||||
/* Find space to map the NRO. */
|
||||
u64 map_address;
|
||||
if (R_FAILED(MapUtils::LocateSpaceForMap(&map_address, nro_size))) {
|
||||
return ResultRoInsufficientAddressSpace;
|
||||
}
|
||||
|
||||
/* Actually map the NRO. */
|
||||
AutoCloseMap nro_map(map_address, context->process_handle, base_address, nro_size);
|
||||
if (!nro_map.IsSuccess()) {
|
||||
return nro_map.GetResult();
|
||||
}
|
||||
|
||||
/* Validate header. */
|
||||
const Registration::NroHeader *header = reinterpret_cast<const Registration::NroHeader *>(map_address);
|
||||
if (header->magic != MagicNro0) {
|
||||
return ResultRoInvalidNro;
|
||||
}
|
||||
if (header->nro_size != nro_size || header->bss_size != bss_size) {
|
||||
return ResultRoInvalidNro;
|
||||
}
|
||||
if ((header->text_size & 0xFFF) || (header->ro_size & 0xFFF) || (header->rw_size & 0xFFF) || (header->bss_size & 0xFFF)) {
|
||||
return ResultRoInvalidNro;
|
||||
}
|
||||
if (header->text_offset > header->ro_offset || header->ro_offset > header->rw_offset) {
|
||||
return ResultRoInvalidNro;
|
||||
}
|
||||
if (header->text_offset != 0 || header->text_offset + header->text_size != header->ro_offset || header->ro_offset + header->ro_size != header->rw_offset || header->rw_offset + header->rw_size != header->nro_size) {
|
||||
return ResultRoInvalidNro;
|
||||
}
|
||||
|
||||
/* Verify NRO hash. */
|
||||
{
|
||||
Sha256Hash hash;
|
||||
sha256CalculateHash(&hash, header, nro_size);
|
||||
if (!IsNroHashPresent(context, &hash)) {
|
||||
return ResultRoNotAuthorized;
|
||||
}
|
||||
}
|
||||
|
||||
ModuleId module_id;
|
||||
std::memcpy(&module_id, header->build_id, sizeof(module_id));
|
||||
|
||||
/* Check if NRO has already been loaded. */
|
||||
for (size_t i = 0; i < Registration::MaxNroInfos; i++) {
|
||||
if (context->nro_in_use[i]) {
|
||||
if (std::memcmp(&context->nro_infos[i].module_id, &module_id, sizeof(module_id)) == 0) {
|
||||
return ResultRoAlreadyLoaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*out_module_id = module_id;
|
||||
*out_rx_size = header->text_size;
|
||||
*out_ro_size = header->ro_size;
|
||||
*out_rw_size = header->rw_size;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Registration::SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size) {
|
||||
Result rc;
|
||||
const u64 rx_offset = 0;
|
||||
const u64 ro_offset = rx_offset + rx_size;
|
||||
const u64 rw_offset = ro_offset + ro_size;
|
||||
|
||||
if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + rx_offset, rx_size, 5)))) {
|
||||
return rc;
|
||||
}
|
||||
if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + ro_offset, ro_size, 1)))) {
|
||||
return rc;
|
||||
}
|
||||
if (R_FAILED((rc = svcSetProcessMemoryPermission(process_handle, base_address + rw_offset, rw_size, 3)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Registration::UnloadNro(RoProcessContext *context, u64 nro_address) {
|
||||
/* Validate address. */
|
||||
if (nro_address & 0xFFF) {
|
||||
return ResultRoInvalidAddress;
|
||||
}
|
||||
|
||||
/* Check the NRO is loaded. */
|
||||
size_t slot = 0;
|
||||
for (slot = 0; slot < Registration::MaxNroInfos; slot++) {
|
||||
if (!context->nro_in_use[slot]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (context->nro_infos[slot].nro_heap_address == nro_address) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (slot == Registration::MaxNrrInfos) {
|
||||
return ResultRoNotLoaded;
|
||||
}
|
||||
|
||||
/* Unmap. */
|
||||
const NroInfo nro_info = context->nro_infos[slot];
|
||||
{
|
||||
/* Nintendo does this unconditionally, whether or not the actual unmap succeeds. */
|
||||
context->nro_in_use[slot] = false;
|
||||
std::memset(&context->nro_infos[slot], 0, sizeof(context->nro_infos[slot]));
|
||||
}
|
||||
return UnmapNro(context->process_handle, nro_info.base_address, nro_info.nro_heap_address, nro_info.bss_heap_address, nro_info.bss_heap_size, nro_info.code_size, nro_info.rw_size);
|
||||
}
|
||||
|
||||
Result Registration::MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, RoModuleType expected_type, bool enforce_type) {
|
||||
Result rc;
|
||||
MappedCodeMemory nrr_mcm;
|
||||
|
@ -242,3 +429,86 @@ Result Registration::UnmapNrr(Handle process_handle, const NrrHeader *header, u6
|
|||
|
||||
return svcUnmapProcessCodeMemory(process_handle, mapped_code_address, nrr_heap_address, nrr_heap_size);
|
||||
}
|
||||
|
||||
Result Registration::MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size) {
|
||||
Result rc;
|
||||
MappedCodeMemory nro_mcm;
|
||||
MappedCodeMemory bss_mcm;
|
||||
u64 base_address;
|
||||
|
||||
/* Map the NRO, and map the BSS immediately after it. */
|
||||
size_t i = 0;
|
||||
for (i = 0; i < MapUtils::LocateRetryCount; i++) {
|
||||
MappedCodeMemory tmp_nro_mcm;
|
||||
bool is_64_bit = true;
|
||||
if (R_FAILED((rc = MapUtils::MapCodeMemoryForProcess(tmp_nro_mcm, process_handle, is_64_bit, nro_heap_address, nro_heap_size)))) {
|
||||
if (GetRuntimeFirmwareVersion() < FirmwareVersion_300) {
|
||||
/* Try mapping as 32-bit, since we might have guessed wrong on < 3.0.0. */
|
||||
is_64_bit = false;
|
||||
rc = MapUtils::MapCodeMemoryForProcess(tmp_nro_mcm, process_handle, is_64_bit, nro_heap_address, nro_heap_size);
|
||||
}
|
||||
if (R_FAILED(rc)) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
base_address = tmp_nro_mcm.GetDstAddress();
|
||||
|
||||
if (bss_heap_size > 0) {
|
||||
MappedCodeMemory tmp_bss_mcm(process_handle, base_address + nro_heap_size, bss_heap_address, bss_heap_size);
|
||||
rc = tmp_bss_mcm.GetResult();
|
||||
if (rc == ResultKernelInvalidMemoryState) {
|
||||
continue;
|
||||
}
|
||||
if (R_FAILED(rc)) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!MapUtils::CanAddGuardRegions(process_handle, base_address, nro_heap_size + bss_heap_size)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bss_mcm = std::move(tmp_bss_mcm);
|
||||
} else {
|
||||
if (!MapUtils::CanAddGuardRegions(process_handle, base_address, nro_heap_size)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
nro_mcm = std::move(tmp_nro_mcm);
|
||||
break;
|
||||
}
|
||||
if (i == MapUtils::LocateRetryCount) {
|
||||
return ResultRoInsufficientAddressSpace;
|
||||
}
|
||||
|
||||
/* Invalidation here actually prevents them from unmapping at scope exit. */
|
||||
nro_mcm.Invalidate();
|
||||
bss_mcm.Invalidate();
|
||||
|
||||
*out_base_address = base_address;
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
||||
Result Registration::UnmapNro(Handle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size) {
|
||||
Result rc;
|
||||
|
||||
/* First, unmap bss. */
|
||||
if (bss_heap_size > 0) {
|
||||
if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address + code_size + rw_size, bss_heap_address, bss_heap_size)))) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Next, unmap .rwdata */
|
||||
if (rw_size > 0) {
|
||||
if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address + code_size, nro_heap_address + code_size, rw_size)))) {
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally, unmap .text + .rodata. */
|
||||
if (R_FAILED((rc = svcUnmapProcessCodeMemory(process_handle, base_address, nro_heap_address, code_size)))) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
return ResultSuccess;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ class Registration {
|
|||
static constexpr size_t MaxSessions = 0x4;
|
||||
static constexpr size_t MaxNrrInfos = 0x40;
|
||||
static constexpr size_t MaxNroInfos = 0x40;
|
||||
|
||||
static constexpr u32 MagicNro0 = 0x304F524E;
|
||||
public:
|
||||
|
||||
struct NroHeader {
|
||||
|
@ -55,6 +57,24 @@ class Registration {
|
|||
};
|
||||
static_assert(sizeof(ModuleId) == sizeof(LoaderModuleInfo::build_id), "ModuleId definition!");
|
||||
|
||||
struct Sha256Hash {
|
||||
u8 hash[0x20];
|
||||
|
||||
bool operator==(const Sha256Hash &o) const {
|
||||
return std::memcmp(this, &o, sizeof(*this)) == 0;
|
||||
}
|
||||
bool operator!=(const Sha256Hash &o) const {
|
||||
return std::memcmp(this, &o, sizeof(*this)) != 0;
|
||||
}
|
||||
bool operator<(const Sha256Hash &o) const {
|
||||
return std::memcmp(this, &o, sizeof(*this)) < 0;
|
||||
}
|
||||
bool operator>(const Sha256Hash &o) const {
|
||||
return std::memcmp(this, &o, sizeof(*this)) > 0;
|
||||
}
|
||||
};
|
||||
static_assert(sizeof(Sha256Hash) == sizeof(Sha256Hash::hash), "Sha256Hash definition!");
|
||||
|
||||
struct NroInfo {
|
||||
u64 base_address;
|
||||
u64 nro_heap_address;
|
||||
|
@ -83,6 +103,12 @@ class Registration {
|
|||
private:
|
||||
static Result MapAndValidateNrr(NrrHeader **out_header, u64 *out_mapped_code_address, Handle process_handle, u64 title_id, u64 nrr_heap_address, u64 nrr_heap_size, RoModuleType expected_type, bool enforce_type);
|
||||
static Result UnmapNrr(Handle process_handle, const NrrHeader *header, u64 nrr_heap_address, u64 nrr_heap_size, u64 mapped_code_address);
|
||||
static bool IsNroHashPresent(RoProcessContext *context, const Sha256Hash *hash);
|
||||
|
||||
static Result MapNro(u64 *out_base_address, Handle process_handle, u64 nro_heap_address, u64 nro_heap_size, u64 bss_heap_address, u64 bss_heap_size);
|
||||
static Result ValidateNro(ModuleId *out_module_id, u64 *out_rx_size, u64 *out_ro_size, u64 *out_rw_size, RoProcessContext *context, u64 base_address, u64 nro_size, u64 bss_size);
|
||||
static Result SetNroPerms(Handle process_handle, u64 base_address, u64 rx_size, u64 ro_size, u64 rw_size);
|
||||
static Result UnmapNro(Handle process_handle, u64 base_address, u64 nro_heap_address, u64 bss_heap_address, u64 bss_heap_size, u64 code_size, u64 rw_size);
|
||||
public:
|
||||
static void Initialize();
|
||||
static bool ShouldEaseNroRestriction();
|
||||
|
@ -92,6 +118,8 @@ class Registration {
|
|||
|
||||
static Result LoadNrr(RoProcessContext *context, u64 title_id, u64 nrr_address, u64 nrr_size, RoModuleType expected_type, bool enforce_type);
|
||||
static Result UnloadNrr(RoProcessContext *context, u64 nrr_address);
|
||||
static Result LoadNro(u64 *out_address, RoProcessContext *context, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size);
|
||||
static Result UnloadNro(RoProcessContext *context, u64 nro_address);
|
||||
|
||||
static Result GetProcessModuleInfo(u32 *out_count, LoaderModuleInfo *out_infos, size_t max_out_count, u64 process_id);
|
||||
};
|
|
@ -25,6 +25,7 @@
|
|||
RelocatableObjectsService::~RelocatableObjectsService() {
|
||||
if (this->IsInitialized()) {
|
||||
Registration::UnregisterProcess(this->context);
|
||||
this->context = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,13 +58,19 @@ u64 RelocatableObjectsService::GetTitleId(Handle process_handle) {
|
|||
}
|
||||
|
||||
Result RelocatableObjectsService::LoadNro(Out<u64> load_address, PidDescriptor pid_desc, u64 nro_address, u64 nro_size, u64 bss_address, u64 bss_size) {
|
||||
/* TODO */
|
||||
return ResultKernelConnectionClosed;
|
||||
if (!this->IsProcessIdValid(pid_desc.pid)) {
|
||||
return ResultRoInvalidProcess;
|
||||
}
|
||||
|
||||
return Registration::LoadNro(load_address.GetPointer(), this->context, nro_address, nro_size, bss_address, bss_size);
|
||||
}
|
||||
|
||||
Result RelocatableObjectsService::UnloadNro(PidDescriptor pid_desc, u64 nro_address) {
|
||||
/* TODO */
|
||||
return ResultKernelConnectionClosed;
|
||||
if (!this->IsProcessIdValid(pid_desc.pid)) {
|
||||
return ResultRoInvalidProcess;
|
||||
}
|
||||
|
||||
return Registration::UnloadNro(this->context, nro_address);
|
||||
}
|
||||
|
||||
Result RelocatableObjectsService::LoadNrr(PidDescriptor pid_desc, u64 nrr_address, u64 nrr_size) {
|
||||
|
|
Loading…
Reference in a new issue