From 9d0c4c150ee2aeb0c695313177f8ce139f32487f Mon Sep 17 00:00:00 2001 From: Nomi Date: Sun, 27 Aug 2023 17:22:52 +0200 Subject: [PATCH 01/15] Stub LDR_RO::LoadCRONew --- include/services/ldr_ro.hpp | 1 + src/core/services/ldr_ro.cpp | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/include/services/ldr_ro.hpp b/include/services/ldr_ro.hpp index 7acfce698..8e530e1e0 100644 --- a/include/services/ldr_ro.hpp +++ b/include/services/ldr_ro.hpp @@ -13,6 +13,7 @@ class LDRService { // Service commands void initialize(u32 messagePointer); void loadCRR(u32 messagePointer); + void loadCRONew(u32 messagePointer); public: LDRService(Memory& mem) : mem(mem) {} diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index 4ea73b4ef..9a9376718 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -4,7 +4,8 @@ namespace LDRCommands { enum : u32 { Initialize = 0x000100C2, - LoadCRR = 0x00020082 + LoadCRR = 0x00020082, + LoadCRONew = 0x000902C2, }; } @@ -15,6 +16,7 @@ void LDRService::handleSyncRequest(u32 messagePointer) { switch (command) { case LDRCommands::Initialize: initialize(messagePointer); break; case LDRCommands::LoadCRR: loadCRR(messagePointer); break; + case LDRCommands::LoadCRONew: loadCRONew(messagePointer); break; default: Helpers::panic("LDR::RO service requested. Command: %08X\n", command); } } @@ -38,4 +40,22 @@ void LDRService::loadCRR(u32 messagePointer) { log("LDR_RO::LoadCRR (buffer = %08X, size = %08X, process = %X)\n", crrPointer, size, process); mem.write32(messagePointer, IPC::responseHeader(0x2, 1, 0)); mem.write32(messagePointer + 4, Result::Success); +} + +void LDRService::loadCRONew(u32 messagePointer) { + const u32 croPointer = mem.read32(messagePointer + 4); + const u32 mapVaddr = mem.read32(messagePointer + 8); + const u32 size = mem.read32(messagePointer + 12); + const u32 dataVaddr = mem.read32(messagePointer + 16); + const u32 dataSize = mem.read32(messagePointer + 24); + const u32 bssVaddr = mem.read32(messagePointer + 28); + const u32 bssSize = mem.read32(messagePointer + 32); + const bool autoLink = mem.read32(messagePointer + 36) != 0; + const u32 fixLevel = mem.read32(messagePointer + 40); + const Handle process = mem.read32(messagePointer + 52); + + log("LDR_RO::LoadCRONew (buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); + mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); + mem.write32(messagePointer + 8, size); } \ No newline at end of file From 205aedc02c36073ca3647e1306915d3d84360a61 Mon Sep 17 00:00:00 2001 From: Nomi Date: Tue, 5 Sep 2023 19:58:47 +0200 Subject: [PATCH 02/15] Add first batch of rebasing --- src/core/services/ldr_ro.cpp | 256 ++++++++++++++++++++++++++++++++++- 1 file changed, 255 insertions(+), 1 deletion(-) diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index 9a9376718..d719f9bc9 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -9,6 +9,229 @@ namespace LDRCommands { }; } +static constexpr u32 CRO_HEADER_SIZE = 0x138; + +class CRO { + // CRO header offsets + static constexpr u32 HEADER_ID = 0x80; + static constexpr u32 HEADER_NAME_OFFSET = 0x84; + static constexpr u32 HEADER_NEXT_CRO = 0x88; + static constexpr u32 HEADER_PREV_CRO = 0x8C; + static constexpr u32 HEADER_CODE_OFFSET = 0xB0; + static constexpr u32 HEADER_DATA_OFFSET = 0xB8; + static constexpr u32 HEADER_MODULE_NAME_OFFSET = 0xC0; + static constexpr u32 HEADER_SEGMENT_TABLE_OFFSET = 0xC8; + static constexpr u32 HEADER_SEGMENT_TABLE_SIZE = 0xCC; + static constexpr u32 HEADER_NAMED_EXPORT_TABLE_OFFSET = 0xD0; + static constexpr u32 HEADER_NAMED_EXPORT_TABLE_SIZE = 0xD4; + static constexpr u32 HEADER_INDEXED_EXPORT_TABLE_OFFSET = 0xD8; + static constexpr u32 HEADER_EXPORT_STRINGS_OFFSET = 0xE0; + static constexpr u32 HEADER_EXPORT_TREE_OFFSET = 0xE8; + static constexpr u32 HEADER_IMPORT_MODULE_TABLE_OFFSET = 0xF0; + static constexpr u32 HEADER_IMPORT_MODULE_TABLE_SIZE = 0xF4; + static constexpr u32 HEADER_IMPORT_PATCHES_OFFSET = 0xF8; + static constexpr u32 HEADER_NAMED_IMPORT_TABLE_OFFSET = 0x100; + static constexpr u32 HEADER_INDEXED_IMPORT_TABLE_OFFSET = 0x108; + static constexpr u32 HEADER_ANONYMOUS_IMPORT_TABLE_OFFSET = 0x110; + static constexpr u32 HEADER_IMPORT_STRINGS_OFFSET = 0x118; + static constexpr u32 HEADER_STATIC_ANONYMOUS_SYMBOLS_OFFSET = 0x120; + static constexpr u32 HEADER_RELOCATION_PATCHES_OFFSET = 0x128; + static constexpr u32 HEADER_STATIC_ANONYMOUS_PATCHES_OFFSET = 0x130; + + // Segment table entry offsets + static constexpr u32 SEGMENT_OFFSET = 0; + static constexpr u32 SEGMENT_ID = 8; + static constexpr u32 SEGMENT_ENTRY_SIZE = 12; + + // Segment table entry IDs + static constexpr u32 SEGMENT_ID_TEXT = 0; + static constexpr u32 SEGMENT_ID_RODATA = 1; + static constexpr u32 SEGMENT_ID_DATA = 2; + static constexpr u32 SEGMENT_ID_BSS = 3; + + static constexpr u32 NAMED_EXPORT_ENTRY_SIZE = 8; + + // Import module table + static constexpr u32 IMPORT_MODULE_TABLE_NAME_OFFSET = 0; + static constexpr u32 IMPORT_MODULE_TABLE_INDEXED_OFFSET = 8; + static constexpr u32 IMPORT_MODULE_TABLE_ANONYMOUS_OFFSET = 16; + static constexpr u32 IMPORT_MODULE_TABLE_ENTRY_SIZE = 20; + + Memory &mem; + + u32 croPointer; + +public: + CRO(Memory &mem, u32 croPointer) : mem(mem), croPointer(croPointer) {} + ~CRO() = default; + + bool load() { + const u8* header = (u8*)mem.getReadPointer(croPointer); + + // TODO: verify SHA hashes? + + // Verify CRO magic + if (std::memcmp(&header[HEADER_ID], "CRO0", 4) != 0) { + return false; + } + + // These fields are initially 0, the RO service sets them on load. If non-0, + // this CRO has already been loaded + if ((*(u32*)&header[HEADER_NEXT_CRO] != 0) || (*(u32*)&header[HEADER_PREV_CRO] != 0)) { + return false; + } + + return true; + } + + // Modify CRO offsets to point at virtual addresses + bool rebase(u32 mapVaddr, u32 dataVaddr, u32 bssVaddr) { + rebaseHeader(mapVaddr); + rebaseSegmentTable(mapVaddr, dataVaddr, bssVaddr); + rebaseNamedExportTable(mapVaddr); + rebaseImportModuleTable(mapVaddr); + + return true; + } + + bool rebaseHeader(u32 mapVaddr) { + std::puts("Rebasing CRO header"); + + const u8* header = (u8*)mem.getReadPointer(croPointer); + + constexpr u32 headerOffsets[] = { + HEADER_NAME_OFFSET, + HEADER_CODE_OFFSET, + HEADER_DATA_OFFSET, + HEADER_MODULE_NAME_OFFSET, + HEADER_SEGMENT_TABLE_OFFSET, + HEADER_NAMED_EXPORT_TABLE_OFFSET, + HEADER_INDEXED_EXPORT_TABLE_OFFSET, + HEADER_EXPORT_STRINGS_OFFSET, + HEADER_EXPORT_TREE_OFFSET, + HEADER_IMPORT_MODULE_TABLE_OFFSET, + HEADER_IMPORT_PATCHES_OFFSET, + HEADER_NAMED_IMPORT_TABLE_OFFSET, + HEADER_INDEXED_IMPORT_TABLE_OFFSET, + HEADER_ANONYMOUS_IMPORT_TABLE_OFFSET, + HEADER_IMPORT_STRINGS_OFFSET, + HEADER_STATIC_ANONYMOUS_SYMBOLS_OFFSET, // ? + HEADER_RELOCATION_PATCHES_OFFSET, + HEADER_STATIC_ANONYMOUS_PATCHES_OFFSET, // ? + }; + + for (auto offset : headerOffsets) { + *(u32*)&header[offset] += mapVaddr; + } + + return true; + } + + bool rebaseSegmentTable(u32 mapVaddr, u32 dataVaddr, u32 bssVaddr) { + std::puts("Rebasing segment table"); + + const u8* header = (u8*)mem.getReadPointer(croPointer); + + const u32 segmentTableAddr = *(u32*)&header[HEADER_SEGMENT_TABLE_OFFSET]; + const u32 segmentTableSize = *(u32*)&header[HEADER_SEGMENT_TABLE_SIZE]; + + if ((segmentTableAddr & 3) != 0) { + Helpers::panic("Unaligned segment table address"); + } + + if (segmentTableSize == 0) { + Helpers::panic("Segment table empty"); + } + + const u8* segmentTable = (u8*)mem.getReadPointer(segmentTableAddr); + + for (u32 segment = 0; segment < segmentTableSize; segment++) { + u32* segmentOffset = (u32*)&segmentTable[SEGMENT_ENTRY_SIZE * segment + SEGMENT_OFFSET]; + + const u32 segmentID = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * segment + SEGMENT_ID]; + switch (segmentID) { + case SEGMENT_ID_DATA: *segmentOffset = dataVaddr; break; + case SEGMENT_ID_BSS: *segmentOffset = bssVaddr; break; + case SEGMENT_ID_TEXT: + case SEGMENT_ID_RODATA: + *segmentOffset += mapVaddr; break; + default: + Helpers::panic("Unknown segment ID"); + } + + std::printf("Rebasing segment table entry %u (ID = %u), addr = %X\n", segment, segmentID, *segmentOffset); + } + + return true; + } + + bool rebaseNamedExportTable(u32 mapVaddr) { + std::puts("Rebasing named export table"); + + const u8* header = (u8*)mem.getReadPointer(croPointer); + + const u32 namedExportAddr = *(u32*)&header[HEADER_NAMED_EXPORT_TABLE_OFFSET]; + const u32 namedExportSize = *(u32*)&header[HEADER_NAMED_EXPORT_TABLE_SIZE]; + + if ((namedExportAddr & 3) != 0) { + Helpers::panic("Unaligned named export table address"); + } + + const u8* namedExportTable = (u8*)mem.getReadPointer(namedExportAddr); + + for (u32 namedExport = 0; namedExport < namedExportSize; namedExport++) { + u32* nameOffset = (u32*)&namedExportTable[NAMED_EXPORT_ENTRY_SIZE * namedExport]; + + assert(*nameOffset != 0); + + *nameOffset += mapVaddr; + + std::printf("Rebasing named export %u, addr = %X\n", namedExport, *nameOffset); + } + + return true; + } + + bool rebaseImportModuleTable(u32 mapVaddr) { + std::puts("Rebasing import module table"); + + const u8* header = (u8*)mem.getReadPointer(croPointer); + + const u32 importModuleTableAddr = *(u32*)&header[HEADER_IMPORT_MODULE_TABLE_OFFSET]; + const u32 importModuleTableSize = *(u32*)&header[HEADER_IMPORT_MODULE_TABLE_SIZE]; + + if ((importModuleTableAddr & 3) != 0) { + Helpers::panic("Unaligned import module table address"); + } + + const u8* importModuleTable = (u8*)mem.getReadPointer(importModuleTableAddr); + + for (u32 importModule = 0; importModule < importModuleTableSize; importModule++) { + u32* nameOffset = (u32*)&importModuleTable[IMPORT_MODULE_TABLE_ENTRY_SIZE * importModule + IMPORT_MODULE_TABLE_NAME_OFFSET]; + + assert(*nameOffset != 0); + + *nameOffset += mapVaddr; + + u32 *indexedOffset = (u32*)&importModuleTable[IMPORT_MODULE_TABLE_ENTRY_SIZE * importModule + IMPORT_MODULE_TABLE_INDEXED_OFFSET]; + + assert(*indexedOffset != 0); + + *indexedOffset += mapVaddr; + + u32 *anonymousOffset = (u32*)&importModuleTable[IMPORT_MODULE_TABLE_ENTRY_SIZE * importModule + IMPORT_MODULE_TABLE_ANONYMOUS_OFFSET]; + + assert(*anonymousOffset != 0); + + *anonymousOffset += mapVaddr; + + std::printf("Rebasing import module %u, name addr = %X, indexed addr = %X, anonymous addr = %X\n", importModule, *nameOffset, *indexedOffset, *anonymousOffset); + } + + return true; + } +}; + void LDRService::reset() {} void LDRService::handleSyncRequest(u32 messagePointer) { @@ -54,7 +277,38 @@ void LDRService::loadCRONew(u32 messagePointer) { const u32 fixLevel = mem.read32(messagePointer + 40); const Handle process = mem.read32(messagePointer + 52); - log("LDR_RO::LoadCRONew (buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); + std::printf("LDR_RO::LoadCRONew (buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); + + // Sanity checks + if (size < CRO_HEADER_SIZE) { + Helpers::panic("CRO too small\n"); + } + + if ((size & mem.pageMask) != 0) { + Helpers::panic("Unaligned CRO size\n"); + } + + if ((croPointer & mem.pageMask) != 0) { + Helpers::panic("Unaligned CRO pointer\n"); + } + + if ((mapVaddr & mem.pageMask) != 0) { + Helpers::panic("Unaligned CRO output vaddr\n"); + } + + // Map CRO to output address + mem.mirrorMapping(mapVaddr, croPointer, size); + + CRO cro(mem, croPointer); + + if (!cro.load()) { + Helpers::panic("Failed to load CRO"); + } + + if (!cro.rebase(mapVaddr, dataVaddr, bssVaddr)) { + Helpers::panic("Failed to rebase CRO"); + } + mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0)); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, size); From 871bcb86959af047f34b9674548f35716f3f11e5 Mon Sep 17 00:00:00 2001 From: Nomi Date: Tue, 5 Sep 2023 22:50:30 +0200 Subject: [PATCH 03/15] Add internal relocations TODO: add linking, make this prettier --- src/core/services/ldr_ro.cpp | 203 ++++++++++++++++++++++++++++++++++- 1 file changed, 200 insertions(+), 3 deletions(-) diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index d719f9bc9..e87dad7b7 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -31,11 +31,15 @@ class CRO { static constexpr u32 HEADER_IMPORT_MODULE_TABLE_SIZE = 0xF4; static constexpr u32 HEADER_IMPORT_PATCHES_OFFSET = 0xF8; static constexpr u32 HEADER_NAMED_IMPORT_TABLE_OFFSET = 0x100; + static constexpr u32 HEADER_NAMED_IMPORT_TABLE_SIZE = 0x104; static constexpr u32 HEADER_INDEXED_IMPORT_TABLE_OFFSET = 0x108; + static constexpr u32 HEADER_INDEXED_IMPORT_TABLE_SIZE = 0x10C; static constexpr u32 HEADER_ANONYMOUS_IMPORT_TABLE_OFFSET = 0x110; + static constexpr u32 HEADER_ANONYMOUS_IMPORT_TABLE_SIZE = 0x114; static constexpr u32 HEADER_IMPORT_STRINGS_OFFSET = 0x118; static constexpr u32 HEADER_STATIC_ANONYMOUS_SYMBOLS_OFFSET = 0x120; static constexpr u32 HEADER_RELOCATION_PATCHES_OFFSET = 0x128; + static constexpr u32 HEADER_RELOCATION_PATCHES_SIZE = 0x12C; static constexpr u32 HEADER_STATIC_ANONYMOUS_PATCHES_OFFSET = 0x130; // Segment table entry offsets @@ -49,6 +53,7 @@ class CRO { static constexpr u32 SEGMENT_ID_DATA = 2; static constexpr u32 SEGMENT_ID_BSS = 3; + // Named export table static constexpr u32 NAMED_EXPORT_ENTRY_SIZE = 8; // Import module table @@ -57,6 +62,19 @@ class CRO { static constexpr u32 IMPORT_MODULE_TABLE_ANONYMOUS_OFFSET = 16; static constexpr u32 IMPORT_MODULE_TABLE_ENTRY_SIZE = 20; + // Named import table + static constexpr u32 NAMED_IMPORT_NAME_OFFSET = 0; + static constexpr u32 NAMED_IMPORT_RELOCATION_OFFSET = 4; + static constexpr u32 NAMED_IMPORT_TABLE_ENTRY_SIZE = 8; + + // Indexed import table + static constexpr u32 INDEXED_IMPORT_RELOCATION_OFFSET = 4; + static constexpr u32 INDEXED_IMPORT_TABLE_ENTRY_SIZE = 8; + + // Anonymous import table + static constexpr u32 ANONYMOUS_IMPORT_RELOCATION_OFFSET = 4; + static constexpr u32 ANONYMOUS_IMPORT_TABLE_ENTRY_SIZE = 8; + Memory &mem; u32 croPointer; @@ -87,9 +105,19 @@ class CRO { // Modify CRO offsets to point at virtual addresses bool rebase(u32 mapVaddr, u32 dataVaddr, u32 bssVaddr) { rebaseHeader(mapVaddr); - rebaseSegmentTable(mapVaddr, dataVaddr, bssVaddr); + + u32 oldDataVaddr = 0; + rebaseSegmentTable(mapVaddr, dataVaddr, bssVaddr, &oldDataVaddr); + + std::printf("Old .data vaddr = %X\n", oldDataVaddr); + rebaseNamedExportTable(mapVaddr); rebaseImportModuleTable(mapVaddr); + rebaseNamedImportTable(mapVaddr); + rebaseIndexedImportTable(mapVaddr); + rebaseAnonymousImportTable(mapVaddr); + + relocateInternal(oldDataVaddr); return true; } @@ -127,7 +155,7 @@ class CRO { return true; } - bool rebaseSegmentTable(u32 mapVaddr, u32 dataVaddr, u32 bssVaddr) { + bool rebaseSegmentTable(u32 mapVaddr, u32 dataVaddr, u32 bssVaddr, u32 *oldDataVaddr) { std::puts("Rebasing segment table"); const u8* header = (u8*)mem.getReadPointer(croPointer); @@ -150,7 +178,8 @@ class CRO { const u32 segmentID = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * segment + SEGMENT_ID]; switch (segmentID) { - case SEGMENT_ID_DATA: *segmentOffset = dataVaddr; break; + case SEGMENT_ID_DATA: + *oldDataVaddr = *segmentOffset + dataVaddr; *segmentOffset = dataVaddr; break; case SEGMENT_ID_BSS: *segmentOffset = bssVaddr; break; case SEGMENT_ID_TEXT: case SEGMENT_ID_RODATA: @@ -230,6 +259,174 @@ class CRO { return true; } + + bool rebaseNamedImportTable(u32 mapVaddr) { + std::puts("Rebasing named import table"); + + const u8* header = (u8*)mem.getReadPointer(croPointer); + + const u32 namedImportTableAddr = *(u32*)&header[HEADER_NAMED_IMPORT_TABLE_OFFSET]; + const u32 namedImportTableSize = *(u32*)&header[HEADER_NAMED_IMPORT_TABLE_SIZE]; + + if ((namedImportTableAddr & 3) != 0) { + Helpers::panic("Unaligned named import table address"); + } + + const u8* namedImportTable = (u8*)mem.getReadPointer(namedImportTableAddr); + + for (u32 namedImport = 0; namedImport < namedImportTableSize; namedImport++) { + u32* nameOffset = (u32*)&namedImportTable[NAMED_IMPORT_TABLE_ENTRY_SIZE * namedImport + NAMED_IMPORT_NAME_OFFSET]; + + assert(*namedImport != 0); + + *nameOffset += mapVaddr; + + u32* relocationOffset = (u32*)&namedImportTable[NAMED_IMPORT_TABLE_ENTRY_SIZE * namedImport + NAMED_IMPORT_RELOCATION_OFFSET]; + + assert(*relocationOffset != 0); + + *relocationOffset += mapVaddr; + + std::printf("Rebasing named import %u, name addr = %X, relocation addr = %X\n", namedImport, *nameOffset, *relocationOffset); + } + + return true; + } + + bool rebaseIndexedImportTable(u32 mapVaddr) { + std::puts("Rebase indexed import table"); + + const u8* header = (u8*)mem.getReadPointer(croPointer); + + const u32 indexedImportTableAddr = *(u32*)&header[HEADER_INDEXED_IMPORT_TABLE_OFFSET]; + const u32 indexedImportTableSize = *(u32*)&header[HEADER_INDEXED_IMPORT_TABLE_SIZE]; + + if ((indexedImportTableAddr & 3) != 0) { + Helpers::panic("Unaligned indexed import table address"); + } + + const u8* indexedImportTable = (u8*)mem.getReadPointer(indexedImportTableAddr); + + for (u32 indexedImport = 0; indexedImport < indexedImportTableSize; indexedImport++) { + u32* relocationOffset = (u32*)&indexedImportTable[INDEXED_IMPORT_TABLE_ENTRY_SIZE * indexedImport + INDEXED_IMPORT_RELOCATION_OFFSET]; + + assert(*relocationOffset != 0); + + *relocationOffset += mapVaddr; + + std::printf("Rebasing indexed import %u, relocation addr = %X\n", indexedImport, *relocationOffset); + } + + return true; + } + + bool rebaseAnonymousImportTable(u32 mapVaddr) { + std::puts("Rebase anonymous import table"); + + const u8* header = (u8*)mem.getReadPointer(croPointer); + + const u32 anonymousImportTableAddr = *(u32*)&header[HEADER_ANONYMOUS_IMPORT_TABLE_OFFSET]; + const u32 anonymousImportTableSize = *(u32*)&header[HEADER_ANONYMOUS_IMPORT_TABLE_SIZE]; + + if ((anonymousImportTableAddr & 3) != 0) { + Helpers::panic("Unaligned anonymous import table address"); + } + + const u8* anonymousImportTable = (u8*)mem.getReadPointer(anonymousImportTableAddr); + + for (u32 anonymousImport = 0; anonymousImport < anonymousImportTableSize; anonymousImport++) { + u32* relocationOffset = (u32*)&anonymousImportTable[ANONYMOUS_IMPORT_TABLE_ENTRY_SIZE * anonymousImport + ANONYMOUS_IMPORT_RELOCATION_OFFSET]; + + assert(*relocationOffset != 0); + + *relocationOffset += mapVaddr; + + std::printf("Rebasing anonymous import %u, relocation addr = %X\n", anonymousImport, *relocationOffset); + } + + return true; + } + + bool relocateInternal(u32 oldDataVaddr) { + std::puts("Relocate internal"); + + const u8* header = (u8*)mem.getReadPointer(croPointer); + + const u32 relocationTableAddr = *(u32*)&header[HEADER_RELOCATION_PATCHES_OFFSET]; + const u32 relocationTableSize = *(u32*)&header[HEADER_RELOCATION_PATCHES_SIZE]; + + const u32 segmentTableAddr = *(u32*)&header[HEADER_SEGMENT_TABLE_OFFSET]; + //const u32 segmentTableSize = *(u32*)&header[HEADER_SEGMENT_TABLE_SIZE]; + + const u8* relocationTable = (u8*)mem.getReadPointer(relocationTableAddr); + const u8* segmentTable = (u8*)mem.getReadPointer(segmentTableAddr); + + for (u32 relocationNum = 0; relocationNum < relocationTableSize; relocationNum++) { + const u32 segmentOffset = *(u32*)&relocationTable[12 * relocationNum]; + const u8 patchType = *(u8*)&relocationTable[12 * relocationNum + 4]; + const u8 index = *(u8*)&relocationTable[12 * relocationNum + 5]; + const u32 addend = *(u32*)&relocationTable[12 * relocationNum + 8]; + + std::printf("Relocation %u, segment offset = %X, patch type = %X, index = %X, addend = %X\n", relocationNum, segmentOffset, patchType, index, addend); + + const u32 segmentAddr = getSegmentAddr(segmentOffset); + + // Get relocation target address + const u32 entryID = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * (segmentOffset & 0xF) + SEGMENT_ID]; + + u32 relocationTarget = segmentAddr; + if (entryID == SEGMENT_ID_DATA) { + // Recompute relocation target for .data + relocationTarget = oldDataVaddr + (segmentOffset >> 4); + } + + if (relocationTarget == 0) { + Helpers::panic("Relocation target is NULL"); + } + + const u32 symbolOffset = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * index + SEGMENT_OFFSET]; + + patchSymbol(relocationTarget, patchType, addend, symbolOffset); + } + + return true; + } + + bool patchSymbol(u32 relocationTarget, u8 patchType, u32 addend, u32 symbolOffset) { + switch (patchType) { + case 2: mem.write32(relocationTarget, symbolOffset + addend); break; + default: Helpers::panic("Unhandled relocation type = %X\n", patchType); + } + + return true; + } + + u32 getSegmentAddr(u32 segmentOffset) { + const u8* header = (u8*)mem.getReadPointer(croPointer); + + // "Decoded" segment tag + const u32 segmentIndex = segmentOffset & 0xF; + const u32 offset = segmentOffset >> 4; + + const u32 segmentTableAddr = *(u32*)&header[HEADER_SEGMENT_TABLE_OFFSET]; + const u32 segmentTableSize = *(u32*)&header[HEADER_SEGMENT_TABLE_SIZE]; + + if (segmentIndex >= segmentTableSize) { + Helpers::panic("bwaaa (invalid segment index = %u, table size = %u)", segmentIndex, segmentTableSize); + } + + const u8* segmentTable = (u8*)mem.getReadPointer(segmentTableAddr); + + // Get segment table entry + const u32 entryOffset = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * segmentIndex]; + const u32 entrySize = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * segmentIndex + 4]; + + if (offset >= entrySize) { + Helpers::panic("bwaaa (invalid offset = %X, entry size = %X)", offset, entrySize); + } + + return entryOffset + offset; + } }; void LDRService::reset() {} From 6b1a56bb829d3bdd0b401e143642a028915949b5 Mon Sep 17 00:00:00 2001 From: Nomi Date: Thu, 7 Sep 2023 18:54:00 +0200 Subject: [PATCH 04/15] Add CRS pointer --- include/services/ldr_ro.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/services/ldr_ro.hpp b/include/services/ldr_ro.hpp index 8e530e1e0..6dde61e4d 100644 --- a/include/services/ldr_ro.hpp +++ b/include/services/ldr_ro.hpp @@ -10,6 +10,8 @@ class LDRService { Memory& mem; MAKE_LOG_FUNCTION(log, ldrLogger) + u32 loadedCRS; + // Service commands void initialize(u32 messagePointer); void loadCRR(u32 messagePointer); From 56fe8c717792bf127dc488a61732fbebad46873f Mon Sep 17 00:00:00 2001 From: Nomi Date: Thu, 7 Sep 2023 18:54:51 +0200 Subject: [PATCH 05/15] Make code prettier --- src/core/services/ldr_ro.cpp | 802 +++++++++++++++++++++++------------ 1 file changed, 541 insertions(+), 261 deletions(-) diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index e87dad7b7..05414b381 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -1,6 +1,9 @@ #include "services/ldr_ro.hpp" #include "ipc.hpp" +#include +#include + namespace LDRCommands { enum : u32 { Initialize = 0x000100C2, @@ -9,107 +12,264 @@ namespace LDRCommands { }; } +namespace CROHeader { + enum : u32 { + ID = 0x080, + NameOffset = 0x084, + NextCRO = 0x088, + PrevCRO = 0x08C, + CodeOffset = 0x0B0, + DataOffset = 0x0B8, + ModuleNameOffset = 0x0C0, + SegmentTableOffset = 0x0C8, + SegmentTableSize = 0x0CC, + NamedExportTableOffset = 0x0D0, + NamedExportTableSize = 0x0D4, + IndexedExportTableOffset = 0x0D8, + IndexedExportTableSize = 0x0DC, + ExportStringTableOffset = 0x0E0, + ExportStringSize = 0x0E4, + ExportTreeOffset = 0x0E8, + ImportModuleTableOffset = 0x0F0, + ImportModuleTableSize = 0x0F4, + ImportPatchTableOffset = 0x0F8, + ImportPatchTableSize = 0x0FC, + NamedImportTableOffset = 0x100, + NamedImportTableSize = 0x104, + IndexedImportTableOffset = 0x108, + IndexedImportTableSize = 0x10C, + AnonymousImportTableOffset = 0x110, + AnonymousImportTableSize = 0x114, + ImportStringTableOffset = 0x118, + ImportStringSize = 0x11C, + StaticAnonymousSymbolTableOffset = 0x120, + StaticAnonymousSymbolTableSize = 0x124, + RelocationPatchTableOffset = 0x128, + RelocationPatchTableSize = 0x12C, + StaticAnonymousPatchTableOffset = 0x130, + StaticAnonymousPatchTableSize = 0x134, + }; +} + +namespace SegmentTable { + enum : u32 { + Offset = 0, + Size = 4, + ID = 8, + }; + + namespace SegmentID { + enum : u32 { + TEXT, RODATA, DATA, BSS, + }; + } +} + +namespace NamedExportTable { + enum : u32 { + NameOffset = 0, + SegmentOffset = 4, + }; +}; + +namespace NamedImportTable { + enum : u32 { + NameOffset = 0, + RelocationOffset = 4, + }; +}; + +namespace IndexedImportTable { + enum : u32 { + Index = 0, + RelocationOffset = 4, + }; +}; + +namespace AnonymousImportTable { + enum : u32 { + SegmentOffset = 0, + RelocationOffset = 4, + }; +}; + +namespace ImportModuleTable { + enum : u32 { + NameOffset = 0, + IndexedOffset = 8, + AnonymousOffset = 16, + }; +}; + +namespace RelocationPatch { + enum : u32 { + SegmentOffset = 0, + PatchType = 4, + IsLastEntry = 5, // For import patches + SegmentIndex = 5, // For relocation patches + IsResolved = 6, + Addend = 8, + }; + + namespace RelocationPatchType { + enum : u32 { + AbsoluteAddress = 2, + }; + }; +}; + +struct CROHeaderEntry { + u32 offset, size; +}; + static constexpr u32 CRO_HEADER_SIZE = 0x138; class CRO { - // CRO header offsets - static constexpr u32 HEADER_ID = 0x80; - static constexpr u32 HEADER_NAME_OFFSET = 0x84; - static constexpr u32 HEADER_NEXT_CRO = 0x88; - static constexpr u32 HEADER_PREV_CRO = 0x8C; - static constexpr u32 HEADER_CODE_OFFSET = 0xB0; - static constexpr u32 HEADER_DATA_OFFSET = 0xB8; - static constexpr u32 HEADER_MODULE_NAME_OFFSET = 0xC0; - static constexpr u32 HEADER_SEGMENT_TABLE_OFFSET = 0xC8; - static constexpr u32 HEADER_SEGMENT_TABLE_SIZE = 0xCC; - static constexpr u32 HEADER_NAMED_EXPORT_TABLE_OFFSET = 0xD0; - static constexpr u32 HEADER_NAMED_EXPORT_TABLE_SIZE = 0xD4; - static constexpr u32 HEADER_INDEXED_EXPORT_TABLE_OFFSET = 0xD8; - static constexpr u32 HEADER_EXPORT_STRINGS_OFFSET = 0xE0; - static constexpr u32 HEADER_EXPORT_TREE_OFFSET = 0xE8; - static constexpr u32 HEADER_IMPORT_MODULE_TABLE_OFFSET = 0xF0; - static constexpr u32 HEADER_IMPORT_MODULE_TABLE_SIZE = 0xF4; - static constexpr u32 HEADER_IMPORT_PATCHES_OFFSET = 0xF8; - static constexpr u32 HEADER_NAMED_IMPORT_TABLE_OFFSET = 0x100; - static constexpr u32 HEADER_NAMED_IMPORT_TABLE_SIZE = 0x104; - static constexpr u32 HEADER_INDEXED_IMPORT_TABLE_OFFSET = 0x108; - static constexpr u32 HEADER_INDEXED_IMPORT_TABLE_SIZE = 0x10C; - static constexpr u32 HEADER_ANONYMOUS_IMPORT_TABLE_OFFSET = 0x110; - static constexpr u32 HEADER_ANONYMOUS_IMPORT_TABLE_SIZE = 0x114; - static constexpr u32 HEADER_IMPORT_STRINGS_OFFSET = 0x118; - static constexpr u32 HEADER_STATIC_ANONYMOUS_SYMBOLS_OFFSET = 0x120; - static constexpr u32 HEADER_RELOCATION_PATCHES_OFFSET = 0x128; - static constexpr u32 HEADER_RELOCATION_PATCHES_SIZE = 0x12C; - static constexpr u32 HEADER_STATIC_ANONYMOUS_PATCHES_OFFSET = 0x130; - - // Segment table entry offsets - static constexpr u32 SEGMENT_OFFSET = 0; - static constexpr u32 SEGMENT_ID = 8; - static constexpr u32 SEGMENT_ENTRY_SIZE = 12; - - // Segment table entry IDs - static constexpr u32 SEGMENT_ID_TEXT = 0; - static constexpr u32 SEGMENT_ID_RODATA = 1; - static constexpr u32 SEGMENT_ID_DATA = 2; - static constexpr u32 SEGMENT_ID_BSS = 3; - - // Named export table - static constexpr u32 NAMED_EXPORT_ENTRY_SIZE = 8; - - // Import module table - static constexpr u32 IMPORT_MODULE_TABLE_NAME_OFFSET = 0; - static constexpr u32 IMPORT_MODULE_TABLE_INDEXED_OFFSET = 8; - static constexpr u32 IMPORT_MODULE_TABLE_ANONYMOUS_OFFSET = 16; - static constexpr u32 IMPORT_MODULE_TABLE_ENTRY_SIZE = 20; - - // Named import table - static constexpr u32 NAMED_IMPORT_NAME_OFFSET = 0; - static constexpr u32 NAMED_IMPORT_RELOCATION_OFFSET = 4; - static constexpr u32 NAMED_IMPORT_TABLE_ENTRY_SIZE = 8; - - // Indexed import table - static constexpr u32 INDEXED_IMPORT_RELOCATION_OFFSET = 4; - static constexpr u32 INDEXED_IMPORT_TABLE_ENTRY_SIZE = 8; - - // Anonymous import table - static constexpr u32 ANONYMOUS_IMPORT_RELOCATION_OFFSET = 4; - static constexpr u32 ANONYMOUS_IMPORT_TABLE_ENTRY_SIZE = 8; - Memory &mem; - u32 croPointer; + u32 croPointer; // Origin address of CRO in RAM + + bool isCRO; // False if CRS public: - CRO(Memory &mem, u32 croPointer) : mem(mem), croPointer(croPointer) {} + CRO(Memory &mem, u32 croPointer, bool isCRO) : mem(mem), croPointer(croPointer), isCRO(isCRO) {} ~CRO() = default; - bool load() { - const u8* header = (u8*)mem.getReadPointer(croPointer); + u32 getNextCRO() { + return mem.read32(croPointer + CROHeader::NextCRO); + } + + u32 getPrevCRO() { + return mem.read32(croPointer + CROHeader::PrevCRO); + } + + void setNextCRO(u32 nextCRO) { + mem.write32(croPointer + CROHeader::NextCRO, nextCRO); + } + + void setPrevCRO(u32 prevCRO) { + mem.write32(croPointer + CROHeader::PrevCRO, prevCRO); + } + + // Returns CRO header offset-size pair + CROHeaderEntry getHeaderEntry(u32 entry) { + return CROHeaderEntry{.offset = mem.read32(croPointer + entry), .size = mem.read32(croPointer + entry + 4)}; + } + + u32 getSegmentAddr(u32 segmentOffset) { + // "Decoded" segment tag + const u32 segmentIndex = segmentOffset & 0xF; + const u32 offset = segmentOffset >> 4; + + const CROHeaderEntry segmentTable = getHeaderEntry(CROHeader::SegmentTableOffset); + + // Safeguard + if (segmentIndex >= segmentTable.size) { + Helpers::panic("Invalid segment index = %u (table size = %u)", segmentIndex, segmentTable.size); + } + // Get segment table entry + const u32 entryOffset = mem.read32(segmentTable.offset + 12 * segmentIndex + SegmentTable::Offset); + const u32 entrySize = mem.read32(segmentTable.offset + 12 * segmentIndex + SegmentTable::Size); + + // Another safeguard + if (offset >= entrySize) { + Helpers::panic("Invalid segment entry offset = %u (entry size = %u)", offset, entrySize); + } + + return entryOffset + offset; + } + + u32 getNamedExportSymbolAddr(const std::string& symbolName) { + // Note: The CRO contains a trie for fast symbol lookup. For simplicity, + // we won't use it and instead look up the symbol in the named export symbol table + + const u32 exportStringSize = mem.read32(croPointer + CROHeader::ExportStringSize); + + const CROHeaderEntry namedExportTable = getHeaderEntry(CROHeader::NamedExportTableOffset); + + for (u32 namedExport = 0; namedExport < namedExportTable.size; namedExport++) { + const u32 nameOffset = mem.read32(namedExportTable.offset + 8 * namedExport + NamedExportTable::NameOffset); + + const std::string exportSymbolName = mem.readString(nameOffset, exportStringSize); + + if (symbolName.compare(exportSymbolName) == 0) { + return getSegmentAddr(mem.read32(namedExportTable.offset + 8 * namedExport + NamedExportTable::SegmentOffset)); + } + } + + return 0; + } + + // Patches one symbol + bool patchSymbol(u32 relocationTarget, u8 patchType, u32 addend, u32 symbolOffset) { + switch (patchType) { + case RelocationPatch::RelocationPatchType::AbsoluteAddress: mem.write32(relocationTarget, symbolOffset + addend); break; + default: Helpers::panic("Unhandled relocation type = %X\n", patchType); + } + + return true; + } + + // Patches symbol batches + bool patchBatch(u32 batchAddr, u32 symbolAddr) { + u32 relocationPatch = batchAddr; + + while (true) { + const u32 segmentOffset = mem.read32(relocationPatch + RelocationPatch::SegmentOffset); + const u8 patchType = mem.read8(relocationPatch + RelocationPatch::PatchType); + const u8 isLastBatch = mem.read8(relocationPatch + RelocationPatch::IsLastEntry); + const u32 addend = mem.read32(relocationPatch + RelocationPatch::Addend); + + const u32 relocationTarget = getSegmentAddr(segmentOffset); + + if (relocationTarget == 0) { + Helpers::panic("Relocation target is NULL"); + } + + patchSymbol(relocationTarget, patchType, addend, symbolAddr); + + if (isLastBatch != 0) { + break; + } + + relocationPatch += 12; + } + + mem.write8(relocationPatch + RelocationPatch::IsResolved, 1); + + return true; + } + + bool load() { // TODO: verify SHA hashes? // Verify CRO magic - if (std::memcmp(&header[HEADER_ID], "CRO0", 4) != 0) { + const std::string magic = mem.readString(croPointer + CROHeader::ID, 4); + if (magic.compare(std::string("CRO0")) != 0) { return false; } // These fields are initially 0, the RO service sets them on load. If non-0, // this CRO has already been loaded - if ((*(u32*)&header[HEADER_NEXT_CRO] != 0) || (*(u32*)&header[HEADER_PREV_CRO] != 0)) { + if ((getNextCRO() != 0) || (getPrevCRO() != 0)) { return false; } return true; } - // Modify CRO offsets to point at virtual addresses - bool rebase(u32 mapVaddr, u32 dataVaddr, u32 bssVaddr) { + // Modifies CRO offsets to point at virtual addresses + bool rebase(u32 loadedCRS, u32 mapVaddr, u32 dataVaddr, u32 bssVaddr) { rebaseHeader(mapVaddr); u32 oldDataVaddr = 0; - rebaseSegmentTable(mapVaddr, dataVaddr, bssVaddr, &oldDataVaddr); - std::printf("Old .data vaddr = %X\n", oldDataVaddr); + // Note: Citra rebases the segment table only if the file is not a CRS. + // Presumably because CRS files don't contain segments? + if (isCRO) { + rebaseSegmentTable(mapVaddr, dataVaddr, bssVaddr, &oldDataVaddr); + } rebaseNamedExportTable(mapVaddr); rebaseImportModuleTable(mapVaddr); @@ -117,319 +277,394 @@ class CRO { rebaseIndexedImportTable(mapVaddr); rebaseAnonymousImportTable(mapVaddr); - relocateInternal(oldDataVaddr); + relocateInternalSymbols(oldDataVaddr); + + // Note: Citra relocates static anonymous symbols and exit symbols only if the file is not a CRS + if (isCRO) { + relocateStaticAnonymousSymbols(); + relocateExitSymbols(loadedCRS); + } return true; } bool rebaseHeader(u32 mapVaddr) { - std::puts("Rebasing CRO header"); - - const u8* header = (u8*)mem.getReadPointer(croPointer); - constexpr u32 headerOffsets[] = { - HEADER_NAME_OFFSET, - HEADER_CODE_OFFSET, - HEADER_DATA_OFFSET, - HEADER_MODULE_NAME_OFFSET, - HEADER_SEGMENT_TABLE_OFFSET, - HEADER_NAMED_EXPORT_TABLE_OFFSET, - HEADER_INDEXED_EXPORT_TABLE_OFFSET, - HEADER_EXPORT_STRINGS_OFFSET, - HEADER_EXPORT_TREE_OFFSET, - HEADER_IMPORT_MODULE_TABLE_OFFSET, - HEADER_IMPORT_PATCHES_OFFSET, - HEADER_NAMED_IMPORT_TABLE_OFFSET, - HEADER_INDEXED_IMPORT_TABLE_OFFSET, - HEADER_ANONYMOUS_IMPORT_TABLE_OFFSET, - HEADER_IMPORT_STRINGS_OFFSET, - HEADER_STATIC_ANONYMOUS_SYMBOLS_OFFSET, // ? - HEADER_RELOCATION_PATCHES_OFFSET, - HEADER_STATIC_ANONYMOUS_PATCHES_OFFSET, // ? + CROHeader::NameOffset, + CROHeader::CodeOffset, + CROHeader::DataOffset, + CROHeader::ModuleNameOffset, + CROHeader::SegmentTableOffset, + CROHeader::NamedExportTableOffset, + CROHeader::IndexedExportTableOffset, + CROHeader::ExportStringTableOffset, + CROHeader::ExportTreeOffset, + CROHeader::ImportModuleTableOffset, + CROHeader::ImportPatchTableOffset, + CROHeader::NamedImportTableOffset, + CROHeader::IndexedImportTableOffset, + CROHeader::AnonymousImportTableOffset, + CROHeader::ImportStringTableOffset, + CROHeader::StaticAnonymousSymbolTableOffset, + CROHeader::RelocationPatchTableOffset, + CROHeader::StaticAnonymousPatchTableOffset, }; - for (auto offset : headerOffsets) { - *(u32*)&header[offset] += mapVaddr; + for (u32 offset : headerOffsets) { + mem.write32(croPointer + offset, mem.read32(croPointer + offset) + mapVaddr); } return true; } bool rebaseSegmentTable(u32 mapVaddr, u32 dataVaddr, u32 bssVaddr, u32 *oldDataVaddr) { - std::puts("Rebasing segment table"); - - const u8* header = (u8*)mem.getReadPointer(croPointer); - - const u32 segmentTableAddr = *(u32*)&header[HEADER_SEGMENT_TABLE_OFFSET]; - const u32 segmentTableSize = *(u32*)&header[HEADER_SEGMENT_TABLE_SIZE]; - - if ((segmentTableAddr & 3) != 0) { - Helpers::panic("Unaligned segment table address"); - } + const CROHeaderEntry segmentTable = getHeaderEntry(CROHeader::SegmentTableOffset); - if (segmentTableSize == 0) { - Helpers::panic("Segment table empty"); - } + for (u32 segment = 0; segment < segmentTable.size; segment++) { + u32 segmentOffset = mem.read32(segmentTable.offset + 12 * segment + SegmentTable::Offset); - const u8* segmentTable = (u8*)mem.getReadPointer(segmentTableAddr); - - for (u32 segment = 0; segment < segmentTableSize; segment++) { - u32* segmentOffset = (u32*)&segmentTable[SEGMENT_ENTRY_SIZE * segment + SEGMENT_OFFSET]; - - const u32 segmentID = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * segment + SEGMENT_ID]; + const u32 segmentID = mem.read32(segmentTable.offset + 12 * segment + SegmentTable::ID); switch (segmentID) { - case SEGMENT_ID_DATA: - *oldDataVaddr = *segmentOffset + dataVaddr; *segmentOffset = dataVaddr; break; - case SEGMENT_ID_BSS: *segmentOffset = bssVaddr; break; - case SEGMENT_ID_TEXT: - case SEGMENT_ID_RODATA: - *segmentOffset += mapVaddr; break; + case SegmentTable::SegmentID::DATA: + *oldDataVaddr = segmentOffset + dataVaddr; segmentOffset = dataVaddr; break; + case SegmentTable::SegmentID::BSS: segmentOffset = bssVaddr; break; + case SegmentTable::SegmentID::TEXT: + case SegmentTable::SegmentID::RODATA: + segmentOffset += mapVaddr; break; default: - Helpers::panic("Unknown segment ID"); + Helpers::panic("Unknown segment ID = %u", segmentID); } - std::printf("Rebasing segment table entry %u (ID = %u), addr = %X\n", segment, segmentID, *segmentOffset); + mem.write32(segmentTable.offset + 12 * segment + SegmentTable::Offset, segmentOffset); } return true; } bool rebaseNamedExportTable(u32 mapVaddr) { - std::puts("Rebasing named export table"); + const CROHeaderEntry namedExportTable = getHeaderEntry(CROHeader::NamedExportTableOffset); - const u8* header = (u8*)mem.getReadPointer(croPointer); + for (u32 namedExport = 0; namedExport < namedExportTable.size; namedExport++) { + u32 nameOffset = mem.read32(namedExportTable.offset + 8 * namedExport); - const u32 namedExportAddr = *(u32*)&header[HEADER_NAMED_EXPORT_TABLE_OFFSET]; - const u32 namedExportSize = *(u32*)&header[HEADER_NAMED_EXPORT_TABLE_SIZE]; + // Note: I don't know if this can happen, better add this safeguard + if (nameOffset == 0) { + Helpers::panic("Named export name offset is NULL"); + } - if ((namedExportAddr & 3) != 0) { - Helpers::panic("Unaligned named export table address"); + mem.write32(namedExportTable.offset + 8 * namedExport, nameOffset + mapVaddr); } - const u8* namedExportTable = (u8*)mem.getReadPointer(namedExportAddr); + return true; + } - for (u32 namedExport = 0; namedExport < namedExportSize; namedExport++) { - u32* nameOffset = (u32*)&namedExportTable[NAMED_EXPORT_ENTRY_SIZE * namedExport]; + bool rebaseImportModuleTable(u32 mapVaddr) { + const CROHeaderEntry importModuleTable = getHeaderEntry(CROHeader::ImportModuleTableOffset); - assert(*nameOffset != 0); + for (u32 importModule = 0; importModule < importModuleTable.size; importModule++) { + u32 nameOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset); - *nameOffset += mapVaddr; + if (nameOffset == 0) { + Helpers::panic("Import module name offset is NULL"); + } - std::printf("Rebasing named export %u, addr = %X\n", namedExport, *nameOffset); - } + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset, nameOffset + mapVaddr); - return true; - } + u32 indexedOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset); - bool rebaseImportModuleTable(u32 mapVaddr) { - std::puts("Rebasing import module table"); + if (indexedOffset == 0) { + Helpers::panic("Import module indexed offset is NULL"); + } - const u8* header = (u8*)mem.getReadPointer(croPointer); + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset, indexedOffset + mapVaddr); + + u32 anonymousOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset); - const u32 importModuleTableAddr = *(u32*)&header[HEADER_IMPORT_MODULE_TABLE_OFFSET]; - const u32 importModuleTableSize = *(u32*)&header[HEADER_IMPORT_MODULE_TABLE_SIZE]; + if (anonymousOffset == 0) { + Helpers::panic("Import module anonymous offset is NULL"); + } - if ((importModuleTableAddr & 3) != 0) { - Helpers::panic("Unaligned import module table address"); + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset, anonymousOffset + mapVaddr); } - const u8* importModuleTable = (u8*)mem.getReadPointer(importModuleTableAddr); + return true; + } - for (u32 importModule = 0; importModule < importModuleTableSize; importModule++) { - u32* nameOffset = (u32*)&importModuleTable[IMPORT_MODULE_TABLE_ENTRY_SIZE * importModule + IMPORT_MODULE_TABLE_NAME_OFFSET]; + bool rebaseNamedImportTable(u32 mapVaddr) { + const CROHeaderEntry namedImportTable = getHeaderEntry(CROHeader::NamedImportTableOffset); - assert(*nameOffset != 0); + for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) { + u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset); - *nameOffset += mapVaddr; + if (nameOffset == 0) { + Helpers::panic("Named import name offset is NULL"); + } - u32 *indexedOffset = (u32*)&importModuleTable[IMPORT_MODULE_TABLE_ENTRY_SIZE * importModule + IMPORT_MODULE_TABLE_INDEXED_OFFSET]; + mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset, nameOffset + mapVaddr); - assert(*indexedOffset != 0); + u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset); - *indexedOffset += mapVaddr; + if (relocationOffset == 0) { + Helpers::panic("Named import relocation offset is NULL"); + } - u32 *anonymousOffset = (u32*)&importModuleTable[IMPORT_MODULE_TABLE_ENTRY_SIZE * importModule + IMPORT_MODULE_TABLE_ANONYMOUS_OFFSET]; + mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset, relocationOffset + mapVaddr); + } - assert(*anonymousOffset != 0); + return true; + } + + bool rebaseIndexedImportTable(u32 mapVaddr) { + const CROHeaderEntry indexedImportTable = getHeaderEntry(CROHeader::IndexedImportTableOffset); + + for (u32 indexedImport = 0; indexedImport < indexedImportTable.size; indexedImport++) { + u32 relocationOffset = mem.read32(indexedImportTable.offset + 8 * indexedImport + IndexedImportTable::RelocationOffset); - *anonymousOffset += mapVaddr; + if (relocationOffset == 0) { + Helpers::panic("Indexed import relocation offset is NULL"); + } - std::printf("Rebasing import module %u, name addr = %X, indexed addr = %X, anonymous addr = %X\n", importModule, *nameOffset, *indexedOffset, *anonymousOffset); + mem.write32(indexedImportTable.offset + 8 * indexedImport + IndexedImportTable::RelocationOffset, relocationOffset + mapVaddr); } return true; } - bool rebaseNamedImportTable(u32 mapVaddr) { - std::puts("Rebasing named import table"); + bool rebaseAnonymousImportTable(u32 mapVaddr) { + const CROHeaderEntry anonymousImportTable = getHeaderEntry(CROHeader::AnonymousImportTableOffset); - const u8* header = (u8*)mem.getReadPointer(croPointer); + for (u32 anonymousImport = 0; anonymousImport < anonymousImportTable.size; anonymousImport++) { + u32 relocationOffset = mem.read32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset); - const u32 namedImportTableAddr = *(u32*)&header[HEADER_NAMED_IMPORT_TABLE_OFFSET]; - const u32 namedImportTableSize = *(u32*)&header[HEADER_NAMED_IMPORT_TABLE_SIZE]; + if (relocationOffset == 0) { + Helpers::panic("Anonymous import relocation offset is NULL"); + } - if ((namedImportTableAddr & 3) != 0) { - Helpers::panic("Unaligned named import table address"); + mem.write32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset + mapVaddr); } - const u8* namedImportTable = (u8*)mem.getReadPointer(namedImportTableAddr); + return true; + } - for (u32 namedImport = 0; namedImport < namedImportTableSize; namedImport++) { - u32* nameOffset = (u32*)&namedImportTable[NAMED_IMPORT_TABLE_ENTRY_SIZE * namedImport + NAMED_IMPORT_NAME_OFFSET]; + bool relocateInternalSymbols(u32 oldDataVaddr) { + const u8* header = (u8*)mem.getReadPointer(croPointer); - assert(*namedImport != 0); + const CROHeaderEntry relocationPatchTable = getHeaderEntry(CROHeader::RelocationPatchTableOffset); + const CROHeaderEntry segmentTable = getHeaderEntry(CROHeader::SegmentTableOffset); - *nameOffset += mapVaddr; + for (u32 relocationPatch = 0; relocationPatch < relocationPatchTable.size; relocationPatch++) { + const u32 segmentOffset = mem.read32(relocationPatchTable.offset + 12 * relocationPatch + RelocationPatch::SegmentOffset); + const u8 patchType = mem.read8(relocationPatchTable.offset + 12 * relocationPatch + RelocationPatch::PatchType); + const u8 segmentIndex = mem.read8(relocationPatchTable.offset + 12 * relocationPatch + RelocationPatch::SegmentIndex); + const u32 addend = mem.read32(relocationPatchTable.offset + 12 * relocationPatch + RelocationPatch::Addend); - u32* relocationOffset = (u32*)&namedImportTable[NAMED_IMPORT_TABLE_ENTRY_SIZE * namedImport + NAMED_IMPORT_RELOCATION_OFFSET]; + const u32 segmentAddr = getSegmentAddr(segmentOffset); - assert(*relocationOffset != 0); + const u32 entryID = mem.read32(segmentTable.offset + 12 * (segmentOffset & 0xF) + SegmentTable::ID); - *relocationOffset += mapVaddr; + u32 relocationTarget = segmentAddr; + if (entryID == SegmentTable::SegmentID::DATA) { + // Recompute relocation target for .data + relocationTarget = oldDataVaddr + (segmentOffset >> 4); + } + + if (relocationTarget == 0) { + Helpers::panic("Relocation target is NULL"); + } - std::printf("Rebasing named import %u, name addr = %X, relocation addr = %X\n", namedImport, *nameOffset, *relocationOffset); + const u32 symbolOffset = mem.read32(segmentTable.offset + 12 * segmentIndex + SegmentTable::Offset); + + patchSymbol(relocationTarget, patchType, addend, symbolOffset); } return true; } - bool rebaseIndexedImportTable(u32 mapVaddr) { - std::puts("Rebase indexed import table"); + bool relocateStaticAnonymousSymbols() { + const CROHeaderEntry staticAnonymousSymbolTable = getHeaderEntry(CROHeader::StaticAnonymousSymbolTableOffset); - const u8* header = (u8*)mem.getReadPointer(croPointer); + for (u32 symbol = 0; symbol < staticAnonymousSymbolTable.size; symbol++) { + Helpers::panic("TODO: relocate static anonymous symbols"); + } - const u32 indexedImportTableAddr = *(u32*)&header[HEADER_INDEXED_IMPORT_TABLE_OFFSET]; - const u32 indexedImportTableSize = *(u32*)&header[HEADER_INDEXED_IMPORT_TABLE_SIZE]; + return true; + } - if ((indexedImportTableAddr & 3) != 0) { - Helpers::panic("Unaligned indexed import table address"); + // Patches "__aeabi_atexit" symbol to "nnroAeabiAtexit_" + bool relocateExitSymbols(u32 loadedCRS) { + if (loadedCRS == 0) { + Helpers::panic("CRS not loaded"); } - const u8* indexedImportTable = (u8*)mem.getReadPointer(indexedImportTableAddr); + const u32 importStringSize = mem.read32(croPointer + CROHeader::ImportStringSize); + + const CROHeaderEntry namedImportTable = getHeaderEntry(CROHeader::NamedImportTableOffset); - for (u32 indexedImport = 0; indexedImport < indexedImportTableSize; indexedImport++) { - u32* relocationOffset = (u32*)&indexedImportTable[INDEXED_IMPORT_TABLE_ENTRY_SIZE * indexedImport + INDEXED_IMPORT_RELOCATION_OFFSET]; + for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) { + const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset); + const u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset); + + const std::string symbolName = mem.readString(nameOffset, importStringSize); - assert(*relocationOffset != 0); + if (symbolName.compare(std::string("__aeabi_atexit")) == 0) { + // Find exit symbol in other CROs + u32 currentCROPointer = loadedCRS; + while (currentCROPointer != 0) { + CRO cro(mem, currentCROPointer, true); - *relocationOffset += mapVaddr; + const u32 exportSymbolAddr = cro.getNamedExportSymbolAddr(std::string("nnroAeabiAtexit_")); + if (exportSymbolAddr != 0) { + patchBatch(relocationOffset, exportSymbolAddr); + + return true; + } - std::printf("Rebasing indexed import %u, relocation addr = %X\n", indexedImport, *relocationOffset); + currentCROPointer = cro.getNextCRO(); + } + } } - return true; + Helpers::warn("Failed to relocate exit symbols"); + + return false; } - bool rebaseAnonymousImportTable(u32 mapVaddr) { - std::puts("Rebase anonymous import table"); + bool importNamedSymbols(u32 loadedCRS) { + if (loadedCRS == 0) { + Helpers::panic("CRS not loaded"); + } - const u8* header = (u8*)mem.getReadPointer(croPointer); + const u32 importStringSize = mem.read32(croPointer + CROHeader::ImportStringSize); - const u32 anonymousImportTableAddr = *(u32*)&header[HEADER_ANONYMOUS_IMPORT_TABLE_OFFSET]; - const u32 anonymousImportTableSize = *(u32*)&header[HEADER_ANONYMOUS_IMPORT_TABLE_SIZE]; + const CROHeaderEntry namedImportTable = getHeaderEntry(CROHeader::NamedImportTableOffset); - if ((anonymousImportTableAddr & 3) != 0) { - Helpers::panic("Unaligned anonymous import table address"); - } + for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) { + const u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset); + + u8 isResolved = mem.read8(relocationOffset + RelocationPatch::IsResolved); + + if (isResolved == 0) { + const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset); + + const std::string symbolName = mem.readString(nameOffset, importStringSize); - const u8* anonymousImportTable = (u8*)mem.getReadPointer(anonymousImportTableAddr); + // Check every loaded CRO for the symbol (the pain) + u32 currentCROPointer = loadedCRS; + while (currentCROPointer != 0) { + CRO cro(mem, currentCROPointer, true); - for (u32 anonymousImport = 0; anonymousImport < anonymousImportTableSize; anonymousImport++) { - u32* relocationOffset = (u32*)&anonymousImportTable[ANONYMOUS_IMPORT_TABLE_ENTRY_SIZE * anonymousImport + ANONYMOUS_IMPORT_RELOCATION_OFFSET]; + const u32 exportSymbolAddr = cro.getNamedExportSymbolAddr(symbolName); + if (exportSymbolAddr != 0) { + patchBatch(relocationOffset, exportSymbolAddr); - assert(*relocationOffset != 0); + isResolved = 1; + break; + } - *relocationOffset += mapVaddr; + currentCROPointer = cro.getNextCRO(); + } - std::printf("Rebasing anonymous import %u, relocation addr = %X\n", anonymousImport, *relocationOffset); + if (isResolved == 0) { + Helpers::panic("Failed to resolve symbol %s", symbolName.c_str()); + } + + mem.write8(relocationOffset + RelocationPatch::IsResolved, 1); + } } return true; } - bool relocateInternal(u32 oldDataVaddr) { - std::puts("Relocate internal"); - - const u8* header = (u8*)mem.getReadPointer(croPointer); + bool importModules(u32 loadedCRS) { + if (loadedCRS == 0) { + Helpers::panic("CRS not loaded"); + } - const u32 relocationTableAddr = *(u32*)&header[HEADER_RELOCATION_PATCHES_OFFSET]; - const u32 relocationTableSize = *(u32*)&header[HEADER_RELOCATION_PATCHES_SIZE]; + const u32 importStringSize = mem.read32(croPointer + CROHeader::ImportStringSize); - const u32 segmentTableAddr = *(u32*)&header[HEADER_SEGMENT_TABLE_OFFSET]; - //const u32 segmentTableSize = *(u32*)&header[HEADER_SEGMENT_TABLE_SIZE]; + const CROHeaderEntry importModuleTable = getHeaderEntry(CROHeader::ImportModuleTableOffset); - const u8* relocationTable = (u8*)mem.getReadPointer(relocationTableAddr); - const u8* segmentTable = (u8*)mem.getReadPointer(segmentTableAddr); + for (u32 importModule = 0; importModule < importModuleTable.size; importModule++) { + Helpers::panic("TODO: import modules"); + } - for (u32 relocationNum = 0; relocationNum < relocationTableSize; relocationNum++) { - const u32 segmentOffset = *(u32*)&relocationTable[12 * relocationNum]; - const u8 patchType = *(u8*)&relocationTable[12 * relocationNum + 4]; - const u8 index = *(u8*)&relocationTable[12 * relocationNum + 5]; - const u32 addend = *(u32*)&relocationTable[12 * relocationNum + 8]; + return true; + } - std::printf("Relocation %u, segment offset = %X, patch type = %X, index = %X, addend = %X\n", relocationNum, segmentOffset, patchType, index, addend); + // Links CROs. Heavily based on Citra's CRO linker + bool link(u32 loadedCRS) { + if (loadedCRS == 0) { + Helpers::panic("CRS not loaded"); + } - const u32 segmentAddr = getSegmentAddr(segmentOffset); + const CROHeaderEntry segmentTable = getHeaderEntry(CROHeader::SegmentTableOffset); - // Get relocation target address - const u32 entryID = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * (segmentOffset & 0xF) + SEGMENT_ID]; + // Fix data segment offset (LoadCRO_New) + // Note: the old LoadCRO does *not* fix .data + u32 dataVaddr; + if (segmentTable.size > 1) { + // Note: ldr:ro assumes that segment index 2 is .data + dataVaddr = mem.read32(segmentTable.offset + 24 + SegmentTable::Offset); - u32 relocationTarget = segmentAddr; - if (entryID == SEGMENT_ID_DATA) { - // Recompute relocation target for .data - relocationTarget = oldDataVaddr + (segmentOffset >> 4); - } + mem.write32(segmentTable.offset + 24 + SegmentTable::Offset, mem.read32(croPointer + CROHeader::DataOffset)); + } - if (relocationTarget == 0) { - Helpers::panic("Relocation target is NULL"); - } + importNamedSymbols(loadedCRS); + importModules(loadedCRS); - const u32 symbolOffset = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * index + SEGMENT_OFFSET]; + // TODO: export symbols to other CROs - patchSymbol(relocationTarget, patchType, addend, symbolOffset); + // Restore .data segment offset (LoadCRO_New) + if (segmentTable.size > 1) { + mem.write32(segmentTable.offset + 24 + SegmentTable::Offset, dataVaddr); } return true; } - bool patchSymbol(u32 relocationTarget, u8 patchType, u32 addend, u32 symbolOffset) { - switch (patchType) { - case 2: mem.write32(relocationTarget, symbolOffset + addend); break; - default: Helpers::panic("Unhandled relocation type = %X\n", patchType); + // Adds CRO to the linked list of loaded CROs + void registerCRO(u32 loadedCRS, bool autoLink) { + if (loadedCRS == 0) { + Helpers::panic("CRS not loaded"); } - return true; - } + CRO crs(mem, loadedCRS, false); + + u32 headAddr = crs.getPrevCRO(); + if (autoLink) { + headAddr = crs.getNextCRO(); + } - u32 getSegmentAddr(u32 segmentOffset) { - const u8* header = (u8*)mem.getReadPointer(croPointer); + if (headAddr == 0) { + // Register first CRO + setPrevCRO(croPointer); - // "Decoded" segment tag - const u32 segmentIndex = segmentOffset & 0xF; - const u32 offset = segmentOffset >> 4; - - const u32 segmentTableAddr = *(u32*)&header[HEADER_SEGMENT_TABLE_OFFSET]; - const u32 segmentTableSize = *(u32*)&header[HEADER_SEGMENT_TABLE_SIZE]; + if (autoLink) { + crs.setNextCRO(croPointer); + } else { + crs.setPrevCRO(croPointer); + } + } else { + // Register new CRO + CRO head(mem, headAddr, true); - if (segmentIndex >= segmentTableSize) { - Helpers::panic("bwaaa (invalid segment index = %u, table size = %u)", segmentIndex, segmentTableSize); - } + if (head.getPrevCRO() == 0) { + Helpers::panic("No tail CRO found"); + } - const u8* segmentTable = (u8*)mem.getReadPointer(segmentTableAddr); + CRO tail(mem, head.getPrevCRO(), true); - // Get segment table entry - const u32 entryOffset = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * segmentIndex]; - const u32 entrySize = *(u32*)&segmentTable[SEGMENT_ENTRY_SIZE * segmentIndex + 4]; + setPrevCRO(tail.croPointer); - if (offset >= entrySize) { - Helpers::panic("bwaaa (invalid offset = %X, entry size = %X)", offset, entrySize); + tail.setNextCRO(croPointer); + head.setPrevCRO(croPointer); } - - return entryOffset + offset; } }; -void LDRService::reset() {} +void LDRService::reset() { + loadedCRS = 0; +} void LDRService::handleSyncRequest(u32 messagePointer) { const u32 command = mem.read32(messagePointer); @@ -448,6 +683,43 @@ void LDRService::initialize(u32 messagePointer) { const Handle process = mem.read32(messagePointer + 20); log("LDR_RO::Initialize (buffer = %08X, size = %08X, vaddr = %08X, process = %X)\n", crsPointer, size, mapVaddr, process); + + // Sanity checks + if (loadedCRS != 0) { + Helpers::panic("CRS already loaded\n"); + } + + if (size < CRO_HEADER_SIZE) { + Helpers::panic("CRS too small\n"); + } + + if ((size & mem.pageMask) != 0) { + Helpers::panic("Unaligned CRS size\n"); + } + + if ((crsPointer & mem.pageMask) != 0) { + Helpers::panic("Unaligned CRS pointer\n"); + } + + if ((mapVaddr & mem.pageMask) != 0) { + Helpers::panic("Unaligned CRS output vaddr\n"); + } + + // Map CRO to output address + mem.mirrorMapping(mapVaddr, crsPointer, size); + + CRO crs(mem, crsPointer, false); + + if (!crs.load()) { + Helpers::panic("Failed to load CRS"); + } + + if (!crs.rebase(0, mapVaddr, 0, 0)) { + Helpers::panic("Failed to rebase CRS"); + } + + loadedCRS = crsPointer; + mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } @@ -474,7 +746,7 @@ void LDRService::loadCRONew(u32 messagePointer) { const u32 fixLevel = mem.read32(messagePointer + 40); const Handle process = mem.read32(messagePointer + 52); - std::printf("LDR_RO::LoadCRONew (buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); + log("LDR_RO::LoadCRONew (buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); // Sanity checks if (size < CRO_HEADER_SIZE) { @@ -496,16 +768,24 @@ void LDRService::loadCRONew(u32 messagePointer) { // Map CRO to output address mem.mirrorMapping(mapVaddr, croPointer, size); - CRO cro(mem, croPointer); + CRO cro(mem, croPointer, true); if (!cro.load()) { Helpers::panic("Failed to load CRO"); } - if (!cro.rebase(mapVaddr, dataVaddr, bssVaddr)) { + if (!cro.rebase(loadedCRS, mapVaddr, dataVaddr, bssVaddr)) { Helpers::panic("Failed to rebase CRO"); } + if (!cro.link(loadedCRS)) { + Helpers::panic("Failed to link CRO"); + } + + cro.registerCRO(loadedCRS, autoLink); + + // TODO: add fixing + mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0)); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, size); From 69a4b218e21b9f46e9f5306d6d861d7056707fe4 Mon Sep 17 00:00:00 2001 From: Nomi Date: Thu, 7 Sep 2023 19:10:11 +0200 Subject: [PATCH 06/15] Skip NULL offsets during rebasing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pokémon XY use NULL offsets, so we can't crash upon encountering them. --- src/core/services/ldr_ro.cpp | 49 ++++++++++++------------------------ 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index 05414b381..c98169788 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -347,12 +347,9 @@ class CRO { for (u32 namedExport = 0; namedExport < namedExportTable.size; namedExport++) { u32 nameOffset = mem.read32(namedExportTable.offset + 8 * namedExport); - // Note: I don't know if this can happen, better add this safeguard - if (nameOffset == 0) { - Helpers::panic("Named export name offset is NULL"); + if (nameOffset != 0) { + mem.write32(namedExportTable.offset + 8 * namedExport, nameOffset + mapVaddr); } - - mem.write32(namedExportTable.offset + 8 * namedExport, nameOffset + mapVaddr); } return true; @@ -364,27 +361,21 @@ class CRO { for (u32 importModule = 0; importModule < importModuleTable.size; importModule++) { u32 nameOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset); - if (nameOffset == 0) { - Helpers::panic("Import module name offset is NULL"); + if (nameOffset != 0) { + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset, nameOffset + mapVaddr); } - mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset, nameOffset + mapVaddr); - u32 indexedOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset); - if (indexedOffset == 0) { - Helpers::panic("Import module indexed offset is NULL"); + if (indexedOffset != 0) { + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset, indexedOffset + mapVaddr); } - mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset, indexedOffset + mapVaddr); - u32 anonymousOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset); - if (anonymousOffset == 0) { - Helpers::panic("Import module anonymous offset is NULL"); + if (anonymousOffset != 0) { + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset, anonymousOffset + mapVaddr); } - - mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset, anonymousOffset + mapVaddr); } return true; @@ -396,19 +387,15 @@ class CRO { for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) { u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset); - if (nameOffset == 0) { - Helpers::panic("Named import name offset is NULL"); + if (nameOffset != 0) { + mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset, nameOffset + mapVaddr); } - mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset, nameOffset + mapVaddr); - u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset); - if (relocationOffset == 0) { - Helpers::panic("Named import relocation offset is NULL"); + if (relocationOffset != 0) { + mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset, relocationOffset + mapVaddr); } - - mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset, relocationOffset + mapVaddr); } return true; @@ -420,11 +407,9 @@ class CRO { for (u32 indexedImport = 0; indexedImport < indexedImportTable.size; indexedImport++) { u32 relocationOffset = mem.read32(indexedImportTable.offset + 8 * indexedImport + IndexedImportTable::RelocationOffset); - if (relocationOffset == 0) { - Helpers::panic("Indexed import relocation offset is NULL"); + if (relocationOffset != 0) { + mem.write32(indexedImportTable.offset + 8 * indexedImport + IndexedImportTable::RelocationOffset, relocationOffset + mapVaddr); } - - mem.write32(indexedImportTable.offset + 8 * indexedImport + IndexedImportTable::RelocationOffset, relocationOffset + mapVaddr); } return true; @@ -436,11 +421,9 @@ class CRO { for (u32 anonymousImport = 0; anonymousImport < anonymousImportTable.size; anonymousImport++) { u32 relocationOffset = mem.read32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset); - if (relocationOffset == 0) { - Helpers::panic("Anonymous import relocation offset is NULL"); + if (relocationOffset != 0) { + mem.write32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset + mapVaddr); } - - mem.write32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset + mapVaddr); } return true; From d452caa976519def82592a7f2699a27d6da99126 Mon Sep 17 00:00:00 2001 From: Nomi Date: Thu, 7 Sep 2023 20:17:55 +0200 Subject: [PATCH 07/15] Add kernel member, stub UnloadCRO --- include/services/ldr_ro.hpp | 6 +++++- src/core/services/ldr_ro.cpp | 18 ++++++++++++++++++ src/core/services/service_manager.cpp | 2 +- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/include/services/ldr_ro.hpp b/include/services/ldr_ro.hpp index 6dde61e4d..828edc336 100644 --- a/include/services/ldr_ro.hpp +++ b/include/services/ldr_ro.hpp @@ -5,9 +5,12 @@ #include "memory.hpp" #include "result/result.hpp" +class Kernel; + class LDRService { Handle handle = KernelHandles::LDR_RO; Memory& mem; + Kernel& kernel; MAKE_LOG_FUNCTION(log, ldrLogger) u32 loadedCRS; @@ -16,9 +19,10 @@ class LDRService { void initialize(u32 messagePointer); void loadCRR(u32 messagePointer); void loadCRONew(u32 messagePointer); + void unloadCRO(u32 messagePointer); public: - LDRService(Memory& mem) : mem(mem) {} + LDRService(Memory& mem, Kernel& kernel) : mem(mem), kernel(kernel) {} void reset(); void handleSyncRequest(u32 messagePointer); }; \ No newline at end of file diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index c98169788..136fd1749 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -1,5 +1,6 @@ #include "services/ldr_ro.hpp" #include "ipc.hpp" +#include "kernel.hpp" #include #include @@ -8,6 +9,7 @@ namespace LDRCommands { enum : u32 { Initialize = 0x000100C2, LoadCRR = 0x00020082, + UnloadCRO = 0x000500C2, LoadCRONew = 0x000902C2, }; } @@ -654,6 +656,7 @@ void LDRService::handleSyncRequest(u32 messagePointer) { switch (command) { case LDRCommands::Initialize: initialize(messagePointer); break; case LDRCommands::LoadCRR: loadCRR(messagePointer); break; + case LDRCommands::UnloadCRO: unloadCRO(messagePointer); break; case LDRCommands::LoadCRONew: loadCRONew(messagePointer); break; default: Helpers::panic("LDR::RO service requested. Command: %08X\n", command); } @@ -701,6 +704,8 @@ void LDRService::initialize(u32 messagePointer) { Helpers::panic("Failed to rebase CRS"); } + //kernel.clearInstructionCache(); + loadedCRS = crsPointer; mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0)); @@ -769,7 +774,20 @@ void LDRService::loadCRONew(u32 messagePointer) { // TODO: add fixing + //kernel.clearInstructionCache(); + mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0)); mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, size); +} + +void LDRService::unloadCRO(u32 messagePointer) { + const u32 mapVaddr = mem.read32(messagePointer + 4); + const u32 croPointer = mem.read32(messagePointer + 12); + const Handle process = mem.read32(messagePointer + 20); + + Helpers::warn("LDR_RO::UnloadCRO (vaddr = %08X, buffer = %08X, process = %X)\n", mapVaddr, croPointer, process); + + mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0)); + mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 9a4027a4f..896003dd8 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -8,7 +8,7 @@ ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), - gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), + gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), news_u(mem), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} static constexpr int MAX_NOTIFICATION_COUNT = 16; From 4cd9c6c77ec564622894ad5f572171ca485c1d81 Mon Sep 17 00:00:00 2001 From: Nomi Date: Thu, 7 Sep 2023 20:38:27 +0200 Subject: [PATCH 08/15] Add old LoadCRO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Silly Pokémon X needs this. --- include/services/ldr_ro.hpp | 2 +- src/core/services/ldr_ro.cpp | 35 +++++++++++++++++++++++------------ 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/include/services/ldr_ro.hpp b/include/services/ldr_ro.hpp index 828edc336..397cc7701 100644 --- a/include/services/ldr_ro.hpp +++ b/include/services/ldr_ro.hpp @@ -17,8 +17,8 @@ class LDRService { // Service commands void initialize(u32 messagePointer); + void loadCRO(u32 messagePointer, bool isNew); void loadCRR(u32 messagePointer); - void loadCRONew(u32 messagePointer); void unloadCRO(u32 messagePointer); public: diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index 136fd1749..d935881b2 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -9,6 +9,7 @@ namespace LDRCommands { enum : u32 { Initialize = 0x000100C2, LoadCRR = 0x00020082, + LoadCRO = 0x000402C2, UnloadCRO = 0x000500C2, LoadCRONew = 0x000902C2, }; @@ -577,7 +578,7 @@ class CRO { } // Links CROs. Heavily based on Citra's CRO linker - bool link(u32 loadedCRS) { + bool link(u32 loadedCRS, bool isNew) { if (loadedCRS == 0) { Helpers::panic("CRS not loaded"); } @@ -587,11 +588,13 @@ class CRO { // Fix data segment offset (LoadCRO_New) // Note: the old LoadCRO does *not* fix .data u32 dataVaddr; - if (segmentTable.size > 1) { - // Note: ldr:ro assumes that segment index 2 is .data - dataVaddr = mem.read32(segmentTable.offset + 24 + SegmentTable::Offset); + if (isNew) { + if (segmentTable.size > 1) { + // Note: ldr:ro assumes that segment index 2 is .data + dataVaddr = mem.read32(segmentTable.offset + 24 + SegmentTable::Offset); - mem.write32(segmentTable.offset + 24 + SegmentTable::Offset, mem.read32(croPointer + CROHeader::DataOffset)); + mem.write32(segmentTable.offset + 24 + SegmentTable::Offset, mem.read32(croPointer + CROHeader::DataOffset)); + } } importNamedSymbols(loadedCRS); @@ -600,8 +603,10 @@ class CRO { // TODO: export symbols to other CROs // Restore .data segment offset (LoadCRO_New) - if (segmentTable.size > 1) { - mem.write32(segmentTable.offset + 24 + SegmentTable::Offset, dataVaddr); + if (isNew) { + if (segmentTable.size > 1) { + mem.write32(segmentTable.offset + 24 + SegmentTable::Offset, dataVaddr); + } } return true; @@ -656,8 +661,9 @@ void LDRService::handleSyncRequest(u32 messagePointer) { switch (command) { case LDRCommands::Initialize: initialize(messagePointer); break; case LDRCommands::LoadCRR: loadCRR(messagePointer); break; + case LDRCommands::LoadCRO: loadCRO(messagePointer, false); break; case LDRCommands::UnloadCRO: unloadCRO(messagePointer); break; - case LDRCommands::LoadCRONew: loadCRONew(messagePointer); break; + case LDRCommands::LoadCRONew: loadCRO(messagePointer, true); break; default: Helpers::panic("LDR::RO service requested. Command: %08X\n", command); } } @@ -722,7 +728,7 @@ void LDRService::loadCRR(u32 messagePointer) { mem.write32(messagePointer + 4, Result::Success); } -void LDRService::loadCRONew(u32 messagePointer) { +void LDRService::loadCRO(u32 messagePointer, bool isNew) { const u32 croPointer = mem.read32(messagePointer + 4); const u32 mapVaddr = mem.read32(messagePointer + 8); const u32 size = mem.read32(messagePointer + 12); @@ -734,7 +740,7 @@ void LDRService::loadCRONew(u32 messagePointer) { const u32 fixLevel = mem.read32(messagePointer + 40); const Handle process = mem.read32(messagePointer + 52); - log("LDR_RO::LoadCRONew (buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); + log("LDR_RO::LoadCRO (isNew = %d, buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", isNew, croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); // Sanity checks if (size < CRO_HEADER_SIZE) { @@ -766,7 +772,7 @@ void LDRService::loadCRONew(u32 messagePointer) { Helpers::panic("Failed to rebase CRO"); } - if (!cro.link(loadedCRS)) { + if (!cro.link(loadedCRS, isNew)) { Helpers::panic("Failed to link CRO"); } @@ -776,7 +782,12 @@ void LDRService::loadCRONew(u32 messagePointer) { //kernel.clearInstructionCache(); - mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0)); + if (isNew) { + mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0)); + } else { + mem.write32(messagePointer, IPC::responseHeader(0x4, 2, 0)); + } + mem.write32(messagePointer + 4, Result::Success); mem.write32(messagePointer + 8, size); } From 60a632b36598b0f2963ddd80de36006366ea6b03 Mon Sep 17 00:00:00 2001 From: Nomi Date: Thu, 7 Sep 2023 23:54:20 +0200 Subject: [PATCH 09/15] Stub fixing, add indexed/anonymous imports TODO: remove debug printfs --- src/core/services/ldr_ro.cpp | 121 +++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 14 deletions(-) diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index d935881b2..ab996fed5 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -75,6 +75,12 @@ namespace NamedExportTable { }; }; +namespace IndexedExportTable { + enum : u32 { + SegmentOffset = 0, + }; +}; + namespace NamedImportTable { enum : u32 { NameOffset = 0, @@ -99,8 +105,10 @@ namespace AnonymousImportTable { namespace ImportModuleTable { enum : u32 { NameOffset = 0, - IndexedOffset = 8, - AnonymousOffset = 16, + IndexedOffset = 4, + IndexedNum = 8, + AnonymousOffset = 12, + AnonymousNum = 16, }; }; @@ -138,6 +146,12 @@ class CRO { CRO(Memory &mem, u32 croPointer, bool isCRO) : mem(mem), croPointer(croPointer), isCRO(isCRO) {} ~CRO() = default; + std::string getModuleName() { + const CROHeaderEntry moduleName = getHeaderEntry(CROHeader::ModuleNameOffset); + + return mem.readString(moduleName.offset, moduleName.size); + } + u32 getNextCRO() { return mem.read32(croPointer + CROHeader::NextCRO); } @@ -262,6 +276,17 @@ class CRO { return true; } + bool fix(u32 fixLevel) { + if (fixLevel != 0) { + mem.write8(croPointer + CROHeader::ID + 0, 'F'); + mem.write8(croPointer + CROHeader::ID + 1, 'I'); + mem.write8(croPointer + CROHeader::ID + 2, 'X'); + mem.write8(croPointer + CROHeader::ID + 3, 'D'); + } + + return true; + } + // Modifies CRO offsets to point at virtual addresses bool rebase(u32 loadedCRS, u32 mapVaddr, u32 dataVaddr, u32 bssVaddr) { rebaseHeader(mapVaddr); @@ -280,11 +305,14 @@ class CRO { rebaseIndexedImportTable(mapVaddr); rebaseAnonymousImportTable(mapVaddr); - relocateInternalSymbols(oldDataVaddr); - // Note: Citra relocates static anonymous symbols and exit symbols only if the file is not a CRS if (isCRO) { relocateStaticAnonymousSymbols(); + } + + relocateInternalSymbols(oldDataVaddr); + + if (isCRO) { relocateExitSymbols(loadedCRS); } @@ -571,7 +599,71 @@ class CRO { const CROHeaderEntry importModuleTable = getHeaderEntry(CROHeader::ImportModuleTableOffset); for (u32 importModule = 0; importModule < importModuleTable.size; importModule++) { - Helpers::panic("TODO: import modules"); + const u32 nameOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset); + + const std::string importModuleName = mem.readString(nameOffset, importStringSize); + + std::printf("Importing module \"%s\"\n", importModuleName.c_str()); + + // Find import module + u32 currentCROPointer = loadedCRS; + while (currentCROPointer != 0) { + CRO cro(mem, currentCROPointer, true); + + if (importModuleName.compare(cro.getModuleName()) == 0) { + std::printf("Found module \"%s\"\n", importModuleName.c_str()); + + // Import indexed symbols + const CROHeaderEntry indexedExportTable = cro.getHeaderEntry(CROHeader::IndexedExportTableOffset); + + const u32 indexedOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset); + const u32 indexedNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedNum); + + std::printf("Importing indexed symbols (num = %u, offset = %X)\n", indexedNum, indexedOffset); + + for (u32 indexedImport = 0; indexedImport < indexedNum; indexedImport++) { + if (indexedOffset == 0) { + Helpers::panic("Indexed symbol offset is NULL"); + } + + const u32 importIndex = mem.read32(indexedOffset + 8 * indexedImport + IndexedImportTable::Index); + + const u32 segmentOffset = mem.read32(indexedExportTable.offset + 4 * importIndex + IndexedExportTable::SegmentOffset); + const u32 relocationOffset = mem.read32(indexedOffset + 8 * indexedImport + IndexedImportTable::RelocationOffset); + + std::printf("Indexed import %u, index = %u, segment offset = %X, relocation offset = %X\n", indexedImport, importIndex, segmentOffset, relocationOffset); + + patchBatch(relocationOffset, cro.getSegmentAddr(segmentOffset)); + } + + // Import anonymous symbols + const u32 anonymousOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset); + const u32 anonymousNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousNum); + + std::printf("Importing anonymous symbols (num = %u, offset = %X)\n", anonymousNum, anonymousOffset); + + for (u32 anonymousImport = 0; anonymousImport < anonymousNum; anonymousImport++) { + if (anonymousOffset == 0) { + Helpers::panic("Anonymous symbol offset is NULL"); + } + + const u32 segmentOffset = mem.read32(anonymousOffset + 8 * anonymousImport + AnonymousImportTable::SegmentOffset); + const u32 relocationOffset = mem.read32(anonymousOffset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset); + + std::printf("Anonymous import %u, segment offset = %X, relocation offset = %X\n", anonymousImport, segmentOffset, relocationOffset); + + patchBatch(relocationOffset, cro.getSegmentAddr(segmentOffset)); + } + + break; + } + + currentCROPointer = cro.getNextCRO(); + } + + if (currentCROPointer == 0) { + Helpers::warn("Unable to find import module %s\n", importModuleName.c_str()); + } } return true; @@ -685,15 +777,15 @@ void LDRService::initialize(u32 messagePointer) { Helpers::panic("CRS too small\n"); } - if ((size & mem.pageMask) != 0) { + if (!mem.isAligned(size)) { Helpers::panic("Unaligned CRS size\n"); } - if ((crsPointer & mem.pageMask) != 0) { + if (!mem.isAligned(crsPointer)) { Helpers::panic("Unaligned CRS pointer\n"); } - if ((mapVaddr & mem.pageMask) != 0) { + if (!mem.isAligned(mapVaddr)) { Helpers::panic("Unaligned CRS output vaddr\n"); } @@ -710,7 +802,7 @@ void LDRService::initialize(u32 messagePointer) { Helpers::panic("Failed to rebase CRS"); } - //kernel.clearInstructionCache(); + kernel.clearInstructionCache(); loadedCRS = crsPointer; @@ -740,22 +832,22 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) { const u32 fixLevel = mem.read32(messagePointer + 40); const Handle process = mem.read32(messagePointer + 52); - log("LDR_RO::LoadCRO (isNew = %d, buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", isNew, croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); + std::printf("LDR_RO::LoadCRO (isNew = %d, buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", isNew, croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); // Sanity checks if (size < CRO_HEADER_SIZE) { Helpers::panic("CRO too small\n"); } - if ((size & mem.pageMask) != 0) { + if (!mem.isAligned(size)) { Helpers::panic("Unaligned CRO size\n"); } - if ((croPointer & mem.pageMask) != 0) { + if (!mem.isAligned(croPointer)) { Helpers::panic("Unaligned CRO pointer\n"); } - if ((mapVaddr & mem.pageMask) != 0) { + if (!mem.isAligned(mapVaddr)) { Helpers::panic("Unaligned CRO output vaddr\n"); } @@ -779,8 +871,9 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) { cro.registerCRO(loadedCRS, autoLink); // TODO: add fixing + cro.fix(fixLevel); - //kernel.clearInstructionCache(); + kernel.clearInstructionCache(); if (isNew) { mem.write32(messagePointer, IPC::responseHeader(0x9, 2, 0)); From 3fc9bca6a5376ab039e3cd34b8fdb08ceff55cfa Mon Sep 17 00:00:00 2001 From: Nomi Date: Sat, 9 Sep 2023 00:25:10 +0200 Subject: [PATCH 10/15] Add LinkCRO --- include/services/ldr_ro.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/services/ldr_ro.hpp b/include/services/ldr_ro.hpp index 397cc7701..71516547b 100644 --- a/include/services/ldr_ro.hpp +++ b/include/services/ldr_ro.hpp @@ -17,6 +17,7 @@ class LDRService { // Service commands void initialize(u32 messagePointer); + void linkCRO(u32 messagePointer); void loadCRO(u32 messagePointer, bool isNew); void loadCRR(u32 messagePointer); void unloadCRO(u32 messagePointer); From b9e7d5db3c1806f234efa1c9af643b52736431a0 Mon Sep 17 00:00:00 2001 From: Nomi Date: Sat, 9 Sep 2023 00:25:33 +0200 Subject: [PATCH 11/15] Many bug fixes, CRO unloading, proper symbol im/exports --- src/core/services/ldr_ro.cpp | 626 ++++++++++++++++++++++++++++++++--- 1 file changed, 573 insertions(+), 53 deletions(-) diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index ab996fed5..a963489a5 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -11,6 +11,7 @@ namespace LDRCommands { LoadCRR = 0x00020082, LoadCRO = 0x000402C2, UnloadCRO = 0x000500C2, + LinkCRO = 0x00060042, LoadCRONew = 0x000902C2, }; } @@ -23,6 +24,7 @@ namespace CROHeader { PrevCRO = 0x08C, CodeOffset = 0x0B0, DataOffset = 0x0B8, + OnUnresolved = 0x0AC, ModuleNameOffset = 0x0C0, SegmentTableOffset = 0x0C8, SegmentTableSize = 0x0CC, @@ -139,11 +141,12 @@ class CRO { Memory &mem; u32 croPointer; // Origin address of CRO in RAM + u32 oldDataSegmentOffset; bool isCRO; // False if CRS public: - CRO(Memory &mem, u32 croPointer, bool isCRO) : mem(mem), croPointer(croPointer), isCRO(isCRO) {} + CRO(Memory &mem, u32 croPointer, bool isCRO) : mem(mem), croPointer(croPointer), oldDataSegmentOffset(0), isCRO(isCRO) {} ~CRO() = default; std::string getModuleName() { @@ -168,6 +171,23 @@ class CRO { mem.write32(croPointer + CROHeader::PrevCRO, prevCRO); } + void write32(u32 addr, u32 value) { + // Note: some games export symbols to the static module, which doesn't contain any segments. + // Instead, its segments point to ROM segments. We need this special write handler for writes to .text, which + // can't be accessed via mem.write32() + auto writePointer = mem.getWritePointer(addr); + if (writePointer) { + *(u32*)writePointer = value; + } else { + auto readPointer = mem.getReadPointer(addr); + if (readPointer) { + *(u32*)readPointer = value; + } else { + Helpers::panic("LDR_RO write to invalid address = %X\n", addr); + } + } + } + // Returns CRO header offset-size pair CROHeaderEntry getHeaderEntry(u32 entry) { return CROHeaderEntry{.offset = mem.read32(croPointer + entry), .size = mem.read32(croPointer + entry + 4)}; @@ -180,23 +200,25 @@ class CRO { const CROHeaderEntry segmentTable = getHeaderEntry(CROHeader::SegmentTableOffset); - // Safeguard if (segmentIndex >= segmentTable.size) { - Helpers::panic("Invalid segment index = %u (table size = %u)", segmentIndex, segmentTable.size); + return 0; } // Get segment table entry const u32 entryOffset = mem.read32(segmentTable.offset + 12 * segmentIndex + SegmentTable::Offset); const u32 entrySize = mem.read32(segmentTable.offset + 12 * segmentIndex + SegmentTable::Size); - // Another safeguard if (offset >= entrySize) { - Helpers::panic("Invalid segment entry offset = %u (entry size = %u)", offset, entrySize); + return 0; } return entryOffset + offset; } + u32 getOnUnresolvedAddr() { + return getSegmentAddr(mem.read32(croPointer + CROHeader::OnUnresolved)); + } + u32 getNamedExportSymbolAddr(const std::string& symbolName) { // Note: The CRO contains a trie for fast symbol lookup. For simplicity, // we won't use it and instead look up the symbol in the named export symbol table @@ -221,7 +243,7 @@ class CRO { // Patches one symbol bool patchSymbol(u32 relocationTarget, u8 patchType, u32 addend, u32 symbolOffset) { switch (patchType) { - case RelocationPatch::RelocationPatchType::AbsoluteAddress: mem.write32(relocationTarget, symbolOffset + addend); break; + case RelocationPatch::RelocationPatchType::AbsoluteAddress: write32(relocationTarget, symbolOffset + addend); break; default: Helpers::panic("Unhandled relocation type = %X\n", patchType); } @@ -229,7 +251,7 @@ class CRO { } // Patches symbol batches - bool patchBatch(u32 batchAddr, u32 symbolAddr) { + bool patchBatch(u32 batchAddr, u32 symbolAddr, bool makeUnresolved = false) { u32 relocationPatch = batchAddr; while (true) { @@ -244,7 +266,11 @@ class CRO { Helpers::panic("Relocation target is NULL"); } - patchSymbol(relocationTarget, patchType, addend, symbolAddr); + if (makeUnresolved) { + write32(relocationTarget, symbolAddr); + } else { + patchSymbol(relocationTarget, patchType, addend, symbolAddr); + } if (isLastBatch != 0) { break; @@ -253,7 +279,11 @@ class CRO { relocationPatch += 12; } - mem.write8(relocationPatch + RelocationPatch::IsResolved, 1); + if (makeUnresolved) { + mem.write8(relocationPatch + RelocationPatch::IsResolved, 0); + } else { + mem.write8(relocationPatch + RelocationPatch::IsResolved, 1); + } return true; } @@ -288,38 +318,84 @@ class CRO { } // Modifies CRO offsets to point at virtual addresses - bool rebase(u32 loadedCRS, u32 mapVaddr, u32 dataVaddr, u32 bssVaddr) { - rebaseHeader(mapVaddr); + bool rebase(u32 loadedCRS, u32 dataVaddr, u32 bssVaddr) { + rebaseHeader(); u32 oldDataVaddr = 0; // Note: Citra rebases the segment table only if the file is not a CRS. // Presumably because CRS files don't contain segments? if (isCRO) { - rebaseSegmentTable(mapVaddr, dataVaddr, bssVaddr, &oldDataVaddr); + rebaseSegmentTable(dataVaddr, bssVaddr, &oldDataVaddr); } - rebaseNamedExportTable(mapVaddr); - rebaseImportModuleTable(mapVaddr); - rebaseNamedImportTable(mapVaddr); - rebaseIndexedImportTable(mapVaddr); - rebaseAnonymousImportTable(mapVaddr); + rebaseNamedExportTable(); + rebaseImportModuleTable(); + rebaseNamedImportTable(); + rebaseIndexedImportTable(); + rebaseAnonymousImportTable(); // Note: Citra relocates static anonymous symbols and exit symbols only if the file is not a CRS if (isCRO) { relocateStaticAnonymousSymbols(); + relocateInternalSymbols(oldDataVaddr); + relocateExitSymbols(loadedCRS); } - relocateInternalSymbols(oldDataVaddr); + return true; + } + + bool unrebase() { + unrebaseAnonymousImportTable(); + unrebaseIndexedImportTable(); + unrebaseNamedImportTable(); + unrebaseImportModuleTable(); + unrebaseNamedExportTable(); + // Note: Citra unrebases the segment table only if the file is not a CRS. + // Presumably because CRS files don't contain segments? if (isCRO) { - relocateExitSymbols(loadedCRS); + unrebaseSegmentTable(); + } + + unrebaseHeader(); + + setNextCRO(0); + setPrevCRO(0); + + return true; + } + + bool rebaseHeader() { + constexpr u32 headerOffsets[] = { + CROHeader::NameOffset, + CROHeader::CodeOffset, + CROHeader::DataOffset, + CROHeader::ModuleNameOffset, + CROHeader::SegmentTableOffset, + CROHeader::NamedExportTableOffset, + CROHeader::IndexedExportTableOffset, + CROHeader::ExportStringTableOffset, + CROHeader::ExportTreeOffset, + CROHeader::ImportModuleTableOffset, + CROHeader::ImportPatchTableOffset, + CROHeader::NamedImportTableOffset, + CROHeader::IndexedImportTableOffset, + CROHeader::AnonymousImportTableOffset, + CROHeader::ImportStringTableOffset, + CROHeader::StaticAnonymousSymbolTableOffset, + CROHeader::RelocationPatchTableOffset, + CROHeader::StaticAnonymousPatchTableOffset, + }; + + for (u32 offset : headerOffsets) { + mem.write32(croPointer + offset, mem.read32(croPointer + offset) + croPointer); } return true; } - bool rebaseHeader(u32 mapVaddr) { + bool unrebaseHeader() { constexpr u32 headerOffsets[] = { CROHeader::NameOffset, CROHeader::CodeOffset, @@ -342,13 +418,13 @@ class CRO { }; for (u32 offset : headerOffsets) { - mem.write32(croPointer + offset, mem.read32(croPointer + offset) + mapVaddr); + mem.write32(croPointer + offset, mem.read32(croPointer + offset) - croPointer); } return true; } - bool rebaseSegmentTable(u32 mapVaddr, u32 dataVaddr, u32 bssVaddr, u32 *oldDataVaddr) { + bool rebaseSegmentTable(u32 dataVaddr, u32 bssVaddr, u32 *oldDataVaddr) { const CROHeaderEntry segmentTable = getHeaderEntry(CROHeader::SegmentTableOffset); for (u32 segment = 0; segment < segmentTable.size; segment++) { @@ -357,11 +433,34 @@ class CRO { const u32 segmentID = mem.read32(segmentTable.offset + 12 * segment + SegmentTable::ID); switch (segmentID) { case SegmentTable::SegmentID::DATA: - *oldDataVaddr = segmentOffset + dataVaddr; segmentOffset = dataVaddr; break; + *oldDataVaddr = segmentOffset + dataVaddr; oldDataSegmentOffset = segmentOffset; segmentOffset = dataVaddr; break; case SegmentTable::SegmentID::BSS: segmentOffset = bssVaddr; break; case SegmentTable::SegmentID::TEXT: case SegmentTable::SegmentID::RODATA: - segmentOffset += mapVaddr; break; + if (segmentOffset != 0) segmentOffset += croPointer; break; + default: + Helpers::panic("Unknown segment ID = %u", segmentID); + } + + mem.write32(segmentTable.offset + 12 * segment + SegmentTable::Offset, segmentOffset); + } + + return true; + } + + bool unrebaseSegmentTable() { + const CROHeaderEntry segmentTable = getHeaderEntry(CROHeader::SegmentTableOffset); + + for (u32 segment = 0; segment < segmentTable.size; segment++) { + u32 segmentOffset = mem.read32(segmentTable.offset + 12 * segment + SegmentTable::Offset); + + const u32 segmentID = mem.read32(segmentTable.offset + 12 * segment + SegmentTable::ID); + switch (segmentID) { + case SegmentTable::SegmentID::DATA: segmentOffset = oldDataSegmentOffset; break; + case SegmentTable::SegmentID::BSS: segmentOffset = 0; break; + case SegmentTable::SegmentID::TEXT: + case SegmentTable::SegmentID::RODATA: + if (segmentOffset != 0) segmentOffset -= croPointer; break; default: Helpers::panic("Unknown segment ID = %u", segmentID); } @@ -372,88 +471,176 @@ class CRO { return true; } - bool rebaseNamedExportTable(u32 mapVaddr) { + bool rebaseNamedExportTable() { + const CROHeaderEntry namedExportTable = getHeaderEntry(CROHeader::NamedExportTableOffset); + + for (u32 namedExport = 0; namedExport < namedExportTable.size; namedExport++) { + u32 nameOffset = mem.read32(namedExportTable.offset + 8 * namedExport); + + if (nameOffset != 0) { + mem.write32(namedExportTable.offset + 8 * namedExport, nameOffset + croPointer); + } + } + + return true; + } + + bool unrebaseNamedExportTable() { const CROHeaderEntry namedExportTable = getHeaderEntry(CROHeader::NamedExportTableOffset); for (u32 namedExport = 0; namedExport < namedExportTable.size; namedExport++) { u32 nameOffset = mem.read32(namedExportTable.offset + 8 * namedExport); if (nameOffset != 0) { - mem.write32(namedExportTable.offset + 8 * namedExport, nameOffset + mapVaddr); + mem.write32(namedExportTable.offset + 8 * namedExport, nameOffset - croPointer); + } + } + + return true; + } + + bool rebaseImportModuleTable() { + const CROHeaderEntry importModuleTable = getHeaderEntry(CROHeader::ImportModuleTableOffset); + + for (u32 importModule = 0; importModule < importModuleTable.size; importModule++) { + u32 nameOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset); + + if (nameOffset != 0) { + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset, nameOffset + croPointer); + } + + u32 indexedOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset); + + if (indexedOffset != 0) { + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset, indexedOffset + croPointer); + } + + u32 anonymousOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset); + + if (anonymousOffset != 0) { + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset, anonymousOffset + croPointer); } } return true; } - bool rebaseImportModuleTable(u32 mapVaddr) { + bool unrebaseImportModuleTable() { const CROHeaderEntry importModuleTable = getHeaderEntry(CROHeader::ImportModuleTableOffset); for (u32 importModule = 0; importModule < importModuleTable.size; importModule++) { u32 nameOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset); if (nameOffset != 0) { - mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset, nameOffset + mapVaddr); + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset, nameOffset - croPointer); } u32 indexedOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset); if (indexedOffset != 0) { - mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset, indexedOffset + mapVaddr); + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset, indexedOffset - croPointer); } u32 anonymousOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset); if (anonymousOffset != 0) { - mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset, anonymousOffset + mapVaddr); + mem.write32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset, anonymousOffset - croPointer); } } return true; } - bool rebaseNamedImportTable(u32 mapVaddr) { + bool rebaseNamedImportTable() { const CROHeaderEntry namedImportTable = getHeaderEntry(CROHeader::NamedImportTableOffset); for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) { u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset); if (nameOffset != 0) { - mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset, nameOffset + mapVaddr); + mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset, nameOffset + croPointer); } u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset); if (relocationOffset != 0) { - mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset, relocationOffset + mapVaddr); + mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset, relocationOffset + croPointer); + } + } + + return true; + } + + bool unrebaseNamedImportTable() { + const CROHeaderEntry namedImportTable = getHeaderEntry(CROHeader::NamedImportTableOffset); + + for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) { + u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset); + + if (nameOffset != 0) { + mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset, nameOffset - croPointer); + } + + u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset); + + if (relocationOffset != 0) { + mem.write32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset, relocationOffset - croPointer); + } + } + + return true; + } + + bool rebaseIndexedImportTable() { + const CROHeaderEntry indexedImportTable = getHeaderEntry(CROHeader::IndexedImportTableOffset); + + for (u32 indexedImport = 0; indexedImport < indexedImportTable.size; indexedImport++) { + u32 relocationOffset = mem.read32(indexedImportTable.offset + 8 * indexedImport + IndexedImportTable::RelocationOffset); + + if (relocationOffset != 0) { + mem.write32(indexedImportTable.offset + 8 * indexedImport + IndexedImportTable::RelocationOffset, relocationOffset + croPointer); } } return true; } - bool rebaseIndexedImportTable(u32 mapVaddr) { + bool unrebaseIndexedImportTable() { const CROHeaderEntry indexedImportTable = getHeaderEntry(CROHeader::IndexedImportTableOffset); for (u32 indexedImport = 0; indexedImport < indexedImportTable.size; indexedImport++) { u32 relocationOffset = mem.read32(indexedImportTable.offset + 8 * indexedImport + IndexedImportTable::RelocationOffset); if (relocationOffset != 0) { - mem.write32(indexedImportTable.offset + 8 * indexedImport + IndexedImportTable::RelocationOffset, relocationOffset + mapVaddr); + mem.write32(indexedImportTable.offset + 8 * indexedImport + IndexedImportTable::RelocationOffset, relocationOffset - croPointer); + } + } + + return true; + } + + bool rebaseAnonymousImportTable() { + const CROHeaderEntry anonymousImportTable = getHeaderEntry(CROHeader::AnonymousImportTableOffset); + + for (u32 anonymousImport = 0; anonymousImport < anonymousImportTable.size; anonymousImport++) { + u32 relocationOffset = mem.read32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset); + + if (relocationOffset != 0) { + mem.write32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset + croPointer); } } return true; } - bool rebaseAnonymousImportTable(u32 mapVaddr) { + bool unrebaseAnonymousImportTable() { const CROHeaderEntry anonymousImportTable = getHeaderEntry(CROHeader::AnonymousImportTableOffset); for (u32 anonymousImport = 0; anonymousImport < anonymousImportTable.size; anonymousImport++) { u32 relocationOffset = mem.read32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset); if (relocationOffset != 0) { - mem.write32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset + mapVaddr); + mem.write32(anonymousImportTable.offset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset, relocationOffset - croPointer); } } @@ -581,14 +768,26 @@ class CRO { if (isResolved == 0) { Helpers::panic("Failed to resolve symbol %s", symbolName.c_str()); } - - mem.write8(relocationOffset + RelocationPatch::IsResolved, 1); } } return true; } + bool clearNamedSymbols() { + const u32 onUnresolvedAddr = getOnUnresolvedAddr(); + + const CROHeaderEntry namedImportTable = getHeaderEntry(CROHeader::NamedImportTableOffset); + + for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) { + const u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset); + + patchBatch(relocationOffset, onUnresolvedAddr, true); + } + + return true; + } + bool importModules(u32 loadedCRS) { if (loadedCRS == 0) { Helpers::panic("CRS not loaded"); @@ -610,6 +809,8 @@ class CRO { while (currentCROPointer != 0) { CRO cro(mem, currentCROPointer, true); + std::printf("Exporting module name \"%s\"\n", cro.getModuleName().c_str()); + if (importModuleName.compare(cro.getModuleName()) == 0) { std::printf("Found module \"%s\"\n", importModuleName.c_str()); @@ -668,6 +869,207 @@ class CRO { return true; } + + bool clearModules() { + const u32 onUnresolvedAddr = getOnUnresolvedAddr(); + + const CROHeaderEntry importModuleTable = getHeaderEntry(CROHeader::ImportModuleTableOffset); + + for (u32 importModule = 0; importModule < importModuleTable.size; importModule++) { + // Clear indexed symbol imports + const u32 indexedOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset); + const u32 indexedNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedNum); + + for (u32 indexedImport = 0; indexedImport < indexedNum; indexedImport++) { + if (indexedOffset == 0) { + Helpers::panic("Indexed symbol offset is NULL"); + } + + const u32 relocationOffset = mem.read32(indexedOffset + 8 * indexedImport + IndexedImportTable::RelocationOffset); + + patchBatch(relocationOffset, onUnresolvedAddr, true); + } + + // Clear anonymous import symbols + const u32 anonymousOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset); + const u32 anonymousNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousNum); + + for (u32 anonymousImport = 0; anonymousImport < anonymousNum; anonymousImport++) { + if (anonymousOffset == 0) { + Helpers::panic("Anonymous symbol offset is NULL"); + } + + const u32 relocationOffset = mem.read32(anonymousOffset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset); + + patchBatch(relocationOffset, onUnresolvedAddr, true); + } + } + + return true; + } + + bool exportSymbols(u32 loadedCRS) { + if (loadedCRS == 0) { + Helpers::panic("CRS not loaded"); + } + + u32 currentCROPointer = loadedCRS; + while (currentCROPointer != 0) { + CRO cro(mem, currentCROPointer, true); + + // Export named symbols + const u32 importStringSize = mem.read32(currentCROPointer + CROHeader::ImportStringSize); + + const CROHeaderEntry namedImportTable = cro.getHeaderEntry(CROHeader::NamedImportTableOffset); + + for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) { + const u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset); + + u8 isResolved = mem.read8(relocationOffset + RelocationPatch::IsResolved); + + if (isResolved == 0) { + const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset); + + const std::string symbolName = mem.readString(nameOffset, importStringSize); + + // Check our current CRO for the symbol + const u32 exportSymbolAddr = getNamedExportSymbolAddr(symbolName); + if (exportSymbolAddr == 0) { + continue; + } + + std::printf("Found symbol \"%s\" @ %X, relocation offset = %X\n", symbolName.c_str(), exportSymbolAddr, relocationOffset); + + cro.patchBatch(relocationOffset, exportSymbolAddr); + } + } + + // Export indexed and anonymous symbols + const CROHeaderEntry importModuleTable = cro.getHeaderEntry(CROHeader::ImportModuleTableOffset); + + for (u32 importModule = 0; importModule < importModuleTable.size; importModule++) { + // Check if other CROs request module imports from this CRO + const u32 nameOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset); + + const std::string moduleName = mem.readString(nameOffset, importStringSize); + + if (moduleName.compare(getModuleName()) != 0) { + continue; + } + + std::printf("Exporting module \"%s\"\n", moduleName.c_str()); + + // Export indexed symbols + const u32 indexedOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset); + const u32 indexedNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedNum); + + for (u32 indexedImport = 0; indexedImport < indexedNum; indexedImport++) { + Helpers::panic("TODO: indexed exports"); + } + + // Export anonymous symbols + const u32 anonymousOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset); + const u32 anonymousNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousNum); + + for (u32 anonymousImport = 0; anonymousImport < anonymousNum; anonymousImport++) { + if (anonymousOffset == 0) { + Helpers::panic("Anonymous symbol offset is NULL"); + } + + const u32 segmentOffset = mem.read32(anonymousOffset + 8 * anonymousImport + AnonymousImportTable::SegmentOffset); + const u32 relocationOffset = mem.read32(anonymousOffset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset); + + std::printf("Anonymous import %u, segment offset = %X, relocation offset = %X\n", anonymousImport, segmentOffset, relocationOffset); + + cro.patchBatch(relocationOffset, getSegmentAddr(segmentOffset)); + } + } + + currentCROPointer = cro.getNextCRO(); + } + + return true; + } + + bool clearSymbolExports(u32 loadedCRS) { + if (loadedCRS == 0) { + Helpers::panic("CRS not loaded"); + } + + u32 currentCROPointer = loadedCRS; + while (currentCROPointer != 0) { + CRO cro(mem, currentCROPointer, true); + + const u32 onUnresolvedAddr = cro.getOnUnresolvedAddr(); + + const u32 importStringSize = mem.read32(currentCROPointer + CROHeader::ImportStringSize); + + // Clear named symbol exports + const CROHeaderEntry namedImportTable = cro.getHeaderEntry(CROHeader::NamedImportTableOffset); + + for (u32 namedImport = 0; namedImport < namedImportTable.size; namedImport++) { + const u32 relocationOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::RelocationOffset); + + u8 isResolved = mem.read8(relocationOffset + RelocationPatch::IsResolved); + + if (isResolved != 0) { + const u32 nameOffset = mem.read32(namedImportTable.offset + 8 * namedImport + NamedImportTable::NameOffset); + + const std::string symbolName = mem.readString(nameOffset, importStringSize); + + // Check our current CRO for the symbol + const u32 exportSymbolAddr = getNamedExportSymbolAddr(symbolName); + if (exportSymbolAddr == 0) { + continue; + } + + std::printf("Clearing symbol \"%s\"\n", symbolName.c_str()); + + cro.patchBatch(relocationOffset, onUnresolvedAddr, true); + } + } + + // Clear indexed and anonymous symbol exports + const CROHeaderEntry importModuleTable = cro.getHeaderEntry(CROHeader::ImportModuleTableOffset); + + for (u32 importModule = 0; importModule < importModuleTable.size; importModule++) { + // Check if other CROs request module imports from this CRO + const u32 nameOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::NameOffset); + + const std::string moduleName = mem.readString(nameOffset, importStringSize); + + if (moduleName.compare(getModuleName()) != 0) { + continue; + } + + // Export indexed symbols + const u32 indexedOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset); + const u32 indexedNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedNum); + + for (u32 indexedImport = 0; indexedImport < indexedNum; indexedImport++) { + Helpers::panic("TODO: clear indexed exports"); + } + + // Export anonymous symbols + const u32 anonymousOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset); + const u32 anonymousNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousNum); + + for (u32 anonymousImport = 0; anonymousImport < anonymousNum; anonymousImport++) { + if (anonymousOffset == 0) { + Helpers::panic("Anonymous symbol offset is NULL"); + } + + const u32 relocationOffset = mem.read32(anonymousOffset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset); + + cro.patchBatch(relocationOffset, onUnresolvedAddr, true); + } + } + + currentCROPointer = cro.getNextCRO(); + } + + return true; + } // Links CROs. Heavily based on Citra's CRO linker bool link(u32 loadedCRS, bool isNew) { @@ -691,8 +1093,7 @@ class CRO { importNamedSymbols(loadedCRS); importModules(loadedCRS); - - // TODO: export symbols to other CROs + exportSymbols(loadedCRS); // Restore .data segment offset (LoadCRO_New) if (isNew) { @@ -704,6 +1105,18 @@ class CRO { return true; } + bool unlink(u32 loadedCRS) { + if (loadedCRS == 0) { + Helpers::panic("CRS not loaded"); + } + + clearNamedSymbols(); + clearModules(); + clearSymbolExports(loadedCRS); + + return true; + } + // Adds CRO to the linked list of loaded CROs void registerCRO(u32 loadedCRS, bool autoLink) { if (loadedCRS == 0) { @@ -729,19 +1142,69 @@ class CRO { } else { // Register new CRO CRO head(mem, headAddr, true); + CRO tail(mem, head.getPrevCRO(), true); - if (head.getPrevCRO() == 0) { - Helpers::panic("No tail CRO found"); + if (tail.getNextCRO() != 0) { + Helpers::panic("Invalid CRO tail"); } - CRO tail(mem, head.getPrevCRO(), true); - setPrevCRO(tail.croPointer); tail.setNextCRO(croPointer); head.setPrevCRO(croPointer); } } + + // Thanks, Citra + void unregisterCRO(u32 loadedCRS) { + if (loadedCRS == 0) { + Helpers::panic("CRS not loaded"); + } + + CRO crs(mem, loadedCRS, false); + + CRO next(mem, getNextCRO(), true); + CRO prev(mem, getPrevCRO(), true); + + CRO nextHead(mem, crs.getNextCRO(), true); + CRO prevHead(mem, crs.getPrevCRO(), true); + + if ((croPointer == nextHead.croPointer) || (croPointer == prevHead.croPointer)) { + // Our current CRO is the head, remove it + const u32 nextPointer = next.croPointer; + + if (nextPointer != 0) { + next.setPrevCRO(prev.croPointer); + } + + if (croPointer == prevHead.croPointer) { + crs.setPrevCRO(nextPointer); + } else { + crs.setNextCRO(nextPointer); + } + } else if (next.croPointer != 0) { + // Our current CRO is neither the head nor the tail, + // link the next and previous CRO with each other + prev.setNextCRO(next.croPointer); + next.setPrevCRO(prev.croPointer); + } else { + // Our current CRO is the tail, remove it + prev.setNextCRO(0); + + const u32 prevPointer = prev.croPointer; + + if ((nextHead.croPointer != 0) && (nextHead.getPrevCRO() == croPointer)) { + nextHead.setPrevCRO(prevPointer); + } else if ((prevHead.croPointer != 0) && (prevHead.getPrevCRO() == croPointer)) { + prevHead.setPrevCRO(prevPointer); + } else { + Helpers::panic("bwaaa"); + } + } + + setNextCRO(0); + setPrevCRO(0); + } }; void LDRService::reset() { @@ -755,6 +1218,7 @@ void LDRService::handleSyncRequest(u32 messagePointer) { case LDRCommands::LoadCRR: loadCRR(messagePointer); break; case LDRCommands::LoadCRO: loadCRO(messagePointer, false); break; case LDRCommands::UnloadCRO: unloadCRO(messagePointer); break; + case LDRCommands::LinkCRO: linkCRO(messagePointer); break; case LDRCommands::LoadCRONew: loadCRO(messagePointer, true); break; default: Helpers::panic("LDR::RO service requested. Command: %08X\n", command); } @@ -766,7 +1230,7 @@ void LDRService::initialize(u32 messagePointer) { const u32 mapVaddr = mem.read32(messagePointer + 12); const Handle process = mem.read32(messagePointer + 20); - log("LDR_RO::Initialize (buffer = %08X, size = %08X, vaddr = %08X, process = %X)\n", crsPointer, size, mapVaddr, process); + std::printf("LDR_RO::Initialize (buffer = %08X, size = %08X, vaddr = %08X, process = %X)\n", crsPointer, size, mapVaddr, process); // Sanity checks if (loadedCRS != 0) { @@ -792,24 +1256,52 @@ void LDRService::initialize(u32 messagePointer) { // Map CRO to output address mem.mirrorMapping(mapVaddr, crsPointer, size); - CRO crs(mem, crsPointer, false); + CRO crs(mem, mapVaddr, false); if (!crs.load()) { Helpers::panic("Failed to load CRS"); } - if (!crs.rebase(0, mapVaddr, 0, 0)) { + if (!crs.rebase(0, 0, 0)) { Helpers::panic("Failed to rebase CRS"); } kernel.clearInstructionCache(); - loadedCRS = crsPointer; + loadedCRS = mapVaddr; mem.write32(messagePointer, IPC::responseHeader(0x1, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } +void LDRService::linkCRO(u32 messagePointer) { + const u32 mapVaddr = mem.read32(messagePointer + 4); + const Handle process = mem.read32(messagePointer + 12); + + std::printf("LDR_RO::LinkCRO (vaddr = %X, process = %X)\n", mapVaddr, process); + + if (loadedCRS == 0) { + Helpers::panic("CRS not loaded"); + } + + if (!mem.isAligned(mapVaddr)) { + Helpers::panic("Unaligned CRO vaddr\n"); + } + + CRO cro(mem, mapVaddr, true); + + // TODO: check if CRO has been loaded prior to calling this + + if (!cro.link(loadedCRS, false)) { + Helpers::panic("Failed to link CRO"); + } + + kernel.clearInstructionCache(); + + mem.write32(messagePointer, IPC::responseHeader(0x6, 1, 0)); + mem.write32(messagePointer + 4, Result::Success); +} + void LDRService::loadCRR(u32 messagePointer) { const u32 crrPointer = mem.read32(messagePointer + 4); const u32 size = mem.read32(messagePointer + 8); @@ -854,13 +1346,13 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) { // Map CRO to output address mem.mirrorMapping(mapVaddr, croPointer, size); - CRO cro(mem, croPointer, true); + CRO cro(mem, mapVaddr, true); if (!cro.load()) { Helpers::panic("Failed to load CRO"); } - if (!cro.rebase(loadedCRS, mapVaddr, dataVaddr, bssVaddr)) { + if (!cro.rebase(loadedCRS, dataVaddr, bssVaddr)) { Helpers::panic("Failed to rebase CRO"); } @@ -873,6 +1365,8 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) { // TODO: add fixing cro.fix(fixLevel); + std::puts("Done :)"); + kernel.clearInstructionCache(); if (isNew) { @@ -890,8 +1384,34 @@ void LDRService::unloadCRO(u32 messagePointer) { const u32 croPointer = mem.read32(messagePointer + 12); const Handle process = mem.read32(messagePointer + 20); - Helpers::warn("LDR_RO::UnloadCRO (vaddr = %08X, buffer = %08X, process = %X)\n", mapVaddr, croPointer, process); + std::printf("LDR_RO::UnloadCRO (vaddr = %08X, buffer = %08X, process = %X)\n", mapVaddr, croPointer, process); + + // TODO: is CRO loaded? + + if (!mem.isAligned(croPointer)) { + Helpers::panic("Unaligned CRO pointer\n"); + } + + if (!mem.isAligned(mapVaddr)) { + Helpers::panic("Unaligned CRO output vaddr\n"); + } + + CRO cro(mem, mapVaddr, true); + + cro.unregisterCRO(loadedCRS); + + if (!cro.unlink(loadedCRS)) { + Helpers::panic("Failed to unlink CRO"); + } + + if (!cro.unrebase()) { + Helpers::panic("Failed to unrebase CRO"); + } + + std::puts("Done :)"); + + kernel.clearInstructionCache(); - mem.write32(messagePointer, IPC::responseHeader(0x5, 2, 0)); + mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0)); mem.write32(messagePointer + 4, Result::Success); } \ No newline at end of file From df9ce3cd10417a290749529c31ec0e92d7bfdf46 Mon Sep 17 00:00:00 2001 From: Nomi Date: Sat, 9 Sep 2023 00:54:59 +0200 Subject: [PATCH 12/15] Remove debug printfs --- src/core/services/ldr_ro.cpp | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index a963489a5..1e1b99f7e 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -802,26 +802,18 @@ class CRO { const std::string importModuleName = mem.readString(nameOffset, importStringSize); - std::printf("Importing module \"%s\"\n", importModuleName.c_str()); - // Find import module u32 currentCROPointer = loadedCRS; while (currentCROPointer != 0) { CRO cro(mem, currentCROPointer, true); - std::printf("Exporting module name \"%s\"\n", cro.getModuleName().c_str()); - if (importModuleName.compare(cro.getModuleName()) == 0) { - std::printf("Found module \"%s\"\n", importModuleName.c_str()); - // Import indexed symbols const CROHeaderEntry indexedExportTable = cro.getHeaderEntry(CROHeader::IndexedExportTableOffset); const u32 indexedOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset); const u32 indexedNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedNum); - std::printf("Importing indexed symbols (num = %u, offset = %X)\n", indexedNum, indexedOffset); - for (u32 indexedImport = 0; indexedImport < indexedNum; indexedImport++) { if (indexedOffset == 0) { Helpers::panic("Indexed symbol offset is NULL"); @@ -832,8 +824,6 @@ class CRO { const u32 segmentOffset = mem.read32(indexedExportTable.offset + 4 * importIndex + IndexedExportTable::SegmentOffset); const u32 relocationOffset = mem.read32(indexedOffset + 8 * indexedImport + IndexedImportTable::RelocationOffset); - std::printf("Indexed import %u, index = %u, segment offset = %X, relocation offset = %X\n", indexedImport, importIndex, segmentOffset, relocationOffset); - patchBatch(relocationOffset, cro.getSegmentAddr(segmentOffset)); } @@ -841,8 +831,6 @@ class CRO { const u32 anonymousOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousOffset); const u32 anonymousNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::AnonymousNum); - std::printf("Importing anonymous symbols (num = %u, offset = %X)\n", anonymousNum, anonymousOffset); - for (u32 anonymousImport = 0; anonymousImport < anonymousNum; anonymousImport++) { if (anonymousOffset == 0) { Helpers::panic("Anonymous symbol offset is NULL"); @@ -851,8 +839,6 @@ class CRO { const u32 segmentOffset = mem.read32(anonymousOffset + 8 * anonymousImport + AnonymousImportTable::SegmentOffset); const u32 relocationOffset = mem.read32(anonymousOffset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset); - std::printf("Anonymous import %u, segment offset = %X, relocation offset = %X\n", anonymousImport, segmentOffset, relocationOffset); - patchBatch(relocationOffset, cro.getSegmentAddr(segmentOffset)); } @@ -938,8 +924,6 @@ class CRO { continue; } - std::printf("Found symbol \"%s\" @ %X, relocation offset = %X\n", symbolName.c_str(), exportSymbolAddr, relocationOffset); - cro.patchBatch(relocationOffset, exportSymbolAddr); } } @@ -957,8 +941,6 @@ class CRO { continue; } - std::printf("Exporting module \"%s\"\n", moduleName.c_str()); - // Export indexed symbols const u32 indexedOffset = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedOffset); const u32 indexedNum = mem.read32(importModuleTable.offset + 20 * importModule + ImportModuleTable::IndexedNum); @@ -979,8 +961,6 @@ class CRO { const u32 segmentOffset = mem.read32(anonymousOffset + 8 * anonymousImport + AnonymousImportTable::SegmentOffset); const u32 relocationOffset = mem.read32(anonymousOffset + 8 * anonymousImport + AnonymousImportTable::RelocationOffset); - std::printf("Anonymous import %u, segment offset = %X, relocation offset = %X\n", anonymousImport, segmentOffset, relocationOffset); - cro.patchBatch(relocationOffset, getSegmentAddr(segmentOffset)); } } @@ -1023,8 +1003,6 @@ class CRO { continue; } - std::printf("Clearing symbol \"%s\"\n", symbolName.c_str()); - cro.patchBatch(relocationOffset, onUnresolvedAddr, true); } } @@ -1230,7 +1208,7 @@ void LDRService::initialize(u32 messagePointer) { const u32 mapVaddr = mem.read32(messagePointer + 12); const Handle process = mem.read32(messagePointer + 20); - std::printf("LDR_RO::Initialize (buffer = %08X, size = %08X, vaddr = %08X, process = %X)\n", crsPointer, size, mapVaddr, process); + log("LDR_RO::Initialize (buffer = %08X, size = %08X, vaddr = %08X, process = %X)\n", crsPointer, size, mapVaddr, process); // Sanity checks if (loadedCRS != 0) { @@ -1278,7 +1256,7 @@ void LDRService::linkCRO(u32 messagePointer) { const u32 mapVaddr = mem.read32(messagePointer + 4); const Handle process = mem.read32(messagePointer + 12); - std::printf("LDR_RO::LinkCRO (vaddr = %X, process = %X)\n", mapVaddr, process); + log("LDR_RO::LinkCRO (vaddr = %X, process = %X)\n", mapVaddr, process); if (loadedCRS == 0) { Helpers::panic("CRS not loaded"); @@ -1324,7 +1302,7 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) { const u32 fixLevel = mem.read32(messagePointer + 40); const Handle process = mem.read32(messagePointer + 52); - std::printf("LDR_RO::LoadCRO (isNew = %d, buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", isNew, croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); + log("LDR_RO::LoadCRO (isNew = %d, buffer = %08X, vaddr = %08X, size = %08X, .data vaddr = %08X, .data size = %08X, .bss vaddr = %08X, .bss size = %08X, auto link = %d, fix level = %X, process = %X)\n", isNew, croPointer, mapVaddr, size, dataVaddr, dataSize, bssVaddr, bssSize, autoLink, fixLevel, process); // Sanity checks if (size < CRO_HEADER_SIZE) { @@ -1365,8 +1343,6 @@ void LDRService::loadCRO(u32 messagePointer, bool isNew) { // TODO: add fixing cro.fix(fixLevel); - std::puts("Done :)"); - kernel.clearInstructionCache(); if (isNew) { @@ -1384,7 +1360,7 @@ void LDRService::unloadCRO(u32 messagePointer) { const u32 croPointer = mem.read32(messagePointer + 12); const Handle process = mem.read32(messagePointer + 20); - std::printf("LDR_RO::UnloadCRO (vaddr = %08X, buffer = %08X, process = %X)\n", mapVaddr, croPointer, process); + log("LDR_RO::UnloadCRO (vaddr = %08X, buffer = %08X, process = %X)\n", mapVaddr, croPointer, process); // TODO: is CRO loaded? @@ -1408,8 +1384,6 @@ void LDRService::unloadCRO(u32 messagePointer) { Helpers::panic("Failed to unrebase CRO"); } - std::puts("Done :)"); - kernel.clearInstructionCache(); mem.write32(messagePointer, IPC::responseHeader(0x5, 1, 0)); From 505d54d79ac20cf7ca2cbd4e0e73af0ead9cf27d Mon Sep 17 00:00:00 2001 From: Nomi Date: Sun, 10 Sep 2023 22:24:38 +0200 Subject: [PATCH 13/15] Update service_manager.cpp --- src/core/services/service_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/services/service_manager.cpp b/src/core/services/service_manager.cpp index 0fbcdac41..3696b0d1b 100644 --- a/src/core/services/service_manager.cpp +++ b/src/core/services/service_manager.cpp @@ -8,7 +8,7 @@ ServiceManager::ServiceManager(std::span regs, Memory& mem, GPU& gpu, u32& currentPID, Kernel& kernel, const EmulatorConfig& config) : regs(regs), mem(mem), kernel(kernel), ac(mem), am(mem), boss(mem), act(mem), apt(mem, kernel), cam(mem, kernel), cecd(mem, kernel), cfg(mem), csnd(mem, kernel), dlp_srvr(mem), dsp(mem, kernel), hid(mem, kernel), http(mem), ir_user(mem, kernel), frd(mem), fs(mem, kernel, config), - gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), + gsp_gpu(mem, gpu, kernel, currentPID), gsp_lcd(mem), ldr(mem, kernel), mcu_hwc(mem, config), mic(mem, kernel), nfc(mem, kernel), nim(mem), ndm(mem), news_u(mem), nwm_uds(mem, kernel), ptm(mem, config), soc(mem), ssl(mem), y2r(mem, kernel) {} static constexpr int MAX_NOTIFICATION_COUNT = 16; From 29e3aa94123a0a5cc1d4fd9c121dcfcbaefe6575 Mon Sep 17 00:00:00 2001 From: Nomi Date: Wed, 13 Sep 2023 17:54:10 +0200 Subject: [PATCH 14/15] Update ldr_ro.cpp --- src/core/services/ldr_ro.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index 1e1b99f7e..a3594c187 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -22,9 +22,9 @@ namespace CROHeader { NameOffset = 0x084, NextCRO = 0x088, PrevCRO = 0x08C, + OnUnresolved = 0x0AC, CodeOffset = 0x0B0, DataOffset = 0x0B8, - OnUnresolved = 0x0AC, ModuleNameOffset = 0x0C0, SegmentTableOffset = 0x0C8, SegmentTableSize = 0x0CC, @@ -137,6 +137,10 @@ struct CROHeaderEntry { static constexpr u32 CRO_HEADER_SIZE = 0x138; +static const std::string CRO_MAGIC("CRO0"); +static const std::string CRO_MAGIC_FIXED("FIXD"); +static const std::string CRR_MAGIC("CRR0"); + class CRO { Memory &mem; @@ -293,7 +297,7 @@ class CRO { // Verify CRO magic const std::string magic = mem.readString(croPointer + CROHeader::ID, 4); - if (magic.compare(std::string("CRO0")) != 0) { + if (magic.compare(CRO_MAGIC) != 0) { return false; } From f2171ff3fe828717faccedd9d51280949c9b5b3b Mon Sep 17 00:00:00 2001 From: Nomi Date: Wed, 13 Sep 2023 17:56:14 +0200 Subject: [PATCH 15/15] Update ldr_ro.cpp --- src/core/services/ldr_ro.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/services/ldr_ro.cpp b/src/core/services/ldr_ro.cpp index a3594c187..2ad62d99c 100644 --- a/src/core/services/ldr_ro.cpp +++ b/src/core/services/ldr_ro.cpp @@ -853,7 +853,7 @@ class CRO { } if (currentCROPointer == 0) { - Helpers::warn("Unable to find import module %s\n", importModuleName.c_str()); + Helpers::warn("Unable to find import module \"%s\"", importModuleName.c_str()); } }