Implement relocs for function regeneration in hooking

This commit is contained in:
Mr-Wiseguy 2025-01-23 01:36:14 -05:00
parent 11259826d7
commit 30919fb4a6
4 changed files with 128 additions and 2 deletions

View file

@ -6,6 +6,7 @@
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <span>
#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<recomp_func_t*> get_base_patched_funcs();
std::span<const RelocEntry> get_section_relocs(uint16_t code_section_index);
std::span<const RelocEntry> get_patch_section_relocs(uint16_t patch_code_section_index);
}
};

View file

@ -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;

View file

@ -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<PatchedSection> sections;
std::vector<PatchedFunction> functions;
std::vector<PatchedReloc> relocs;
};
N64Recomp::Context context_from_patched_function_list(const PatchedList& patchlist, std::span<const uint8_t> 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<N64Recomp::Reloc>{};
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<N64Recomp::RelocType>(reloc_in.type);
reloc_out.reference_symbol = true;
}
}
return ret;
@ -1036,6 +1063,8 @@ std::vector<recomp::mods::ModLoadErrorDetails> 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<const RelocEntry> 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<recomp_func_t*> func_ptrs{};
@ -1069,13 +1098,16 @@ std::vector<recomp::mods::ModLoadErrorDetails> 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::ModLoadErrorDetails> 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::ModLoadErrorDetails> 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::ModLoadErrorDetails> 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++) {

View file

@ -344,3 +344,21 @@ std::unordered_set<recomp_func_t*> recomp::overlays::get_base_patched_funcs() {
return ret;
}
std::span<const RelocEntry> 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<const RelocEntry> 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 {};
}