From bc00a039f7da92bfe860077ba78315c687466285 Mon Sep 17 00:00:00 2001 From: Matthew Stanley <1379tech@gmail.com> Date: Sun, 3 May 2026 11:43:50 -0700 Subject: [PATCH] recompilation: remap SectionAbsolute relocs to relocatable sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When splat marks a fragment-internal symbol as undefined (e.g. 'D_8140DD78 = 0x8140DD78;' in undefined_syms_auto.ld), the elf parser records target_section = SectionAbsolute and target_section_offset = the symbol's literal vram. Previously the recompilation walker treated this as a non-relocatable reference and emitted the link-time literal as a lui+addiu pair. At runtime the containing fragment loads at a non-canonical address, so the access misses by the relocation delta and lands in unwritten memory. Same pattern as the prior bss-remap (28de57f) and unsorted-relocs (506b9fc) fixes: producer/consumer asymmetry where some references get RELOC and others bake in the link-time literal. Fix: after the bss → parent remap, walk the registered relocatable sections; if the absolute value falls inside one, redirect reloc_section to that section index and use bss_remap_offset_adjustment to subtract the new section's vram base. Downstream target_relocatable check then treats it correctly and emits RELOC_HI16/LO16. Verified on PokemonStadiumRecomp: the attract-path G_DL target 0x8140DD78 (a Gfx array in fragment34, used by fragment62) now emits as RELOC_HI16(147, 0xDD78) instead of literal 0x8141<<16. Stadium attract advances past the prior send_dl=1157 freeze; environment renders. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/recompilation.cpp | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/recompilation.cpp b/src/recompilation.cpp index 3f41ce2..a6e8fc9 100644 --- a/src/recompilation.cpp +++ b/src/recompilation.cpp @@ -197,6 +197,45 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con reloc_section = find_bss_it->second; } + // SectionAbsolute → relocatable-section remap. When splat marks + // a symbol as undefined (e.g. D_8140DD78 = 0x8140DD78 lives in + // undefined_syms_auto.ld), the elf parser writes + // target_section = SectionAbsolute and target_section_offset = + // the symbol's literal vram. Without remapping, the HI16/LO16 + // emit path below treats it as a non-relocatable reference and + // bakes in the link-time literal. At runtime, when the symbol's + // containing fragment is loaded at a non-canonical address, the + // access misses by the relocation delta — same bug pattern as + // the BSS-remap case (producer/consumer asymmetry: properly + // resolved peers get RELOC, this one gets a literal). + // + // Walk the registered relocatable sections; if the absolute + // value falls inside one, redirect the reloc to that section. + // The downstream relocatable check then treats it correctly. + if (reloc_section == N64Recomp::SectionAbsolute && !reloc.reference_symbol) { + const uint32_t abs_value = uint32_t(reloc.target_section_offset); + for (size_t si = 0; si < context.sections.size(); si++) { + const auto& s = context.sections[si]; + if (!s.relocatable) continue; + const uint32_t s_start = uint32_t(s.ram_addr); + const uint32_t s_end = s_start + uint32_t(s.size); + if (abs_value >= s_start && abs_value < s_end) { + reloc_section = uint32_t(si); + // The downstream code computes + // reloc_target_section_offset = + // reloc.target_section_offset + bss_remap_offset_adjustment; + // For ABS, reloc.target_section_offset is the literal + // vram (e.g. 0x8140DD78). To get the offset within + // the new section base s_start, subtract s_start by + // adding -s_start into the same adjustment slot + // (bss/abs paths are mutually exclusive — bss_remap + // is 0 here because target wasn't a bss section). + bss_remap_offset_adjustment = uint32_t(0) - s_start; + break; + } + } + } + // Check if the relocation references a relocatable section. bool target_relocatable = false; if (!reloc.reference_symbol && reloc_section != N64Recomp::SectionAbsolute) {