diff --git a/include/recomp.h b/include/recomp.h index 73f3e73..2775713 100644 --- a/include/recomp.h +++ b/include/recomp.h @@ -468,6 +468,16 @@ void recomp_syscall_handler(uint8_t* rdram, recomp_context* ctx, int32_t instruc void pause_self(uint8_t *rdram); +/* Runtime fragment registrar — invoked from a Memmap_RelocateFragment + * hook (see Stadium's game.toml). Translates Stadium's encoded fragment + * id to the matching section in the recompiled section_table and + * registers all of that section's FuncEntry rows in func_map at + * (fragment_ptr + offset). Also runs the trampoline scanner over the + * fragment's textbin region. Implemented in + * librecomp/src/overlays.cpp. */ +void recomp_register_runtime_fragment(uint8_t *rdram, uint32_t id, + int32_t fragment_ptr); + #ifdef __cplusplus } #endif diff --git a/src/main.cpp b/src/main.cpp index f79580f..bd563b7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -792,7 +792,12 @@ int main(int argc, char** argv) { // Determine the end of this static function uint32_t cur_func_end = static_cast(section.size + section.ram_addr); - // Search for the closest function + // Search for the closest function. The bounds check must come + // first — the previous order read section_funcs[size] before + // exiting, which is OOB and segfaults for static funcs whose + // address lies past the last known function in the section + // (observed in Stadium's .fragment1 once it was marked + // relocatable, which exposes more CreateStatic targets). size_t closest_func_index = 0; while (closest_func_index < section_funcs.size() && section_funcs[closest_func_index] < static_func_addr) { closest_func_index++; @@ -951,6 +956,18 @@ int main(int argc, char** argv) { for (const N64Recomp::Reloc& reloc : section_relocs) { bool emit_reloc = false; uint16_t target_section = reloc.target_section; + // Skip relocs whose type the runtime doesn't understand + // (e.g. R_MIPS_PC16, R_MIPS_GOT16) — these come through + // the ELF parser as raw cast values and would index + // reloc_names out of bounds, producing a NUL byte in + // the .type field of the emitted RelocEntry. Stadium's + // .rel.fragment* sections include R_MIPS_PC16 (type + // 10) for branches; the recompiler resolves those + // statically already, so dropping them here is safe. + size_t type_idx = static_cast(reloc.type); + if (type_idx >= reloc_names.size()) { + continue; + } // In reference symbol mode, only emit relocations into the table that point to // non-absolute reference symbols, events, or manual patch symbols. if (reference_symbol_mode) { diff --git a/src/recompilation.cpp b/src/recompilation.cpp index c228433..514078f 100644 --- a/src/recompilation.cpp +++ b/src/recompilation.cpp @@ -297,8 +297,16 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con } else { uint32_t target_section = func.section_index; - // If this instruction has a reloc and the target section is a normal section, use the section of the reloc when searching for a matching target function. - if (has_reloc && reloc_section < 65500) { + // Only override target_section when the reloc points at a real + // section. SectionAbsolute / SectionImport / SectionEvent are + // special sentinels (negative when interpreted signed) and + // would index context.sections out of bounds — observed when + // marking Stadium's .fragment1 relocatable, which surfaces + // R_MIPS_26 relocs whose ELF symbol lives in SHN_ABS (the + // absolute address is already encoded in the JAL immediate, + // so the in-section function lookup against the caller's + // section is the right fallback). + if (has_reloc && reloc_section < context.sections.size()) { target_section = reloc_section; } JalResolutionResult jal_result = resolve_jal(context, target_section, target_func_vram, matched_func_index);