cross section relocs

This commit is contained in:
Rainchus 2026-04-04 17:45:54 -05:00
parent 81213c1831
commit b1be38310b
5 changed files with 30 additions and 4 deletions

View file

@ -235,6 +235,8 @@ namespace N64Recomp {
bool skip_validating_reference_symbols = true; bool skip_validating_reference_symbols = true;
// Whether all function calls (excluding reference symbols) should go through lookup. // Whether all function calls (excluding reference symbols) should go through lookup.
bool use_lookup_for_all_function_calls = false; bool use_lookup_for_all_function_calls = false;
// Whether function calls between relocatable sections is allowed.
bool cross_relocatable_section_function_calls = false;
//// Only used by the CLI, TODO move this to a struct in the internal headers. //// Only used by the CLI, TODO move this to a struct in the internal headers.
// A mapping of function name to index in the functions vector // A mapping of function name to index in the functions vector

View file

@ -417,6 +417,15 @@ N64Recomp::Config::Config(const char* path) {
use_mdebug = false; use_mdebug = false;
} }
// Whether function calls across relocatable sections is allowed (optional)
std::optional<bool> cross_relocatable_section_function_calls_opt = input_data["cross_relocatable_section_function_calls"].value<bool>();
if (cross_relocatable_section_function_calls_opt.has_value()) {
cross_relocatable_section_function_calls = cross_relocatable_section_function_calls_opt.value();
}
else {
cross_relocatable_section_function_calls = false;
}
// Symbols to ignore when parsing mdebug (option, defaults to empty) // Symbols to ignore when parsing mdebug (option, defaults to empty)
toml::node_view mdebug_mappings_data = input_data["mdebug_file_mappings"]; toml::node_view mdebug_mappings_data = input_data["mdebug_file_mappings"];
if (mdebug_mappings_data.is_array()) { if (mdebug_mappings_data.is_array()) {

View file

@ -44,6 +44,7 @@ namespace N64Recomp {
bool use_absolute_symbols; bool use_absolute_symbols;
bool unpaired_lo16_warnings; bool unpaired_lo16_warnings;
bool use_mdebug; bool use_mdebug;
bool cross_relocatable_section_function_calls;
bool trace_mode; bool trace_mode;
bool allow_exports; bool allow_exports;
bool strict_patch_mode; bool strict_patch_mode;

View file

@ -524,7 +524,8 @@ int main(int argc, char** argv) {
func->name = func->name + "_recomp"; func->name = func->name + "_recomp";
} }
// Propogate the trace mode parameter. // Propagate config params.
context.cross_relocatable_section_function_calls = config.cross_relocatable_section_function_calls;
context.trace_mode = config.trace_mode; context.trace_mode = config.trace_mode;
// Apply any single-instruction patches. // Apply any single-instruction patches.

View file

@ -18,6 +18,7 @@ enum class JalResolutionResult {
Match, Match,
CreateStatic, CreateStatic,
Ambiguous, Ambiguous,
AmbiguousWithRelocatable,
Error Error
}; };
@ -34,6 +35,7 @@ JalResolutionResult resolve_jal(const N64Recomp::Context& context, size_t cur_se
uint32_t section_vram_end = cur_section.ram_addr + cur_section.size; uint32_t section_vram_end = cur_section.ram_addr + cur_section.size;
bool in_current_section = target_func_vram >= section_vram_start && target_func_vram < section_vram_end; bool in_current_section = target_func_vram >= section_vram_start && target_func_vram < section_vram_end;
bool exact_match_found = false; bool exact_match_found = false;
bool found_relocatable_match = false;
// Use a thread local to prevent reallocation across runs and to allow multi-threading in the future. // Use a thread local to prevent reallocation across runs and to allow multi-threading in the future.
thread_local std::vector<size_t> matched_funcs{}; thread_local std::vector<size_t> matched_funcs{};
@ -59,10 +61,13 @@ JalResolutionResult resolve_jal(const N64Recomp::Context& context, size_t cur_se
break; break;
} }
// If the function's section isn't relocatable, add the function as a candidate. // If the function's section isn't relocatable or if cross relocatable section function calls are enabled, add the function as a candidate.
const auto& target_func_section = context.sections[target_func.section_index]; const auto& target_func_section = context.sections[target_func.section_index];
if (!target_func_section.relocatable) { if (context.cross_relocatable_section_function_calls || !target_func_section.relocatable) {
matched_funcs.push_back(target_func_index); matched_funcs.push_back(target_func_index);
if (target_func_section.relocatable) {
found_relocatable_match = true;
}
} }
} }
} }
@ -93,6 +98,11 @@ JalResolutionResult resolve_jal(const N64Recomp::Context& context, size_t cur_se
} }
// If there's more than one match, use an indirect jump to resolve the function at runtime. // If there's more than one match, use an indirect jump to resolve the function at runtime.
else { else {
// Ambiguous matches where any of the matches are in a relocatable section must be reported differently than normal ambiguous matches.
// This is because the loaded address of the function may not be the original address, so a lookup using the original address would fail.
if (found_relocatable_match) {
return JalResolutionResult::AmbiguousWithRelocatable;
}
return JalResolutionResult::Ambiguous; return JalResolutionResult::Ambiguous;
} }
} }
@ -162,7 +172,7 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
uint16_t imm = instr.Get_immediate(); uint16_t imm = instr.Get_immediate();
// Check if this instruction has a reloc. // Check if this instruction has a reloc.
if (section.relocs.size() > 0 && section.relocs[reloc_index].address == instr_vram) { if (section.relocs.size() > 0 && section.relocs[reloc_index].address == instr_vram && section.relocs[reloc_index].type != N64Recomp::RelocType::R_MIPS_NONE) {
has_reloc = true; has_reloc = true;
// Get the reloc data for this instruction // Get the reloc data for this instruction
const auto& reloc = section.relocs[reloc_index]; const auto& reloc = section.relocs[reloc_index];
@ -326,6 +336,9 @@ bool process_instruction(GeneratorType& generator, const N64Recomp::Context& con
// If a game ever needs to jump between multiple relocatable sections, relocation will be necessary here. // If a game ever needs to jump between multiple relocatable sections, relocation will be necessary here.
call_by_lookup = true; call_by_lookup = true;
break; break;
case JalResolutionResult::AmbiguousWithRelocatable:
fmt::print(stderr, "Ambiguous jal target 0x{:08X} in function {}, but one or more matches were relocatable. Calling by lookup for relocatable functions with the unrelocated address is not supported.\n", target_func_vram, func.name);
return false;
case JalResolutionResult::Error: case JalResolutionResult::Error:
fmt::print(stderr, "Internal error when resolving jal to address 0x{:08X} in function {}. Please report this issue.\n", target_func_vram, func.name); fmt::print(stderr, "Internal error when resolving jal to address 0x{:08X} in function {}. Please report this issue.\n", target_func_vram, func.name);
return false; return false;