Allow cross-section relocations, encode exact target section in mod relocations, add way to tag reference symbol relocations

This commit is contained in:
Mr-Wiseguy 2024-08-23 03:11:12 -04:00
parent 008b1177fa
commit 293b97817b
8 changed files with 142 additions and 86 deletions

View file

@ -230,19 +230,21 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, st
section_order.resize(input_context.sections.size());
std::iota(section_order.begin(), section_order.end(), 0);
// Sort the vector based on the rom address of the corresponding section.
std::sort(section_order.begin(), section_order.end(),
[&](uint16_t a, uint16_t b) {
const auto& section_a = input_context.sections[a];
const auto& section_b = input_context.sections[b];
// Sort primarily by ROM address.
if (section_a.rom_addr != section_b.rom_addr) {
return section_a.rom_addr < section_b.rom_addr;
}
// Sort secondarily by RAM address.
return section_a.ram_addr < section_b.ram_addr;
}
);
// TODO this sort is currently disabled because sections seem to already be ordered
// by elf offset. Determine if this is always the case and remove this if so.
//// Sort the vector based on the rom address of the corresponding section.
//std::sort(section_order.begin(), section_order.end(),
// [&](uint16_t a, uint16_t b) {
// const auto& section_a = input_context.sections[a];
// const auto& section_b = input_context.sections[b];
// // Sort primarily by ROM address.
// if (section_a.rom_addr != section_b.rom_addr) {
// return section_a.rom_addr < section_b.rom_addr;
// }
// // Sort secondarily by RAM address.
// return section_a.ram_addr < section_b.ram_addr;
// }
//);
// TODO avoid a copy here.
ret.rom = input_context.rom;
@ -254,6 +256,9 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, st
size_t num_imports = ret.import_symbols.size();
// Mapping of input section to output section for fixing up relocations.
std::unordered_map<uint16_t, uint16_t> input_section_to_output_section{};
// Iterate over the input sections in their sorted order.
for (uint16_t section_index : section_order) {
const auto& cur_section = input_context.sections[section_index];
@ -271,6 +276,7 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, st
if (cur_section.ram_addr >= output_section_bss_end && cur_section.ram_addr <= round_up_16(output_section_bss_end)) {
// Calculate the output section's bss size by using its non-bss end address and the current section's end address.
section_out.bss_size = cur_section.ram_addr + cur_section.size - output_section_bss_start;
input_section_to_output_section[section_index] = output_section_index;
}
}
continue;
@ -294,6 +300,9 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, st
new_section.ram_addr = cur_section.ram_addr;
new_section.size = cur_section.size;
}
// Map this section to the current output section.
input_section_to_output_section[section_index] = output_section_index;
// Check for special section names.
bool patch_section = cur_section.name == N64Recomp::PatchSectionName;
@ -508,9 +517,11 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, st
}
section_out.relocs.emplace_back(N64Recomp::Reloc{
.address = cur_reloc.address,
.target_section_offset = output_section_offset,
// Use the original target section offset, this will be recalculated based on the output section afterwards.
.target_section_offset = cur_reloc.target_section_offset,
.symbol_index = 0,
.target_section = N64Recomp::SectionSelf,
// Use the input section index in the reloc, this will be converted to the output section afterwards.
.target_section = cur_reloc.target_section,
.type = cur_reloc.type,
.reference_symbol = false,
});
@ -520,6 +531,28 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, st
}
}
// Fix up every internal reloc's target section based on the input to output section mapping.
for (auto& section : ret.sections) {
for (auto& reloc : section.relocs) {
if (!reloc.reference_symbol) {
uint16_t input_section_index = reloc.target_section;
auto find_it = input_section_to_output_section.find(input_section_index);
if (find_it == input_section_to_output_section.end()) {
fmt::print("Reloc at address 0x{:08X} references section {}, which didn't get mapped to an output section\n",
reloc.address, input_context.sections[input_section_index].name);
return {};
}
uint16_t output_section_index = find_it->second;
const auto& input_section = input_context.sections[input_section_index];
const auto& output_section = ret.sections[output_section_index];
// Adjust the reloc's target section offset based on the reloc's new section.
reloc.target_section_offset = reloc.target_section_offset + input_section.ram_addr - output_section.ram_addr;
// Replace the target section with the mapped output section.
reloc.target_section = find_it->second;
}
}
}
// Copy the import reference symbols from the input context as-is to this context.
ret.import_symbols = input_context.import_symbols;
@ -607,6 +640,10 @@ int main(int argc, const char** argv) {
bool mod_context_good;
N64Recomp::Context mod_context = build_mod_context(context, std::move(dependencies), mod_context_good);
std::vector<uint8_t> symbols_bin = N64Recomp::symbols_to_bin_v1(mod_context);
if (symbols_bin.empty()) {
fmt::print(stderr, "Failed to create symbol file\n");
return EXIT_FAILURE;
}
std::ofstream output_syms_file{ config.output_syms_path, std::ios::binary };
output_syms_file.write(reinterpret_cast<const char*>(symbols_bin.data()), symbols_bin.size());

View file

@ -19,6 +19,7 @@ namespace N64Recomp {
uint16_t imm16;
bool reloc_tag_as_reference;
RelocType reloc_type;
uint32_t reloc_section_index;
uint32_t reloc_target_section_offset;

View file

@ -57,7 +57,6 @@ namespace N64Recomp {
bool reference_symbol;
};
constexpr uint16_t SectionSelf = (uint16_t)-1;
constexpr uint16_t SectionAbsolute = (uint16_t)-2;
constexpr uint16_t SectionImport = (uint16_t)-3; // Imported symbols for mods
constexpr uint16_t SectionEvent = (uint16_t)-4;
@ -181,6 +180,8 @@ namespace N64Recomp {
std::vector<std::vector<size_t>> section_functions;
// A mapping of vram address to every function with that address.
std::unordered_map<uint32_t, std::vector<size_t>> functions_by_vram;
// A mapping of bss section index to the corresponding non-bss section index.
std::unordered_map<uint16_t, uint16_t> bss_section_to_section;
// The target ROM being recompiled, TODO move this outside of the context to avoid making a copy for mod contexts.
// Used for reading relocations and for the output binary feature.
std::vector<uint8_t> rom;
@ -469,7 +470,7 @@ namespace N64Recomp {
}
};
bool recompile_function(const Context& context, const Function& func, std::ofstream& output_file, std::span<std::vector<uint32_t>> static_funcs);
bool recompile_function(const Context& context, const Function& func, std::ofstream& output_file, std::span<std::vector<uint32_t>> static_funcs, bool tag_reference_relocs);
enum class ModSymbolsError {
Good,

View file

@ -103,9 +103,11 @@ std::string fpr_u64_to_string(int fpr_index) {
std::string unsigned_reloc(const N64Recomp::InstructionContext& context) {
switch (context.reloc_type) {
case N64Recomp::RelocType::R_MIPS_HI16:
return fmt::format("RELOC_HI16({}, {:#X})", context.reloc_section_index, context.reloc_target_section_offset);
return fmt::format("{}RELOC_HI16({}, {:#X})",
context.reloc_tag_as_reference ? "REF_" : "", context.reloc_section_index, context.reloc_target_section_offset);
case N64Recomp::RelocType::R_MIPS_LO16:
return fmt::format("RELOC_LO16({}, {:#X})", context.reloc_section_index, context.reloc_target_section_offset);
return fmt::format("{}RELOC_LO16({}, {:#X})",
context.reloc_tag_as_reference ? "REF_" : "", context.reloc_section_index, context.reloc_target_section_offset);
default:
throw std::runtime_error(fmt::format("Unexpected reloc type {}\n", static_cast<int>(context.reloc_type)));
}

View file

@ -345,6 +345,7 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP
if (bss_find != bss_sections_by_name.end()) {
section_out.bss_section_index = bss_find->second->get_index();
section_out.bss_size = bss_find->second->get_size();
context.bss_section_to_section[section_out.bss_section_index] = section_index;
}
if (context.has_reference_symbols() || section_out.relocatable) {
@ -427,24 +428,21 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP
return nullptr;
}
}
else if (rel_symbol_section_index == ELFIO::SHN_ABS) {
reloc_out.reference_symbol = false;
reloc_out.target_section = N64Recomp::SectionAbsolute;
rel_section_vram = 0;
}
else {
reloc_out.reference_symbol = false;
reloc_out.target_section = rel_symbol_section_index;
// Handle special sections.
if (rel_symbol_section_index >= context.sections.size()) {
switch (rel_symbol_section_index) {
case ELFIO::SHN_ABS:
rel_section_vram = 0;
break;
default:
fmt::print(stderr, "Reloc {} references symbol {} which is in an unknown section 0x{:04X}!\n",
i, rel_symbol_name, rel_symbol_section_index);
return nullptr;
}
}
else {
rel_section_vram = context.sections[rel_symbol_section_index].ram_addr;
fmt::print(stderr, "Reloc {} references symbol {} which is in an unknown section 0x{:04X}!\n",
i, rel_symbol_name, rel_symbol_section_index);
return nullptr;
}
rel_section_vram = context.sections[rel_symbol_section_index].ram_addr;
}
// Reloc pairing, see MIPS System V ABI documentation page 4-18 (https://refspecs.linuxfoundation.org/elf/mipsabi.pdf)

View file

@ -127,7 +127,7 @@ bool recompile_single_function(const N64Recomp::Context& context, const N64Recom
"\n",
recomp_include);
if (!N64Recomp::recompile_function(context, func, output_file, static_funcs_out)) {
if (!N64Recomp::recompile_function(context, func, output_file, static_funcs_out, false)) {
return false;
}
@ -617,7 +617,7 @@ int main(int argc, char** argv) {
// Recompile the function.
if (config.single_file_output || config.functions_per_output_file > 1) {
result = N64Recomp::recompile_function(context, func, current_output_file, static_funcs_by_section);
result = N64Recomp::recompile_function(context, func, current_output_file, static_funcs_by_section, false);
if (!config.single_file_output) {
cur_file_function_count++;
if (cur_file_function_count >= config.functions_per_output_file) {
@ -704,7 +704,7 @@ int main(int argc, char** argv) {
bool result;
size_t prev_num_statics = static_funcs_by_section[func.section_index].size();
if (config.single_file_output || config.functions_per_output_file > 1) {
result = N64Recomp::recompile_function(context, func, current_output_file, static_funcs_by_section);
result = N64Recomp::recompile_function(context, func, current_output_file, static_funcs_by_section, false);
if (!config.single_file_output) {
cur_file_function_count++;
if (cur_file_function_count >= config.functions_per_output_file) {

View file

@ -32,7 +32,8 @@ struct FuncV1 {
uint32_t size;
};
constexpr uint32_t SectionSelfVromV1 = 0xFFFFFFFF;
// Local section flag, if set then the reloc is pointing to a section within the mod and the vrom is the section index.
constexpr uint32_t SectionSelfVromFlagV1 = 0x80000000;
// Special sections
constexpr uint32_t SectionImportVromV1 = 0xFFFFFFFE;
@ -211,12 +212,7 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
uint16_t reloc_target_section;
uint32_t reloc_target_section_offset;
uint32_t reloc_symbol_index;
if (target_section_vrom == SectionSelfVromV1) {
reloc_target_section = N64Recomp::SectionSelf;
reloc_target_section_offset = reloc_in.target_section_offset_or_index;
reloc_symbol_index = 0; // Not used for normal relocs.
}
else if (target_section_vrom == SectionImportVromV1) {
if (target_section_vrom == SectionImportVromV1) {
reloc_target_section = N64Recomp::SectionImport;
reloc_target_section_offset = 0; // Not used for imports or reference symbols.
reloc_symbol_index = reloc_in.target_section_offset_or_index;
@ -228,6 +224,16 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
reloc_symbol_index = reloc_in.target_section_offset_or_index;
cur_reloc.reference_symbol = true;
}
else if (target_section_vrom & SectionSelfVromFlagV1) {
reloc_target_section = static_cast<uint16_t>(target_section_vrom & ~SectionSelfVromFlagV1);
reloc_target_section_offset = reloc_in.target_section_offset_or_index;
reloc_symbol_index = 0; // Not used for normal relocs.
cur_reloc.reference_symbol = false;
if (reloc_target_section >= mod_context.sections.size()) {
printf("Reloc %zu in section %zu references local section %u, but only %zu exist\n",
reloc_index, section_index, reloc_target_section, mod_context.sections.size());
}
}
else {
// TODO lookup by section index by original vrom
auto find_section_it = sections_by_vrom.find(target_section_vrom);
@ -593,11 +599,14 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
vec_put(ret, &func_out);
}
for (const Reloc& cur_reloc : cur_section.relocs) {
for (size_t reloc_index = 0; reloc_index < cur_section.relocs.size(); reloc_index++) {
const Reloc& cur_reloc = cur_section.relocs[reloc_index];
uint32_t target_section_vrom;
uint32_t target_section_offset_or_index = cur_reloc.target_section_offset;
if (cur_reloc.target_section == SectionSelf) {
target_section_vrom = SectionSelfVromV1;
if (cur_reloc.target_section == SectionAbsolute) {
printf("Internal error: reloc %zu in section %zu references an absolute symbol and should have been relocated already\n",
reloc_index, section_index);
return {};
}
else if (cur_reloc.target_section == SectionImport) {
target_section_vrom = SectionImportVromV1;
@ -611,7 +620,12 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& cont
target_section_vrom = context.get_reference_section_rom(cur_reloc.target_section);
}
else {
target_section_vrom = context.sections[cur_reloc.target_section].rom_addr;
if (cur_reloc.target_section >= context.sections.size()) {
printf("Internal error: reloc %zu in section %zu references section %u, but only %zu exist\n",
reloc_index, section_index, cur_reloc.target_section, context.sections.size());
return {};
}
target_section_vrom = SectionSelfVromFlagV1 | cur_reloc.target_section;
}
RelocV1 reloc_out {
.section_offset = cur_reloc.address - cur_section.ram_addr,

View file

@ -110,7 +110,7 @@ std::string_view ctx_gpr_prefix(int reg) {
}
// Major TODO, this function grew very organically and needs to be cleaned up. Ideally, it'll get split up into some sort of lookup table grouped by similar instruction types.
bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Function& func, const N64Recomp::FunctionStats& stats, const std::unordered_set<uint32_t>& skipped_insns, size_t instr_index, const std::vector<rabbitizer::InstructionCpu>& instructions, std::ofstream& output_file, bool indent, bool emit_link_branch, int link_branch_index, size_t reloc_index, bool& needs_link_branch, bool& is_branch_likely, std::span<std::vector<uint32_t>> static_funcs_out) {
bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Function& func, const N64Recomp::FunctionStats& stats, const std::unordered_set<uint32_t>& skipped_insns, size_t instr_index, const std::vector<rabbitizer::InstructionCpu>& instructions, std::ofstream& output_file, bool indent, bool emit_link_branch, int link_branch_index, size_t reloc_index, bool& needs_link_branch, bool& is_branch_likely, bool tag_reference_relocs, std::span<std::vector<uint32_t>> static_funcs_out) {
using namespace N64Recomp;
const auto& section = context.sections[func.section_index];
@ -158,48 +158,50 @@ bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Fun
// Get the reloc data for this instruction
const auto& reloc = section.relocs[reloc_index];
reloc_section = reloc.target_section;
// Only process this relocation if this section is relocatable or if this relocation targets a reference symbol.
if (section.relocatable || reloc.reference_symbol) {
// Ignore this reloc if it points to a different section.
// Also check if the reloc points to the bss section since that will also be relocated with the section.
// Additionally, always process reference symbol relocations.
if (reloc_section == func.section_index || reloc_section == section.bss_section_index || reloc_section == N64Recomp::SectionSelf || reloc.reference_symbol) {
// Record the reloc's data.
reloc_type = reloc.type;
reloc_target_section_offset = reloc.target_section_offset;
// Ignore all relocs that aren't HI16 or LO16.
if (reloc_type == N64Recomp::RelocType::R_MIPS_HI16 || reloc_type == N64Recomp::RelocType::R_MIPS_LO16 || reloc_type == N64Recomp::RelocType::R_MIPS_26) {
if (reloc.reference_symbol) {
reloc_reference_symbol = reloc.symbol_index;
// Don't try to relocate special section symbols.
if (context.is_regular_reference_section(reloc.target_section)) {
bool ref_section_relocatable = context.is_reference_section_relocatable(reloc.target_section);
uint32_t ref_section_vram = context.get_reference_section_vram(reloc.target_section);
// Resolve HI16 and LO16 reference symbol relocs to non-relocatable sections by patching the instruction immediate.
if (!ref_section_relocatable && (reloc_type == N64Recomp::RelocType::R_MIPS_HI16 || reloc_type == N64Recomp::RelocType::R_MIPS_LO16)) {
uint32_t full_immediate = reloc.target_section_offset + ref_section_vram;
if (reloc_type == N64Recomp::RelocType::R_MIPS_HI16) {
imm = (full_immediate >> 16) + ((full_immediate >> 15) & 1);
reloc_type = N64Recomp::RelocType::R_MIPS_NONE;
}
else if (reloc_type == N64Recomp::RelocType::R_MIPS_LO16) {
imm = full_immediate & 0xFFFF;
reloc_type = N64Recomp::RelocType::R_MIPS_NONE;
}
// Check if the relocation references a relocatable section.
bool target_relocatable = false;
if (!reloc.reference_symbol && reloc_section != N64Recomp::SectionAbsolute) {
const auto& target_section = context.sections[reloc_section];
target_relocatable = target_section.relocatable;
}
// The reloc has been processed, so set it to none to prevent it getting processed a second time during instruction code generation.
reloc_type = N64Recomp::RelocType::R_MIPS_NONE;
reloc_reference_symbol = (size_t)-1;
// Only process this relocation if the target section is relocatable or if this relocation targets a reference symbol.
if (target_relocatable || reloc.reference_symbol) {
// Record the reloc's data.
reloc_type = reloc.type;
reloc_target_section_offset = reloc.target_section_offset;
// Ignore all relocs that aren't MIPS_HI16, MIPS_LO16 or MIPS_26.
if (reloc_type == N64Recomp::RelocType::R_MIPS_HI16 || reloc_type == N64Recomp::RelocType::R_MIPS_LO16 || reloc_type == N64Recomp::RelocType::R_MIPS_26) {
if (reloc.reference_symbol) {
reloc_reference_symbol = reloc.symbol_index;
// Don't try to relocate special section symbols.
if (context.is_regular_reference_section(reloc.target_section) || reloc_section == N64Recomp::SectionAbsolute) {
bool ref_section_relocatable = context.is_reference_section_relocatable(reloc.target_section);
uint32_t ref_section_vram = context.get_reference_section_vram(reloc.target_section);
// Resolve HI16 and LO16 reference symbol relocs to non-relocatable sections by patching the instruction immediate.
if (!ref_section_relocatable && (reloc_type == N64Recomp::RelocType::R_MIPS_HI16 || reloc_type == N64Recomp::RelocType::R_MIPS_LO16)) {
uint32_t full_immediate = reloc.target_section_offset + ref_section_vram;
if (reloc_type == N64Recomp::RelocType::R_MIPS_HI16) {
imm = (full_immediate >> 16) + ((full_immediate >> 15) & 1);
}
else if (reloc_type == N64Recomp::RelocType::R_MIPS_LO16) {
imm = full_immediate & 0xFFFF;
}
// The reloc has been processed, so set it to none to prevent it getting processed a second time during instruction code generation.
reloc_type = N64Recomp::RelocType::R_MIPS_NONE;
reloc_reference_symbol = (size_t)-1;
}
}
}
}
// Repoint bss relocations at their non-bss counterpart section.
if (reloc_section != N64Recomp::SectionSelf && reloc_section == section.bss_section_index) {
reloc_section = func.section_index;
}
// Repoint bss relocations at their non-bss counterpart section.
auto find_bss_it = context.bss_section_to_section.find(reloc_section);
if (find_bss_it != context.bss_section_to_section.end()) {
reloc_section = find_bss_it->second;
}
}
}
@ -219,7 +221,7 @@ bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Fun
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
next_reloc_index++;
}
if (!process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, false, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out)) {
if (!process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, false, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, tag_reference_relocs, static_funcs_out)) {
return false;
}
}
@ -333,7 +335,7 @@ bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Fun
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
next_reloc_index++;
}
if (!process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, true, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out)) {
if (!process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, true, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, tag_reference_relocs, static_funcs_out)) {
return false;
}
}
@ -503,7 +505,7 @@ bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Fun
if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) {
next_reloc_index++;
}
if (!process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, false, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out)) {
if (!process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, false, false, link_branch_index, next_reloc_index, dummy_needs_link_branch, dummy_is_branch_likely, tag_reference_relocs, static_funcs_out)) {
return false;
}
print_indent();
@ -582,6 +584,7 @@ bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Fun
instruction_context.ft = ft;
instruction_context.cop1_cs = cop1_cs;
instruction_context.imm16 = imm;
instruction_context.reloc_tag_as_reference = (reloc_reference_symbol != (size_t)-1) && tag_reference_relocs;
instruction_context.reloc_type = reloc_type;
instruction_context.reloc_section_index = reloc_section;
instruction_context.reloc_target_section_offset = reloc_target_section_offset;
@ -730,7 +733,7 @@ bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Fun
return true;
}
bool N64Recomp::recompile_function(const N64Recomp::Context& context, const N64Recomp::Function& func, std::ofstream& output_file, std::span<std::vector<uint32_t>> static_funcs_out) {
bool N64Recomp::recompile_function(const N64Recomp::Context& context, const N64Recomp::Function& func, std::ofstream& output_file, std::span<std::vector<uint32_t>> static_funcs_out, bool tag_reference_relocs) {
//fmt::print("Recompiling {}\n", func.name);
std::vector<rabbitizer::InstructionCpu> instructions;
@ -813,7 +816,7 @@ bool N64Recomp::recompile_function(const N64Recomp::Context& context, const N64R
}
// Process the current instruction and check for errors
if (process_instruction(context, func, stats, skipped_insns, instr_index, instructions, output_file, false, needs_link_branch, num_link_branches, reloc_index, needs_link_branch, is_branch_likely, static_funcs_out) == false) {
if (process_instruction(context, func, stats, skipped_insns, instr_index, instructions, output_file, false, needs_link_branch, num_link_branches, reloc_index, needs_link_branch, is_branch_likely, tag_reference_relocs, static_funcs_out) == false) {
fmt::print(stderr, "Error in recompiling {}, clearing output file\n", func.name);
output_file.clear();
return false;