From b2a57f43915a4dfbde4ab7ca38afbed90dc2edf9 Mon Sep 17 00:00:00 2001 From: techflashYT Date: Wed, 12 Nov 2025 21:21:51 -0700 Subject: [PATCH] Core/Boot: Fix ELF load address semantics Tested with a binary that has VMA != (LMA | 0x80000000), unlike most libogc binaries. This is indeed a valid setup for ELFs, and one that is generally expected to work properly. Test code: https://github.com/Wii-Linux/NPLL/tree/reloc In either case, you still need to set the PC in the debugger to get it to boot properly, though that is a different issue that I will be fixing in a future patch. Without this patch, the code to be runtime-relocated ends up already loaded at the desired final address, and then when the code tries to relocate from what it thinks is the source address (where the code should be, where the LMA is) to the destination address (the VMA, where Dolphin mistakenly already put the code), it ends up overwriting the code that it is about to execute with garbage, and then promptly crashing. After this patch is applied, the behavior now matches the GameCube with Swiss (assuming a [recent patch](https://github.com/emukidid/swiss-gc/commit/bb4a57186c1481e291b7b0ea6d8e722bab816042) is applied), and the Wii with the Homebrew Channel. --- Source/Core/Core/Boot/ElfReader.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/Source/Core/Core/Boot/ElfReader.cpp b/Source/Core/Core/Boot/ElfReader.cpp index f667387ab3c..1ef3cd6f6da 100644 --- a/Source/Core/Core/Boot/ElfReader.cpp +++ b/Source/Core/Core/Boot/ElfReader.cpp @@ -143,17 +143,23 @@ bool ElfReader::LoadIntoMemory(Core::System& system, bool only_in_mem1) const { Elf32_Phdr* p = segments + i; - INFO_LOG_FMT(BOOT, "Type: {} Vaddr: {:08x} Filesz: {} Memsz: {}", p->p_type, p->p_vaddr, - p->p_filesz, p->p_memsz); + INFO_LOG_FMT(BOOT, "Type: {} Vaddr: {:08x} Paddr: {:08x} Filesz: {} Memsz: {}", p->p_type, + p->p_vaddr, p->p_paddr, p->p_filesz, p->p_memsz); if (p->p_type == PT_LOAD) { - u32 writeAddr = p->p_vaddr; + // Check LMA (paddr) first - some are nonsense, so fall back to VMA (vaddr) if invalid + u32 writeAddr = p->p_paddr; + if (writeAddr) + writeAddr |= 0x80000000; // map to virtual address + else + writeAddr = p->p_vaddr; // LMA is empty, fall back to VMA + const u8* src = GetSegmentPtr(i); u32 srcSize = p->p_filesz; u32 dstSize = p->p_memsz; - if (only_in_mem1 && p->p_vaddr >= memory.GetRamSizeReal()) + if (only_in_mem1 && writeAddr >= memory.GetRamSizeReal()) continue; memory.CopyToEmu(writeAddr, src, srcSize);