diff --git a/include/recomp_port.h b/include/recomp_port.h index 1b440be..03fba2c 100644 --- a/include/recomp_port.h +++ b/include/recomp_port.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "elfio/elfio.hpp" #ifdef _MSC_VER @@ -46,13 +47,35 @@ namespace RecompPort { bool reimplemented; }; + enum class RelocType : uint8_t { + 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, + }; + + struct Reloc { + uint32_t address; + uint32_t target_address; + uint32_t symbol_index; + uint32_t target_section; + RelocType type; + bool needs_relocation; + }; + struct Section { ELFIO::Elf_Xword rom_addr; ELFIO::Elf64_Addr ram_addr; ELFIO::Elf_Xword size; std::vector function_addrs; + std::vector relocs; std::string name; bool executable; + bool relocatable; }; struct FunctionStats { @@ -68,6 +91,8 @@ namespace RecompPort { std::vector rom; // A list of the list of each function (by index in `functions`) in a given section std::vector> section_functions; + // The section names that were specified as relocatable + std::unordered_set relocatable_sections; int executable_section_count; Context(const ELFIO::elfio& elf_file) { diff --git a/recomp.h b/recomp.h index 1243b2c..9db6594 100644 --- a/recomp.h +++ b/recomp.h @@ -187,6 +187,20 @@ recomp_func_t* get_function(int32_t vram); #define LOOKUP_FUNC(val) \ get_function((int32_t)(val)) +extern int32_t section_addresses[]; + +#define LO16(x) \ + ((x) & 0xFFFF) + +#define HI16(x) \ + (((x) >> 16) + (((x) >> 15) & 1)) + +#define RELOC_HI16(section_index, offset) \ + HI16(section_addresses[section_index] + (offset)) + +#define RELOC_LO16(section_index, offset) \ + LO16(section_addresses[section_index] + (offset)) + // For the Mario Party games (not working) //// This has to be in this file so it can be inlined //struct jmp_buf_storage { diff --git a/sections.h b/sections.h index 0aebde2..1d1b228 100644 --- a/sections.h +++ b/sections.h @@ -17,6 +17,7 @@ typedef struct { uint32_t size; FuncEntry *funcs; size_t num_funcs; + size_t index; } SectionTableEntry; #endif diff --git a/src/main.cpp b/src/main.cpp index 65f41cd..a056892 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -658,6 +658,8 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio& } ); + std::unordered_map reloc_sections_by_name; + // Iterate over every section to record rom addresses and find the symbol table fmt::print("Sections\n"); for (const auto& section : elf_file.sections) { @@ -666,10 +668,33 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio& // Set the rom address of this section to the current accumulated ROM size section_out.ram_addr = section->get_address(); section_out.size = section->get_size(); + ELFIO::Elf_Word type = section->get_type(); + std::string section_name = section->get_name(); + + // Check if this section is the symbol table and record it if so + if (type == ELFIO::SHT_SYMTAB) { + symtab_section = section.get(); + } + + // Check if this section is a reloc section + if (type == ELFIO::SHT_REL) { + // If it is, determine the name of the section it relocates + if (!section_name.starts_with(".rel")) { + fmt::print(stderr, "Could not determine corresponding section for reloc section {}\n", section_name.c_str()); + return nullptr; + } + + std::string reloc_target_section = section_name.substr(strlen(".rel")); + + // If this reloc section is for a section that has been marked as relocatable, record it in the reloc section lookup + if (context.relocatable_sections.contains(reloc_target_section)) { + reloc_sections_by_name[reloc_target_section] = section.get(); + } + } // If this section isn't bss (SHT_NOBITS) and ends up in the rom (SHF_ALLOC), // find this section's rom address and copy it into the rom - if (section->get_type() != ELFIO::SHT_NOBITS && section->get_flags() & ELFIO::SHF_ALLOC) { + if (type != ELFIO::SHT_NOBITS && section->get_flags() & ELFIO::SHF_ALLOC) { // Find the segment this section is in to determine the physical (rom) address of the section auto segment_it = std::upper_bound(segments.begin(), segments.end(), section->get_offset(), [](ELFIO::Elf64_Off section_offset, const SegmentEntry& segment) { @@ -677,7 +702,7 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio& } ); if (segment_it == segments.begin()) { - fmt::print(stderr, "Could not find segment that section {} belongs to!\n", section->get_name().c_str()); + fmt::print(stderr, "Could not find segment that section {} belongs to!\n", section_name.c_str()); return nullptr; } // Upper bound returns the iterator after the element we're looking for, so rewind by one @@ -685,7 +710,7 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio& const SegmentEntry& segment = *(segment_it - 1); // Check to be sure that the section is actually in this segment if (section->get_offset() >= segment.data_offset + segment.memory_size) { - fmt::print(stderr, "Section {} out of range of segment at offset 0x{:08X}\n", section->get_name().c_str(), segment.data_offset); + fmt::print(stderr, "Section {} out of range of segment at offset 0x{:08X}\n", section_name.c_str(), segment.data_offset); return nullptr; } // Calculate the rom address based on this section's offset into the segment and the segment's rom address @@ -706,15 +731,123 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio& section_out.executable = true; context.executable_section_count++; } - section_out.name = section->get_name(); + section_out.name = section_name; } - // Find the symbol table - for (const auto& section : elf_file.sections) { - // Check if this section is the symbol table and record it if so - if (section->get_type() == ELFIO::SHT_SYMTAB) { - symtab_section = section.get(); + + if (symtab_section == nullptr) { + fmt::print(stderr, "No symtab section found\n"); + return nullptr; + } + + ELFIO::symbol_section_accessor symbol_accessor{ elf_file, symtab_section }; + auto num_syms = symbol_accessor.get_symbols_num(); + + // TODO make sure that a reloc section was found for every section marked as relocatable + + // Process reloc sections + for (RecompPort::Section §ion_out : context.sections) { + // Check if a reloc section was found that corresponds with this section + auto reloc_find = reloc_sections_by_name.find(section_out.name); + if (reloc_find != reloc_sections_by_name.end()) { + // Mark the section as relocatable + section_out.relocatable = true; + // Create an accessor for the reloc section + ELFIO::relocation_section_accessor rel_accessor{ elf_file, reloc_find->second }; + // Allocate space for the relocs in this section + section_out.relocs.resize(rel_accessor.get_entries_num()); + // Track whether the previous reloc was a HI16 and its previous full_immediate + bool prev_hi = false; + uint32_t prev_hi_immediate = 0; + uint32_t prev_hi_symbol = std::numeric_limits::max(); + + for (size_t i = 0; i < section_out.relocs.size(); i++) { + // Get the current reloc + ELFIO::Elf64_Addr rel_offset; + ELFIO::Elf_Word rel_symbol; + unsigned int rel_type; + ELFIO::Elf_Sxword bad_rel_addend; // Addends aren't encoded in the reloc, so ignore this one + rel_accessor.get_entry(i, rel_offset, rel_symbol, rel_type, bad_rel_addend); + + RecompPort::Reloc& reloc_out = section_out.relocs[i]; + + // Get the real full_immediate by extracting the immediate from the instruction + uint32_t instr_word = byteswap(*reinterpret_cast(context.rom.data() + section_out.rom_addr + rel_offset - section_out.ram_addr)); + rabbitizer::InstructionCpu instr{ instr_word, static_cast(rel_offset) }; + //context.rom section_out.rom_addr; + + reloc_out.address = rel_offset; + reloc_out.symbol_index = rel_symbol; + reloc_out.type = static_cast(rel_type); + reloc_out.needs_relocation = false; + + std::string rel_symbol_name; + ELFIO::Elf64_Addr rel_symbol_value; + ELFIO::Elf_Xword rel_symbol_size; + unsigned char rel_symbol_bind; + unsigned char rel_symbol_type; + ELFIO::Elf_Half rel_symbol_section_index; + unsigned char rel_symbol_other; + + bool found_rel_symbol = symbol_accessor.get_symbol( + rel_symbol, rel_symbol_name, rel_symbol_value, rel_symbol_size, rel_symbol_bind, rel_symbol_type, rel_symbol_section_index, rel_symbol_other); + + reloc_out.target_section = rel_symbol_section_index; + + bool rel_needs_relocation = false; + + if (rel_symbol_section_index < context.sections.size()) { + rel_needs_relocation = context.sections[rel_symbol_section_index].relocatable; + } + + // Reloc pairing, see MIPS System V ABI documentation page 4-18 (https://refspecs.linuxfoundation.org/elf/mipsabi.pdf) + if (reloc_out.type == RecompPort::RelocType::R_MIPS_LO16) { + if (prev_hi) { + if (prev_hi_symbol != rel_symbol) { + fmt::print(stderr, "[WARN] Paired HI16 and LO16 relocations have different symbols\n" + " LO16 reloc index {} in section {} referencing symbol {} with offset 0x{:08X}\n", + i, section_out.name, reloc_out.symbol_index, reloc_out.address); + } + uint32_t rel_immediate = instr.getProcessedImmediate(); + uint32_t full_immediate = (prev_hi_immediate << 16) + (int16_t)rel_immediate; + + + // Set this and the previous HI16 relocs' relocated addresses + section_out.relocs[i - 1].target_address = full_immediate; + reloc_out.target_address = full_immediate; + } + } else { + if (prev_hi) { + fmt::print(stderr, "Unpaired HI16 reloc index {} in section {} referencing symbol {} with offset 0x{:08X}\n", + i - 1, section_out.name, section_out.relocs[i - 1].symbol_index, section_out.relocs[i - 1].address); + return nullptr; + } + } + + if (reloc_out.type == RecompPort::RelocType::R_MIPS_HI16) { + uint32_t rel_immediate = instr.getProcessedImmediate(); + prev_hi = true; + prev_hi_immediate = rel_immediate; + prev_hi_symbol = rel_symbol; + } else { + prev_hi = false; + } + + if (reloc_out.type == RecompPort::RelocType::R_MIPS_32) { + // Nothing to do here + } + } + + // Sort this section's relocs by address, which allows for binary searching and more efficient iteration during recompilation. + // This is safe to do as the entire full_immediate in present in relocs due to the pairing that was done earlier, so the HI16 does not + // need to directly preceed the matching LO16 anymore. + std::sort(section_out.relocs.begin(), section_out.relocs.end(), + [](const RecompPort::Reloc& a, const RecompPort::Reloc& b) { + return a.address < b.address; + } + ); } } + return symtab_section; } @@ -748,9 +881,22 @@ void analyze_sections(RecompPort::Context& context, const ELFIO::elfio& elf_file ); } +bool read_list_file(const char* filename, std::unordered_set& entries_out) { + std::ifstream input_file{ filename }; + if (!input_file.good()) { + return false; + } + + std::string entry; + + while (input_file >> entry) { + entries_out.emplace(std::move(entry)); + } +} + int main(int argc, char** argv) { - if (argc != 3) { - fmt::print("Usage: {} [input elf file] [entrypoint RAM address]\n", argv[0]); + if (argc < 3 || argc > 4) { + fmt::print("Usage: {} [input elf file] [entrypoint RAM address] [relocatable sections list file (optional)]\n", argv[0]); std::exit(EXIT_SUCCESS); } @@ -765,6 +911,14 @@ int main(int argc, char** argv) { std::exit(EXIT_FAILURE); }; + std::unordered_set relocatable_sections{}; + + if (argc == 4) { + if (!read_list_file(argv[3], relocatable_sections)) { + exit_failure("Failed to load the relocatable section list file: " + std::string(argv[3]) + "\n"); + } + } + std::string elf_name{ argv[1] }; if (!elf_file.load(elf_name)) { @@ -786,6 +940,7 @@ int main(int argc, char** argv) { } RecompPort::Context context{ elf_file }; + context.relocatable_sections = std::move(relocatable_sections); // Read all of the sections in the elf and look for the symbol table section ELFIO::section* symtab_section = read_sections(context, elf_file); @@ -840,7 +995,7 @@ int main(int argc, char** argv) { "void {}(uint8_t* restrict rdram, recomp_context* restrict ctx);\n", func.name); //fmt::print(lookup_file, // " {{ 0x{:08X}u, {} }},\n", func.vram, func.name); - if (RecompPort::recompile_function(context, func, output_dir + "ignore.txt"/*func.name + ".c"*/, static_funcs_by_section) == false) { + if (RecompPort::recompile_function(context, func, output_dir + func.name + ".c", static_funcs_by_section) == false) { //lookup_file.clear(); fmt::print(stderr, "Error recompiling {}\n", func.name); std::exit(EXIT_FAILURE); @@ -957,8 +1112,8 @@ int main(int argc, char** argv) { std::string section_funcs_array_name = fmt::format("section_{}_{}_funcs", section_index, section_name_trimmed); - section_load_table += fmt::format(" {{ .rom_addr = 0x{0:08X}, .ram_addr = 0x{1:08X}, .size = 0x{2:08X}, .funcs = {3}, .num_funcs = ARRLEN({3}) }},\n", - section.rom_addr, section.ram_addr, section.size, section_funcs_array_name); + section_load_table += fmt::format(" {{ .rom_addr = 0x{0:08X}, .ram_addr = 0x{1:08X}, .size = 0x{2:08X}, .funcs = {3}, .num_funcs = ARRLEN({3}), .index = {4} }},\n", + section.rom_addr, section.ram_addr, section.size, section_funcs_array_name, section_index); fmt::print(overlay_file, "static FuncEntry {}[] = {{\n", section_funcs_array_name); diff --git a/src/recompilation.cpp b/src/recompilation.cpp index 182edb0..95c943b 100644 --- a/src/recompilation.cpp +++ b/src/recompilation.cpp @@ -17,7 +17,8 @@ std::string_view ctx_gpr_prefix(int reg) { return ""; } -bool process_instruction(const RecompPort::Context& context, const RecompPort::Function& func, const RecompPort::FunctionStats& stats, const std::unordered_set& skipped_insns, size_t instr_index, const std::vector& instructions, std::ofstream& output_file, bool indent, bool emit_link_branch, int link_branch_index, bool& needs_link_branch, bool& is_branch_likely, std::span> static_funcs_out) { +bool process_instruction(const RecompPort::Context& context, const RecompPort::Function& func, const RecompPort::FunctionStats& stats, const std::unordered_set& skipped_insns, size_t instr_index, const std::vector& 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> static_funcs_out) { + const auto& section = context.sections[func.section_index]; const auto& instr = instructions[instr_index]; needs_link_branch = false; is_branch_likely = false; @@ -37,6 +38,40 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F return true; } + + bool at_reloc = false; + bool reloc_handled = false; + RecompPort::RelocType reloc_type = RecompPort::RelocType::R_MIPS_NONE; + uint32_t reloc_section = 0; + uint32_t reloc_target_section_offset = 0; + + // Check if this instruction has a reloc. + if (section.relocatable && section.relocs[reloc_index].address == instr_vram) { + // Get the reloc data for this instruction + const auto& reloc = section.relocs[reloc_index]; + reloc_section = reloc.target_section; + // Some symbols are in a nonexistent section (e.g. absolute symbols), so check that the section is valid before doing anything else. + // Absolute symbols will never need to be relocated so it's safe to skip this. + if (reloc_section < context.sections.size()) { + // Check if the target section is also relocatable. References to symbols that are in a non-relocatable section don't need to + // be relocated, so we can skip those. + if (context.sections[reloc_section].relocatable) { + // For games where references between sections aren't relocated, ignore this reloc if it points to a different section + // TODO expose this as a config option + // TODO!!!!! also relocate references to the corresponding bss section!!!!! + if (reloc_section == func.section_index) { + // Record the reloc's data. + reloc_type = reloc.type; + reloc_target_section_offset = reloc.target_address - context.sections[reloc_section].ram_addr; + // Ignore all relocs that aren't HI16 or LO16. + if (reloc_type == RecompPort::RelocType::R_MIPS_HI16 || reloc_type == RecompPort::RelocType::R_MIPS_LO16) { + at_reloc = true; + } + } + } + } + } + auto print_indent = [&]() { fmt::print(output_file, " "); }; @@ -56,7 +91,12 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F if (instr_index < instructions.size() - 1) { bool dummy_needs_link_branch; bool dummy_is_branch_likely; - process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, false, false, link_branch_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out); + size_t next_reloc_index = reloc_index; + uint32_t next_vram = instr_vram + 4; + if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) { + next_reloc_index++; + } + 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); } print_indent(); fmt::print(output_file, fmt_str, args...); @@ -72,7 +112,12 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F if (instr_index < instructions.size() - 1) { bool dummy_needs_link_branch; bool dummy_is_branch_likely; - process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, true, false, link_branch_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out); + size_t next_reloc_index = reloc_index; + uint32_t next_vram = instr_vram + 4; + if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) { + next_reloc_index++; + } + 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); } fmt::print(output_file, " "); fmt::print(output_file, fmt_str, args...); @@ -98,13 +143,34 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F uint16_t imm = instr.Get_immediate(); + std::string unsigned_imm_string; + std::string signed_imm_string; + + if (!at_reloc) { + unsigned_imm_string = fmt::format("{:#X}", imm); + signed_imm_string = fmt::format("{:#X}", (int16_t)imm); + } else { + switch (reloc_type) { + case RecompPort::RelocType::R_MIPS_HI16: + unsigned_imm_string = fmt::format("RELOC_HI16({}, {:#X})", reloc_section, reloc_target_section_offset); + signed_imm_string = "(int16_t)" + unsigned_imm_string; + reloc_handled = true; + break; + case RecompPort::RelocType::R_MIPS_LO16: + unsigned_imm_string = fmt::format("RELOC_LO16({}, {:#X})", reloc_section, reloc_target_section_offset); + signed_imm_string = "(int16_t)" + unsigned_imm_string; + reloc_handled = true; + break; + } + } + switch (instr.getUniqueId()) { case InstrId::cpu_nop: fmt::print(output_file, "\n"); break; // Arithmetic case InstrId::cpu_lui: - print_line("{}{} = S32({:#X} << 16)", ctx_gpr_prefix(rt), rt, imm); + print_line("{}{} = S32({} << 16)", ctx_gpr_prefix(rt), rt, unsigned_imm_string); break; case InstrId::cpu_add: case InstrId::cpu_addu: @@ -132,19 +198,19 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F break; case InstrId::cpu_addi: case InstrId::cpu_addiu: - print_line("{}{} = ADD32({}{}, {:#X})", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, (int16_t)imm); + print_line("{}{} = ADD32({}{}, {})", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string); break; case InstrId::cpu_and: print_line("{}{} = {}{} & {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); break; case InstrId::cpu_andi: - print_line("{}{} = {}{} & {:#X}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, imm); + print_line("{}{} = {}{} & {}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, unsigned_imm_string); break; case InstrId::cpu_or: print_line("{}{} = {}{} | {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); break; case InstrId::cpu_ori: - print_line("{}{} = {}{} | {:#X}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, imm); + print_line("{}{} = {}{} | {}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, unsigned_imm_string); break; case InstrId::cpu_nor: print_line("{}{} = ~({}{} | {}{})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); @@ -153,7 +219,7 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F print_line("{}{} = {}{} ^ {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); break; case InstrId::cpu_xori: - print_line("{}{} = {}{} ^ {:#X}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, imm); + print_line("{}{} = {}{} ^ {}", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, unsigned_imm_string); break; case InstrId::cpu_sll: print_line("{}{} = S32({}{}) << {}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa); @@ -177,13 +243,13 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F print_line("{}{} = SIGNED({}{}) < SIGNED({}{}) ? 1 : 0", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); break; case InstrId::cpu_slti: - print_line("{}{} = SIGNED({}{}) < {:#X} ? 1 : 0", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, (int16_t)imm); + print_line("{}{} = SIGNED({}{}) < {} ? 1 : 0", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string); break; case InstrId::cpu_sltu: print_line("{}{} = {}{} < {}{} ? 1 : 0", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); break; case InstrId::cpu_sltiu: - print_line("{}{} = {}{} < {:#X} ? 1 : 0", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, (int16_t)imm); + print_line("{}{} = {}{} < {} ? 1 : 0", ctx_gpr_prefix(rt), rt, ctx_gpr_prefix(rs), rs, signed_imm_string); break; case InstrId::cpu_mult: print_line("result = S64({}{}) * S64({}{}); lo = S32(result >> 0); hi = S32(result >> 32)", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); @@ -207,29 +273,29 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F // Loads // TODO ld case InstrId::cpu_lw: - print_line("{}{} = MEM_W({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); + print_line("{}{} = MEM_W({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base); break; case InstrId::cpu_lh: - print_line("{}{} = MEM_H({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); + print_line("{}{} = MEM_H({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base); break; case InstrId::cpu_lb: - print_line("{}{} = MEM_B({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); + print_line("{}{} = MEM_B({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base); break; case InstrId::cpu_lhu: - print_line("{}{} = MEM_HU({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); + print_line("{}{} = MEM_HU({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base); break; case InstrId::cpu_lbu: - print_line("{}{} = MEM_BU({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); + print_line("{}{} = MEM_BU({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base); break; // Stores case InstrId::cpu_sw: - print_line("MEM_W({:#X}, {}{}) = {}{}", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); + print_line("MEM_W({}, {}{}) = {}{}", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); break; case InstrId::cpu_sh: - print_line("MEM_H({:#X}, {}{}) = {}{}", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); + print_line("MEM_H({}, {}{}) = {}{}", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); break; case InstrId::cpu_sb: - print_line("MEM_B({:#X}, {}{}) = {}{}", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); + print_line("MEM_B({}, {}{}) = {}{}", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); break; // TODO lwl, lwr // examples: @@ -246,20 +312,20 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F // LWR x + 2 -> 00000000 0189ABCD // LWR x + 3 -> FFFFFFFF 89ABCDEF case InstrId::cpu_lwl: - print_line("{}{} = do_lwl(rdram, {:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); - //print_line("{}{} = MEM_WL({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); + print_line("{}{} = do_lwl(rdram, {}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base); + //print_line("{}{} = MEM_WL({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base); break; case InstrId::cpu_lwr: - //print_line("{}{} = do_lwr(rdram, {:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); - //print_line("//{}{} = MEM_WR({:#X}, {}{})", ctx_gpr_prefix(rt), rt, (int16_t)imm, ctx_gpr_prefix(base), base); + //print_line("{}{} = do_lwr(rdram, {}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base); + //print_line("//{}{} = MEM_WR({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base); break; case InstrId::cpu_swl: - print_line("do_swl(rdram, {:#X}, {}{}, {}{})", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); - //print_line("MEM_WL({:#X}, {}{}) = {}{}", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); + print_line("do_swl(rdram, {}, {}{}, {}{})", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); + //print_line("MEM_WL({}, {}{}) = {}{}", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); break; case InstrId::cpu_swr: - //print_line("do_swr(rdram, {:#X}, {}{}, {}{})", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); - //print_line("//MEM_WR({:#X}, {}{}) = {}{}", (int16_t)imm, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); + //print_line("do_swr(rdram, {}, {}{}, {}{})", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); + //print_line("//MEM_WR({}, {}{}) = {}{}", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt); break; // Branches @@ -356,7 +422,12 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F if (jtbl_find_result != stats.jump_tables.end()) { const RecompPort::JumpTable& cur_jtbl = *jtbl_find_result; bool dummy_needs_link_branch, dummy_is_branch_likely; - process_instruction(context, func, stats, skipped_insns, instr_index + 1, instructions, output_file, false, false, link_branch_index, dummy_needs_link_branch, dummy_is_branch_likely, static_funcs_out); + size_t next_reloc_index = reloc_index; + uint32_t next_vram = instr_vram + 4; + if (reloc_index + 1 < section.relocs.size() && next_vram > section.relocs[reloc_index].address) { + next_reloc_index++; + } + 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); print_indent(); fmt::print(output_file, "switch (jr_addend_{:08X} >> 2) {{\n", cur_jtbl.jr_vram); for (size_t entry_index = 0; entry_index < cur_jtbl.entries.size(); entry_index++) { @@ -471,16 +542,16 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F case InstrId::cpu_lwc1: if ((ft & 1) == 0) { // even fpr - print_line("ctx->f{}.u32l = MEM_W({:#X}, {}{})", ft, (int16_t)imm, ctx_gpr_prefix(base), base); + print_line("ctx->f{}.u32l = MEM_W({}, {}{})", ft, signed_imm_string, ctx_gpr_prefix(base), base); print_line("NAN_CHECK(ctx->f{}.fl)", ft); } else { // odd fpr - print_line("ctx->f{}.u32h = MEM_W({:#X}, {}{})", ft - 1, (int16_t)imm, ctx_gpr_prefix(base), base); + print_line("ctx->f{}.u32h = MEM_W({}, {}{})", ft - 1, signed_imm_string, ctx_gpr_prefix(base), base); } break; case InstrId::cpu_ldc1: if ((ft & 1) == 0) { - print_line("ctx->f{}.u64 = LD({:#X}, {}{})", ft, (int16_t)imm, ctx_gpr_prefix(base), base); + print_line("ctx->f{}.u64 = LD({}, {}{})", ft, signed_imm_string, ctx_gpr_prefix(base), base); print_line("NAN_CHECK(ctx->f{}.d)", ft); } else { fmt::print(stderr, "Invalid operand for ldc1: f{}\n", ft); @@ -490,15 +561,15 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F case InstrId::cpu_swc1: if ((ft & 1) == 0) { // even fpr - print_line("MEM_W({:#X}, {}{}) = ctx->f{}.u32l", (int16_t)imm, ctx_gpr_prefix(base), base, ft); + print_line("MEM_W({}, {}{}) = ctx->f{}.u32l", signed_imm_string, ctx_gpr_prefix(base), base, ft); } else { // odd fpr - print_line("MEM_W({:#X}, {}{}) = ctx->f{}.u32h", (int16_t)imm, ctx_gpr_prefix(base), base, ft - 1); + print_line("MEM_W({}, {}{}) = ctx->f{}.u32h", signed_imm_string, ctx_gpr_prefix(base), base, ft - 1); } break; case InstrId::cpu_sdc1: if ((ft & 1) == 0) { - print_line("SD(ctx->f{}.u64, {:#X}, {}{})", ft, (int16_t)imm, ctx_gpr_prefix(base), base); + print_line("SD(ctx->f{}.u64, {}, {}{})", ft, signed_imm_string, ctx_gpr_prefix(base), base); } else { fmt::print(stderr, "Invalid operand for sdc1: f{}\n", ft); return false; @@ -955,6 +1026,9 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re int num_likely_branches = 0; bool needs_link_branch = false; bool in_likely_delay_slot = false; + const auto& section = context.sections[func.section_index]; + bool needs_reloc = section.relocatable; + size_t reloc_index = 0; for (size_t instr_index = 0; instr_index < instructions.size(); ++instr_index) { bool had_link_branch = needs_link_branch; bool is_branch_likely = false; @@ -967,8 +1041,16 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re fmt::print(output_file, "L_{:08X}:\n", *cur_label); ++cur_label; } + + // If this is a relocatable section, advance the reloc index until we reach the last one or until we get to/pass the current instruction + if (needs_reloc) { + while (reloc_index < (section.relocs.size() - 1) && section.relocs[reloc_index].address < vram) { + reloc_index++; + } + } + // 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, 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, static_funcs_out) == false) { fmt::print(stderr, "Error in recompilation, clearing {}\n", output_path); output_file.clear(); return false; diff --git a/test/RecompFuncs/RecompFuncs.vcxproj b/test/RecompFuncs/RecompFuncs.vcxproj index e0bd0b8..a3d947e 100644 --- a/test/RecompFuncs/RecompFuncs.vcxproj +++ b/test/RecompFuncs/RecompFuncs.vcxproj @@ -168,7 +168,6 @@ - <_WildCardClCompile Include="..\funcs\*.c" /> diff --git a/test/RecompFuncs/RecompFuncs.vcxproj.filters b/test/RecompFuncs/RecompFuncs.vcxproj.filters index 52ec329..c052c85 100644 --- a/test/RecompFuncs/RecompFuncs.vcxproj.filters +++ b/test/RecompFuncs/RecompFuncs.vcxproj.filters @@ -15,9 +15,6 @@ - - Source Files - diff --git a/test/src/overlays.cpp b/test/src/overlays.cpp index d8f6f7c..3e2dc5a 100644 --- a/test/src/overlays.cpp +++ b/test/src/overlays.cpp @@ -27,6 +27,12 @@ void load_overlay(size_t section_table_index, int32_t ram) { func_map[ram + func.offset] = func.func; } loaded_sections.emplace_back(ram, section_table_index); + section_addresses[section.index] = ram; +} + + +extern "C" { +int32_t section_addresses[num_sections]; } extern "C" void load_overlays(uint32_t rom, int32_t ram_addr, uint32_t size) { @@ -69,6 +75,8 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) { uint32_t func_address = func.offset + it->loaded_ram_addr; func_map.erase(func_address); } + // Reset the section's address in the address table + section_addresses[section.index] = 0; // Remove the section from the loaded section map it = loaded_sections.erase(it); // Skip incrementing the iterator @@ -79,6 +87,10 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) { } void init_overlays() { + for (size_t section_index = 0; section_index < num_sections; section_index++) { + section_addresses[section_index] = section_table[section_index].ram_addr; + } + // Sort the executable sections by rom address std::sort(§ion_table[0], §ion_table[num_sections], [](const SectionTableEntry& a, const SectionTableEntry& b) {