diff --git a/CHANGES b/CHANGES index 581ec1405..e20cf3b5b 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,7 @@ Other fixes: - VFS: Fix minizip write returning 0 on success instead of size Misc: - GB Serialize: Add missing savestate support for MBC6 and NT (newer) + - GBA: Improve detection of valid ELF ROMs - macOS: Add category to plist (closes mgba.io/i/2691) - macOS: Fix modern build with libepoxy (fixes mgba.io/i/2700) - Qt: Keep track of current pslette preset name (fixes mgba.io/i/2680) diff --git a/include/mgba/internal/gba/gba.h b/include/mgba/internal/gba/gba.h index 206aedf70..020dd2ab5 100644 --- a/include/mgba/internal/gba/gba.h +++ b/include/mgba/internal/gba/gba.h @@ -154,6 +154,12 @@ void GBAHalt(struct GBA* gba); void GBAStop(struct GBA* gba); void GBADebug(struct GBA* gba, uint16_t value); +#ifdef USE_ELF +struct ELF; + +bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target); +#endif + #ifdef USE_DEBUGGERS struct mDebugger; void GBAAttachDebugger(struct GBA* gba, struct mDebugger* debugger); diff --git a/src/gba/core.c b/src/gba/core.c index 5b134338f..68ea66c79 100644 --- a/src/gba/core.c +++ b/src/gba/core.c @@ -237,7 +237,7 @@ static bool _GBACoreInit(struct mCore* core) { #if !defined(MINIMAL_CORE) || MINIMAL_CORE < 2 mDirectorySetInit(&core->dirs); #endif - + return true; } @@ -512,7 +512,7 @@ static bool _GBACoreLoadROM(struct mCore* core, struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { - if (ELFEntry(elf) == BASE_CART0) { + if (GBAVerifyELFEntry(elf, BASE_CART0)) { GBALoadNull(core->board); } bool success = mCoreLoadELF(core, elf); diff --git a/src/gba/gba.c b/src/gba/gba.c index bd0353d0c..de975855a 100644 --- a/src/gba/gba.c +++ b/src/gba/gba.c @@ -595,6 +595,63 @@ void GBADebug(struct GBA* gba, uint16_t flags) { gba->debugFlags = GBADebugFlagsClearSend(gba->debugFlags); } +#ifdef USE_ELF +bool GBAVerifyELFEntry(struct ELF* elf, uint32_t target) { + if (ELFEntry(elf) == target) { + return true; + } + + struct ELFProgramHeaders ph; + ELFProgramHeadersInit(&ph, 0); + ELFGetProgramHeaders(elf, &ph); + size_t i; + for (i = 0; i < ELFProgramHeadersSize(&ph); ++i) { + Elf32_Phdr* phdr = ELFProgramHeadersGetPointer(&ph, i); + if (!phdr->p_filesz) { + continue; + } + + size_t phdrS = phdr->p_paddr; + size_t phdrE = phdrS + phdr->p_filesz; + + // Does the segment contain our target address? + if (target < phdrS || target + 4 > phdrE) { + continue; + } + + // File offset to what should be the rom entry instruction + size_t off = phdr->p_offset + target - phdrS; + + size_t eSize; + const char* bytes = ELFBytes(elf, &eSize); + + // Bounds and alignment check + if (off >= eSize || off & 3) { + continue; + } + + uint32_t opcode; + LOAD_32(opcode, off, bytes); + struct ARMInstructionInfo info; + ARMDecodeARM(opcode, &info); + + if (info.branchType != ARM_BRANCH && info.branchType != ARM_BRANCH_LINKED) { + continue; + } + + uint32_t bTarget = target + info.op1.immediate + 8; + + if (ELFEntry(elf) == bTarget) { + ELFProgramHeadersDeinit(&ph); + return true; + } + } + + ELFProgramHeadersDeinit(&ph); + return false; +} +#endif + bool GBAIsROM(struct VFile* vf) { if (!vf) { return false; @@ -606,7 +663,7 @@ bool GBAIsROM(struct VFile* vf) { uint32_t entry = ELFEntry(elf); bool isGBA = true; isGBA = isGBA && ELFMachine(elf) == EM_ARM; - isGBA = isGBA && (entry == BASE_CART0 || entry == BASE_WORKING_RAM + 0xC0); + isGBA = isGBA && (GBAVerifyELFEntry(elf, BASE_CART0) || GBAVerifyELFEntry(elf, BASE_WORKING_RAM + 0xC0)); ELFClose(elf); return isGBA; } @@ -662,7 +719,7 @@ bool GBAIsMB(struct VFile* vf) { #ifdef USE_ELF struct ELF* elf = ELFOpen(vf); if (elf) { - bool isMB = ELFEntry(elf) == BASE_WORKING_RAM + 0xC0; + bool isMB = GBAVerifyELFEntry(elf, BASE_WORKING_RAM + 0xC0); ELFClose(elf); return isMB; }