From 30919fb4a6df4e8d96a67924ee00f937b7456d1b Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Thu, 23 Jan 2025 01:36:14 -0500 Subject: [PATCH] Implement relocs for function regeneration in hooking --- librecomp/include/librecomp/overlays.hpp | 4 ++ librecomp/include/librecomp/sections.h | 24 +++++++ librecomp/src/mods.cpp | 84 +++++++++++++++++++++++- librecomp/src/overlays.cpp | 18 +++++ 4 files changed, 128 insertions(+), 2 deletions(-) diff --git a/librecomp/include/librecomp/overlays.hpp b/librecomp/include/librecomp/overlays.hpp index 339910a..c06abd7 100644 --- a/librecomp/include/librecomp/overlays.hpp +++ b/librecomp/include/librecomp/overlays.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "sections.h" namespace recomp { @@ -42,6 +43,9 @@ namespace recomp { void add_loaded_function(int32_t ram_addr, recomp_func_t* func); std::unordered_set get_base_patched_funcs(); + + std::span get_section_relocs(uint16_t code_section_index); + std::span get_patch_section_relocs(uint16_t patch_code_section_index); } }; diff --git a/librecomp/include/librecomp/sections.h b/librecomp/include/librecomp/sections.h index 91789d3..52005f3 100644 --- a/librecomp/include/librecomp/sections.h +++ b/librecomp/include/librecomp/sections.h @@ -12,12 +12,36 @@ typedef struct { uint32_t rom_size; } FuncEntry; +typedef enum { + R_MIPS_NONE = 0, + R_MIPS_16, + R_MIPS_32, + R_MIPS_REL32, + R_MIPS_26, + R_MIPS_HI16, + R_MIPS_LO16, + R_MIPS_GPREL16, +} RelocEntryType; + +typedef struct { + // Offset into the section of the word to relocate. + uint32_t offset; + // Reloc addend from the target section's address. + uint32_t target_section_offset; + // Index of the target section (indexes into `section_addresses`). + uint16_t target_section; + // Relocation type. + RelocEntryType type; +} RelocEntry; + typedef struct { uint32_t rom_addr; uint32_t ram_addr; uint32_t size; FuncEntry *funcs; size_t num_funcs; + RelocEntry* relocs; + size_t num_relocs; size_t index; } SectionTableEntry; diff --git a/librecomp/src/mods.cpp b/librecomp/src/mods.cpp index a9fa9dd..59a2046 100644 --- a/librecomp/src/mods.cpp +++ b/librecomp/src/mods.cpp @@ -757,6 +757,7 @@ struct PatchedSection { uint32_t rom_addr; uint32_t ram_addr; size_t first_func_index; + size_t first_reloc_index; }; struct PatchedFunction { @@ -764,9 +765,17 @@ struct PatchedFunction { uint32_t size; }; +struct PatchedReloc { + uint32_t section_offset; + uint32_t target_section; + uint32_t target_section_offset; + RelocEntryType type; +}; + struct PatchedList { std::vector sections; std::vector functions; + std::vector relocs; }; N64Recomp::Context context_from_patched_function_list(const PatchedList& patchlist, std::span rom) { @@ -784,11 +793,14 @@ N64Recomp::Context context_from_patched_function_list(const PatchedList& patchli N64Recomp::Section& section_out = ret.sections[section_index]; size_t cur_num_funcs; + size_t cur_num_relocs; if (section_index == patchlist.sections.size() - 1) { cur_num_funcs = patchlist.functions.size() - section_in.first_func_index; + cur_num_relocs = patchlist.relocs.size() - section_in.first_reloc_index; } else { cur_num_funcs = patchlist.sections[section_index + 1].first_func_index - section_in.first_func_index; + cur_num_relocs = patchlist.sections[section_index + 1].first_reloc_index - section_in.first_reloc_index; } section_out.rom_addr = section_in.rom_addr; @@ -796,7 +808,7 @@ N64Recomp::Context context_from_patched_function_list(const PatchedList& patchli section_out.size = 0; section_out.bss_size = 0; section_out.function_addrs.resize(cur_num_funcs); - section_out.relocs = std::vector{}; + section_out.relocs.resize(cur_num_relocs); section_out.name = "patch_section_" + std::to_string(section_index); section_out.bss_section_index = 0; section_out.executable = true; @@ -831,6 +843,21 @@ N64Recomp::Context context_from_patched_function_list(const PatchedList& patchli // Add the function to the lookup table. ret.functions_by_vram[function_out.vram].push_back(function_index); } + + for (size_t section_reloc_index = 0; section_reloc_index < cur_num_relocs; section_reloc_index++) { + // Get the global index of the reloc within the patchlist. + size_t reloc_index = section_in.first_reloc_index + section_reloc_index; + + const PatchedReloc& reloc_in = patchlist.relocs[reloc_index]; + N64Recomp::Reloc& reloc_out = section_out.relocs[section_reloc_index]; + + reloc_out.address = reloc_in.section_offset + section_out.ram_addr; + reloc_out.target_section_offset = reloc_in.target_section_offset; + reloc_out.symbol_index = 0; // Unused for live recompilation. + reloc_out.target_section = reloc_in.target_section; + reloc_out.type = static_cast(reloc_in.type); + reloc_out.reference_symbol = true; + } } return ret; @@ -1036,6 +1063,8 @@ std::vector recomp::mods::ModContext::load_mo uint32_t cur_section_vram = 0xFFFFFFFF; uint16_t cur_section_index = 0xFFFF; uint32_t cur_function_vram = 0xFFFFFFFF; + std::span cur_section_relocs = {}; + size_t cur_section_reloc_index = 0; // While scanning, also track the hook slot indices for recompilation and the native functions so they can be patched. std::vector func_ptrs{}; @@ -1069,13 +1098,16 @@ std::vector recomp::mods::ModContext::load_mo auto& section_out = patchlist.sections.emplace_back(PatchedSection{ .rom_addr = cur_hook_def.section_rom, .ram_addr = recomp::overlays::get_section_ram_addr(section_index), - .first_func_index = patchlist.functions.size() + .first_func_index = patchlist.functions.size(), + .first_reloc_index = patchlist.relocs.size() }); // Update the tracked section fields. cur_section_rom = section_out.rom_addr; cur_section_vram = section_out.ram_addr; cur_section_index = section_index; + cur_section_relocs = recomp::overlays::get_section_relocs(cur_section_index); + cur_section_reloc_index = 0; // Reset the tracked function vram to prevent issues when two functions have the same vram in different sections. cur_function_vram = 0xFFFFFFFF; @@ -1122,6 +1154,42 @@ std::vector recomp::mods::ModContext::load_mo // Update the tracked function address. cur_function_vram = cur_hook_def.function_vram; + + // Advance forward in the section's reloc list until reaching this function's offset or the end of the list. + size_t cur_function_offset = cur_function_vram - cur_section_vram; + size_t cur_function_end_offset = cur_function_offset + function_rom_size; + while (true) { + if (cur_section_reloc_index >= cur_section_relocs.size()) { + break; + } + const auto& reloc_in = cur_section_relocs[cur_section_reloc_index]; + if (reloc_in.offset >= cur_function_offset) { + break; + } + + cur_section_reloc_index++; + } + + // Add all relocs until the end of this function or the end of the reloc list. + while (true) { + if (cur_section_reloc_index >= cur_section_relocs.size()) { + break; + } + + const auto& reloc_in = cur_section_relocs[cur_section_reloc_index]; + if (reloc_in.offset >= cur_function_end_offset) { + break; + } + + patchlist.relocs.emplace_back(PatchedReloc { + .section_offset = reloc_in.offset, + .target_section = reloc_in.target_section, + .target_section_offset = reloc_in.target_section_offset, + .type = reloc_in.type + }); + + cur_section_reloc_index++; + } } // Record the hooks in the function to hook mapping. @@ -1136,6 +1204,7 @@ std::vector recomp::mods::ModContext::load_mo // Generate the recompiler context. N64Recomp::Context hook_context = context_from_patched_function_list(patchlist, decompressed_rom); + hook_context.set_all_reference_sections_relocatable(); hook_context.use_lookup_for_all_function_calls = true; // Regenerate the functions using the live recompiler. @@ -1159,6 +1228,17 @@ std::vector recomp::mods::ModContext::load_mo unload_mods(); return ret; } + + std::string reference_syms_error_param{}; + CodeModLoadError reference_syms_error = regenerated_code_handle->populate_reference_symbols(hook_context, reference_syms_error_param); + if (reference_syms_error != CodeModLoadError::Good) { + regenerated_code_handle.reset(); + ret.emplace_back(ModLoadErrorDetails{ + "", ModLoadError::FailedToLoadCode, error_to_string(CodeModLoadError::InternalError) + }); + unload_mods(); + return ret; + } // Patch the functions that were regenerated. for (size_t patched_func_index = 0; patched_func_index < func_ptrs.size(); patched_func_index++) { diff --git a/librecomp/src/overlays.cpp b/librecomp/src/overlays.cpp index 53354b6..64e48fa 100644 --- a/librecomp/src/overlays.cpp +++ b/librecomp/src/overlays.cpp @@ -344,3 +344,21 @@ std::unordered_set recomp::overlays::get_base_patched_funcs() { return ret; } + +std::span recomp::overlays::get_section_relocs(uint16_t code_section_index) { + if (code_section_index < sections_info.num_code_sections) { + const auto& section = sections_info.code_sections[code_section_index]; + return std::span{ section.relocs, section.num_relocs }; + } + assert(false); + return {}; +} + +std::span recomp::overlays::get_patch_section_relocs(uint16_t patch_code_section_index) { + if (patch_code_section_index < num_patch_code_sections) { + const auto& section = patch_code_sections[patch_code_section_index]; + return std::span{ section.relocs, section.num_relocs }; + } + assert(false); + return {}; +}