From 871bcb86959af047f34b9674548f35716f3f11e5 Mon Sep 17 00:00:00 2001 From: Nomi Date: Tue, 5 Sep 2023 22:50:30 +0200 Subject: [PATCH] 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 d719f9bc..e87dad7b 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 @@ public: // 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 @@ public: 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 @@ public: 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 @@ public: 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() {}