Add internal relocations

TODO: add linking, make this prettier
This commit is contained in:
Nomi 2023-09-05 22:50:30 +02:00
parent 205aedc02c
commit 871bcb8695

View file

@ -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() {}