diff options
| -rw-r--r-- | src/core/hle/service/ldr/ldr.cpp | 112 |
1 files changed, 72 insertions, 40 deletions
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp index 6ad3be1b3..62d4bfc08 100644 --- a/src/core/hle/service/ldr/ldr.cpp +++ b/src/core/hle/service/ldr/ldr.cpp | |||
| @@ -39,47 +39,68 @@ 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 { | ||
| 43 | u64_le application_id_mask; | ||
| 44 | u64_le application_id_pattern; | ||
| 45 | std::array<u8, 0x10> reserved; | ||
| 46 | std::array<u8, 0x100> public_key; // Also known as modulus | ||
| 47 | std::array<u8, 0x100> signature; | ||
| 48 | }; | ||
| 49 | static_assert(sizeof(Certification) == 0x220, "Certification has invalid size!"); | ||
| 50 | |||
| 51 | using SHA256Hash = std::array<u8, 0x20>; | ||
| 52 | |||
| 42 | struct NRRHeader { | 53 | struct NRRHeader { |
| 43 | u32_le magic; | 54 | u32_le magic; |
| 44 | INSERT_PADDING_BYTES(12); | 55 | u32_le certification_signature_key_generation; // 9.0.0+ |
| 45 | u64_le title_id_mask; | 56 | u64_le reserved; |
| 46 | u64_le title_id_pattern; | 57 | Certification certification; |
| 47 | INSERT_PADDING_BYTES(16); | 58 | std::array<u8, 0x100> signature; |
| 48 | std::array<u8, 0x100> modulus; | 59 | u64_le application_id; |
| 49 | std::array<u8, 0x100> signature_1; | ||
| 50 | std::array<u8, 0x100> signature_2; | ||
| 51 | u64_le title_id; | ||
| 52 | u32_le size; | 60 | u32_le size; |
| 53 | INSERT_PADDING_BYTES(4); | 61 | u8 nrr_kind; |
| 62 | std::array<u8, 0x3> reserved_2; | ||
| 54 | u32_le hash_offset; | 63 | u32_le hash_offset; |
| 55 | u32_le hash_count; | 64 | u32_le hash_count; |
| 56 | INSERT_PADDING_BYTES(8); | 65 | 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; | ||
| 57 | }; | 73 | }; |
| 58 | static_assert(sizeof(NRRHeader) == 0x350, "NRRHeader has incorrect size."); | 74 | |
| 75 | struct SegmentHeader { | ||
| 76 | u32_le memory_offset; | ||
| 77 | u32_le memory_size; | ||
| 78 | }; | ||
| 79 | static_assert(sizeof(SegmentHeader) == 0x8, "SegmentHeader has invalid size!"); | ||
| 59 | 80 | ||
| 60 | struct NROHeader { | 81 | struct NROHeader { |
| 61 | INSERT_PADDING_WORDS(1); | 82 | // Switchbrew calls this "Start" (0x10) |
| 83 | u32_le unused; | ||
| 62 | u32_le mod_offset; | 84 | u32_le mod_offset; |
| 63 | INSERT_PADDING_WORDS(2); | 85 | u64_le padding; |
| 86 | |||
| 87 | // Switchbrew calls this "Header" (0x70) | ||
| 64 | u32_le magic; | 88 | u32_le magic; |
| 65 | u32_le version; | 89 | u32_le version; |
| 66 | u32_le nro_size; | 90 | u32_le nro_size; |
| 67 | u32_le flags; | 91 | u32_le flags; |
| 68 | u32_le text_offset; | 92 | // .text, .ro, .data (yuzu previously called it "rw" instead of "data") |
| 69 | u32_le text_size; | 93 | std::array<SegmentHeader, 0x3> segment_headers; |
| 70 | u32_le ro_offset; | ||
| 71 | u32_le ro_size; | ||
| 72 | u32_le rw_offset; | ||
| 73 | u32_le rw_size; | ||
| 74 | u32_le bss_size; | 94 | u32_le bss_size; |
| 75 | INSERT_PADDING_WORDS(1); | 95 | u32_le reserved; |
| 76 | std::array<u8, 0x20> build_id; | 96 | std::array<u8, 0x20> build_id; |
| 77 | INSERT_PADDING_BYTES(0x20); | 97 | u32_le dso_handle_offset; |
| 98 | u32_le unused_2; | ||
| 99 | // .apiInfo, .dynstr, .dynsym | ||
| 100 | std::array<SegmentHeader, 0x3> segment_headers_2; | ||
| 78 | }; | 101 | }; |
| 79 | static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); | 102 | static_assert(sizeof(NROHeader) == 0x80, "NROHeader has invalid size."); |
| 80 | 103 | ||
| 81 | using SHA256Hash = std::array<u8, 0x20>; | ||
| 82 | |||
| 83 | struct NROInfo { | 104 | struct NROInfo { |
| 84 | SHA256Hash hash{}; | 105 | SHA256Hash hash{}; |
| 85 | VAddr nro_address{}; | 106 | VAddr nro_address{}; |
| @@ -226,11 +247,11 @@ public: | |||
| 226 | return; | 247 | return; |
| 227 | } | 248 | } |
| 228 | 249 | ||
| 229 | if (system.CurrentProcess()->GetTitleID() != header.title_id) { | 250 | if (system.CurrentProcess()->GetTitleID() != header.application_id) { |
| 230 | LOG_ERROR(Service_LDR, | 251 | LOG_ERROR(Service_LDR, |
| 231 | "Attempting to load NRR with title ID other than current process. (actual " | 252 | "Attempting to load NRR with title ID other than current process. (actual " |
| 232 | "{:016X})!", | 253 | "{:016X})!", |
| 233 | header.title_id); | 254 | header.application_id); |
| 234 | IPC::ResponseBuilder rb{ctx, 2}; | 255 | IPC::ResponseBuilder rb{ctx, 2}; |
| 235 | rb.Push(ERROR_INVALID_NRR); | 256 | rb.Push(ERROR_INVALID_NRR); |
| 236 | return; | 257 | return; |
| @@ -348,10 +369,10 @@ public: | |||
| 348 | 369 | ||
| 349 | ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, | 370 | ResultCode LoadNro(Kernel::Process* process, const NROHeader& nro_header, VAddr nro_addr, |
| 350 | VAddr start) const { | 371 | VAddr start) const { |
| 351 | const VAddr text_start{start + nro_header.text_offset}; | 372 | const VAddr text_start{start + nro_header.segment_headers[0].memory_offset}; |
| 352 | const VAddr ro_start{start + nro_header.ro_offset}; | 373 | const VAddr ro_start{start + nro_header.segment_headers[1].memory_offset}; |
| 353 | const VAddr data_start{start + nro_header.rw_offset}; | 374 | const VAddr data_start{start + nro_header.segment_headers[2].memory_offset}; |
| 354 | const VAddr bss_start{data_start + nro_header.rw_size}; | 375 | const VAddr bss_start{data_start + nro_header.segment_headers[2].memory_size}; |
| 355 | const VAddr bss_end_addr{ | 376 | const VAddr bss_end_addr{ |
| 356 | Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; | 377 | Common::AlignUp(bss_start + nro_header.bss_size, Kernel::Memory::PageSize)}; |
| 357 | 378 | ||
| @@ -360,9 +381,12 @@ public: | |||
| 360 | system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); | 381 | system.Memory().ReadBlock(src_addr, source_data.data(), source_data.size()); |
| 361 | system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); | 382 | system.Memory().WriteBlock(dst_addr, source_data.data(), source_data.size()); |
| 362 | }}; | 383 | }}; |
| 363 | CopyCode(nro_addr + nro_header.text_offset, text_start, nro_header.text_size); | 384 | CopyCode(nro_addr + nro_header.segment_headers[0].memory_offset, text_start, |
| 364 | CopyCode(nro_addr + nro_header.ro_offset, ro_start, nro_header.ro_size); | 385 | nro_header.segment_headers[0].memory_size); |
| 365 | CopyCode(nro_addr + nro_header.rw_offset, data_start, nro_header.rw_size); | 386 | CopyCode(nro_addr + nro_header.segment_headers[1].memory_offset, ro_start, |
| 387 | nro_header.segment_headers[1].memory_size); | ||
| 388 | CopyCode(nro_addr + nro_header.segment_headers[2].memory_offset, data_start, | ||
| 389 | nro_header.segment_headers[2].memory_size); | ||
| 366 | 390 | ||
| 367 | CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( | 391 | CASCADE_CODE(process->PageTable().SetCodeMemoryPermission( |
| 368 | text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); | 392 | text_start, ro_start - text_start, Kernel::Memory::MemoryPermission::ReadAndExecute)); |
| @@ -484,9 +508,11 @@ public: | |||
| 484 | } | 508 | } |
| 485 | 509 | ||
| 486 | // Track the loaded NRO | 510 | // Track the loaded NRO |
| 487 | nro.insert_or_assign(*map_result, NROInfo{hash, *map_result, nro_size, bss_address, | 511 | nro.insert_or_assign(*map_result, |
| 488 | bss_size, header.text_size, header.ro_size, | 512 | NROInfo{hash, *map_result, nro_size, bss_address, bss_size, |
| 489 | header.rw_size, nro_address}); | 513 | header.segment_headers[0].memory_size, |
| 514 | header.segment_headers[1].memory_size, | ||
| 515 | header.segment_headers[2].memory_size, nro_address}); | ||
| 490 | 516 | ||
| 491 | // Invalidate JIT caches for the newly mapped process code | 517 | // Invalidate JIT caches for the newly mapped process code |
| 492 | system.InvalidateCpuInstructionCaches(); | 518 | system.InvalidateCpuInstructionCaches(); |
| @@ -584,11 +610,17 @@ private: | |||
| 584 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { | 610 | static bool IsValidNRO(const NROHeader& header, u64 nro_size, u64 bss_size) { |
| 585 | return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && | 611 | return header.magic == Common::MakeMagic('N', 'R', 'O', '0') && |
| 586 | header.nro_size == nro_size && header.bss_size == bss_size && | 612 | header.nro_size == nro_size && header.bss_size == bss_size && |
| 587 | header.ro_offset == header.text_offset + header.text_size && | 613 | header.segment_headers[1].memory_offset == |
| 588 | header.rw_offset == header.ro_offset + header.ro_size && | 614 | header.segment_headers[0].memory_offset + |
| 589 | nro_size == header.rw_offset + header.rw_size && | 615 | header.segment_headers[0].memory_size && |
| 590 | Common::Is4KBAligned(header.text_size) && Common::Is4KBAligned(header.ro_size) && | 616 | header.segment_headers[2].memory_offset == |
| 591 | Common::Is4KBAligned(header.rw_size); | 617 | header.segment_headers[1].memory_offset + |
| 618 | header.segment_headers[1].memory_size && | ||
| 619 | nro_size == header.segment_headers[2].memory_offset + | ||
| 620 | header.segment_headers[2].memory_size && | ||
| 621 | Common::Is4KBAligned(header.segment_headers[0].memory_size) && | ||
| 622 | Common::Is4KBAligned(header.segment_headers[1].memory_size) && | ||
| 623 | Common::Is4KBAligned(header.segment_headers[2].memory_size); | ||
| 592 | } | 624 | } |
| 593 | Core::System& system; | 625 | Core::System& system; |
| 594 | }; | 626 | }; |