diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/service/ldr/ldr.cpp | 95 |
1 files changed, 59 insertions, 36 deletions
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 69caf3aae..5b372b7db 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -39,22 +39,27 @@ constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87}; | |||
| 39 | constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; | 39 | constexpr std::size_t MAXIMUM_LOADED_RO{0x40}; |
| 40 | constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; | 40 | constexpr std::size_t MAXIMUM_MAP_RETRIES{0x200}; |
| 41 | 41 | ||
| 42 | struct Certification { | 42 | constexpr std::size_t TEXT_INDEX{0}; |
| 43 | constexpr std::size_t RO_INDEX{1}; | ||
| 44 | constexpr std::size_t DATA_INDEX{2}; | ||
| 45 | |||
| 46 | struct NRRCertification { | ||
| 43 | u64_le application_id_mask; | 47 | u64_le application_id_mask; |
| 44 | u64_le application_id_pattern; | 48 | u64_le application_id_pattern; |
| 45 | std::array<u8, 0x10> reserved; | 49 | std::array<u8, 0x10> reserved; |
| 46 | std::array<u8, 0x100> public_key; // Also known as modulus | 50 | std::array<u8, 0x100> public_key; // Also known as modulus |
| 47 | std::array<u8, 0x100> signature; | 51 | std::array<u8, 0x100> signature; |
| 48 | }; | 52 | }; |
| 49 | static_assert(sizeof(Certification) == 0x220, "Certification has invalid size!"); | 53 | static_assert(sizeof(NRRCertification) == 0x220, "Certification has invalid size!"); |
| 50 | 54 | ||
| 51 | using SHA256Hash = std::array<u8, 0x20>; | 55 | using SHA256Hash = std::array<u8, 0x20>; |
| 52 | 56 | ||
| 57 | #pragma pack(1) | ||
| 53 | struct NRRHeader { | 58 | struct NRRHeader { |
| 54 | u32_le magic; | 59 | u32_le magic; |
| 55 | u32_le certification_signature_key_generation; // 9.0.0+ | 60 | u32_le certification_signature_key_generation; // 9.0.0+ |
| 56 | u64_le reserved; | 61 | u64_le reserved; |
| 57 | Certification certification; | 62 | NRRCertification certification; |
| 58 | std::array<u8, 0x100> signature; | 63 | std::array<u8, 0x100> signature; |
| 59 | u64_le application_id; | 64 | u64_le application_id; |
| 60 | u32_le size; | 65 | u32_le size; |
| @@ -63,21 +68,19 @@ struct NRRHeader { | |||
| 63 | u32_le hash_offset; | 68 | u32_le hash_offset; |
| 64 | u32_le hash_count; | 69 | u32_le hash_count; |
| 65 | u64_le reserved_3; | 70 | u64_le reserved_3; |
| 66 | |||
| 67 | // Must be dynamically allocated because, according to | ||
| 68 | // SwitchBrew, its size is (0x20 * hash_count) and | ||
| 69 | // it's impossible to determine the value of hash_count | ||
| 70 | // (SwitchBrew calls it "NumHash") before runtime, | ||
| 71 | // therefore it's not possible to calculate a SHA-256 | ||
| 72 | std::vector<SHA256Hash> NroHashList; | ||
| 73 | }; | 71 | }; |
| 72 | #pragma pack() | ||
| 73 | static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has invalid size!"); | ||
| 74 | 74 | ||
| 75 | #pragma pack(1) | ||
| 75 | struct SegmentHeader { | 76 | struct SegmentHeader { |
| 76 | u32_le memory_offset; | 77 | u32_le memory_offset; |
| 77 | u32_le memory_size; | 78 | u32_le memory_size; |
| 78 | }; | 79 | }; |
| 80 | #pragma pack() | ||
| 79 | static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size!"); | 81 | static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size!"); |
| 80 | 82 | ||
| 83 | #pragma pack(1) | ||
| 81 | struct NROHeader { | 84 | struct NROHeader { |
| 82 | // Switchbrew calls this "Start" (0x10) | 85 | // Switchbrew calls this "Start" (0x10) |
| 83 | u32_le unused; | 86 | u32_le unused; |
| @@ -99,8 +102,10 @@ struct NROHeader { | |||
| 99 | // .apiInfo, .dynstr, .dynsym | 102 | // .apiInfo, .dynstr, .dynsym |
| 100 | std::array<SegmentHeader, 3> segment_headers_2; | 103 | std::array<SegmentHeader, 3> segment_headers_2; |
| 101 | }; | 104 | }; |
| 105 | #pragma pack() | ||
| 102 | static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); | 106 | static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); |
| 103 | 107 | ||
| 108 | #pragma pack(1) | ||
| 104 | struct NROInfo { | 109 | struct NROInfo { |
| 105 | SHA256Hash hash{}; | 110 | SHA256Hash hash{}; |
| 106 | VAddr nro_address{}; | 111 | VAddr nro_address{}; |
| @@ -112,6 +117,8 @@ struct NROInfo { | |||
| 112 | std::size_t data_size{}; | 117 | std::size_t data_size{}; |
| 113 | VAddr src_addr{}; | 118 | VAddr src_addr{}; |
| 114 | }; | 119 | }; |
| 120 | #pragma pack() | ||
| 121 | static_assert(sizeof(NROInfo) == 0x60, "NROInfo has invalid size."); | ||
| 115 | 122 | ||
| 116 | class DebugMonitor final : public ServiceFramework<DebugMonitor> { | 123 | class DebugMonitor final : public ServiceFramework<DebugMonitor> { |
| 117 | public: | 124 | public: |
| @@ -369,10 +376,10 @@ public: | |||
| 369 | 376 | ||
| 370 | ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, | 377 | ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, |
| 371 | VAddr start) const { | 378 | VAddr start) const { |
| 372 | const VAddr text_start{start + nro_header.segment_headers[0].memory_offset}; | 379 | const VAddr text_start{start + nro_header.segment_headers[TEXT_INDEX].memory_offset}; |
| 373 | const VAddr ro_start{start + nro_header.segment_headers[1].memory_offset}; | 380 | const VAddr ro_start{start + nro_header.segment_headers[RO_INDEX].memory_offset}; |
| 374 | const VAddr data_start{start + nro_header.segment_headers[2].memory_offset}; | 381 | const VAddr data_start{start + nro_header.segment_headers[DATA_INDEX].memory_offset}; |
| 375 | const VAddr bss_start{data_start + nro_header.segment_headers[2].memory_size}; | 382 | const VAddr bss_start{data_start + nro_header.segment_headers[DATA_INDEX].memory_size}; |
| 376 | const VAddr bss_end_addr{ | 383 | const VAddr bss_end_addr{ |
| 377 | Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; | 384 | Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; |
| 378 | 385 | ||
| @@ -381,12 +388,12 @@ public: | |||
| 381 | system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); | 388 | system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); |
| 382 | system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); | 389 | system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); |
| 383 | }}; | 390 | }}; |
| 384 | CopyCode(nro_addr + nro_header.segment_headers[0].memory_offset, text_start, | 391 | CopyCode(nro_addr + nro_header.segment_headers[TEXT_INDEX].memory_offset, text_start, |
| 385 | nro_header.segment_headers[0].memory_size); | 392 | nro_header.segment_headers[TEXT_INDEX].memory_size); |
| 386 | CopyCode(nro_addr + nro_header.segment_headers[1].memory_offset, ro_start, | 393 | CopyCode(nro_addr + nro_header.segment_headers[RO_INDEX].memory_offset, ro_start, |
| 387 | nro_header.segment_headers[1].memory_size); | 394 | nro_header.segment_headers[RO_INDEX].memory_size); |
| 388 | CopyCode(nro_addr + nro_header.segment_headers[2].memory_offset, data_start, | 395 | CopyCode(nro_addr + nro_header.segment_headers[DATA_INDEX].memory_offset, data_start, |
| 389 | nro_header.segment_headers[2].memory_size); | 396 | nro_header.segment_headers[DATA_INDEX].memory_size); |
| 390 | 397 | ||
| 391 | CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( | 398 | CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( |
| 392 | text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); | 399 | text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); |
| @@ -510,9 +517,9 @@ public: | |||
| 510 | // Track the loaded NRO | 517 | // Track the loaded NRO |
| 511 | nro.insert_or_assign(*map_result, | 518 | nro.insert_or_assign(*map_result, |
| 512 | NROInfo{hash, *map_result, nro_size, bss_address, bss_size, | 519 | NROInfo{hash, *map_result, nro_size, bss_address, bss_size, |
| 513 | header.segment_headers[0].memory_size, | 520 | header.segment_headers[TEXT_INDEX].memory_size, |
| 514 | header.segment_headers[1].memory_size, | 521 | header.segment_headers[RO_INDEX].memory_size, |
| 515 | header.segment_headers[2].memory_size, nro_address}); | 522 | header.segment_headers[DATA_INDEX].memory_size, nro_address}); |
| 516 | 523 | ||
| 517 | // Invalidate JIT caches for the newly mapped process code | 524 | // Invalidate JIT caches for the newly mapped process code |
| 518 | system.InvalidateCpuInstructionCaches(); | 525 | system.InvalidateCpuInstructionCaches(); |
| @@ -608,19 +615,35 @@ private: | |||
| 608 | } | 615 | } |
| 609 | 616 | ||
| 610 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { | 617 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { |
| 611 | return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && | 618 | |
| 612 | header.nro_size == nro_size && header.bss_size == bss_size && | 619 | const bool valid_magic = header.magic == Common::MakeMagic('N', 'R', 'O', '0'); |
| 613 | header.segment_headers[1].memory_offset == | 620 | |
| 614 | header.segment_headers[0].memory_offset + | 621 | const bool valid_nro_size = header.nro_size == nro_size; |
| 615 | header.segment_headers[0].memory_size && | 622 | |
| 616 | header.segment_headers[2].memory_offset == | 623 | const bool valid_bss_size = header.bss_size == bss_size; |
| 617 | header.segment_headers[1].memory_offset + | 624 | |
| 618 | header.segment_headers[1].memory_size && | 625 | const bool valid_ro_offset = header.segment_headers[RO_INDEX].memory_offset == |
| 619 | nro_size == header.segment_headers[2].memory_offset + | 626 | header.segment_headers[TEXT_INDEX].memory_offset + |
| 620 | header.segment_headers[2].memory_size && | 627 | header.segment_headers[TEXT_INDEX].memory_size; |
| 621 | Common::Is4KBAligned(header.segment_headers[0].memory_size) && | 628 | |
| 622 | Common::Is4KBAligned(header.segment_headers[1].memory_size) && | 629 | const bool valid_rw_offset = header.segment_headers[DATA_INDEX].memory_offset == |
| 623 | Common::Is4KBAligned(header.segment_headers[2].memory_size); | 630 | header.segment_headers[RO_INDEX].memory_offset + |
| 631 | header.segment_headers[RO_INDEX].memory_size; | ||
| 632 | |||
| 633 | const bool valid_nro_calculated_size = | ||
| 634 | nro_size == header.segment_headers[DATA_INDEX].memory_offset + | ||
| 635 | header.segment_headers[DATA_INDEX].memory_size; | ||
| 636 | |||
| 637 | const bool text_aligned = | ||
| 638 | Common::Is4KBAligned(header.segment_headers[TEXT_INDEX].memory_size); | ||
| 639 | |||
| 640 | const bool ro_aligned = Common::Is4KBAligned(header.segment_headers[RO_INDEX].memory_size); | ||
| 641 | |||
| 642 | const bool rw_aligned = | ||
| 643 | Common::Is4KBAligned(header.segment_headers[DATA_INDEX].memory_size); | ||
| 644 | |||
| 645 | return valid_magic && valid_nro_size && valid_bss_size && valid_ro_offset && | ||
| 646 | valid_rw_offset && valid_nro_calculated_size && text_aligned && ro_aligned && rw_aligned; | ||
| 624 | } | 647 | } |
| 625 | Core::System& system; | 648 | Core::System& system; |
| 626 | }; | 649 | }; |