Implemented relocatable overlays (OoT runs)

This commit is contained in:
Mr-Wiseguy 2023-02-04 00:14:03 -05:00
parent d2603ce07c
commit 602be9c2c6
8 changed files with 337 additions and 52 deletions

View file

@ -7,6 +7,7 @@
#include <vector> #include <vector>
#include <unordered_map> #include <unordered_map>
#include <span> #include <span>
#include <unordered_set>
#include "elfio/elfio.hpp" #include "elfio/elfio.hpp"
#ifdef _MSC_VER #ifdef _MSC_VER
@ -46,13 +47,35 @@ namespace RecompPort {
bool reimplemented; 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 { struct Section {
ELFIO::Elf_Xword rom_addr; ELFIO::Elf_Xword rom_addr;
ELFIO::Elf64_Addr ram_addr; ELFIO::Elf64_Addr ram_addr;
ELFIO::Elf_Xword size; ELFIO::Elf_Xword size;
std::vector<uint32_t> function_addrs; std::vector<uint32_t> function_addrs;
std::vector<Reloc> relocs;
std::string name; std::string name;
bool executable; bool executable;
bool relocatable;
}; };
struct FunctionStats { struct FunctionStats {
@ -68,6 +91,8 @@ namespace RecompPort {
std::vector<uint8_t> rom; std::vector<uint8_t> rom;
// A list of the list of each function (by index in `functions`) in a given section // A list of the list of each function (by index in `functions`) in a given section
std::vector<std::vector<size_t>> section_functions; std::vector<std::vector<size_t>> section_functions;
// The section names that were specified as relocatable
std::unordered_set<std::string> relocatable_sections;
int executable_section_count; int executable_section_count;
Context(const ELFIO::elfio& elf_file) { Context(const ELFIO::elfio& elf_file) {

View file

@ -187,6 +187,20 @@ recomp_func_t* get_function(int32_t vram);
#define LOOKUP_FUNC(val) \ #define LOOKUP_FUNC(val) \
get_function((int32_t)(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) // For the Mario Party games (not working)
//// This has to be in this file so it can be inlined //// This has to be in this file so it can be inlined
//struct jmp_buf_storage { //struct jmp_buf_storage {

View file

@ -17,6 +17,7 @@ typedef struct {
uint32_t size; uint32_t size;
FuncEntry *funcs; FuncEntry *funcs;
size_t num_funcs; size_t num_funcs;
size_t index;
} SectionTableEntry; } SectionTableEntry;
#endif #endif

View file

@ -658,6 +658,8 @@ ELFIO::section* read_sections(RecompPort::Context& context, const ELFIO::elfio&
} }
); );
std::unordered_map<std::string, ELFIO::section*> reloc_sections_by_name;
// Iterate over every section to record rom addresses and find the symbol table // Iterate over every section to record rom addresses and find the symbol table
fmt::print("Sections\n"); fmt::print("Sections\n");
for (const auto& section : elf_file.sections) { 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 // Set the rom address of this section to the current accumulated ROM size
section_out.ram_addr = section->get_address(); section_out.ram_addr = section->get_address();
section_out.size = section->get_size(); 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), // 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 // 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 // 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(), auto segment_it = std::upper_bound(segments.begin(), segments.end(), section->get_offset(),
[](ELFIO::Elf64_Off section_offset, const SegmentEntry& segment) { [](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()) { 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; return nullptr;
} }
// Upper bound returns the iterator after the element we're looking for, so rewind by one // 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); const SegmentEntry& segment = *(segment_it - 1);
// Check to be sure that the section is actually in this segment // Check to be sure that the section is actually in this segment
if (section->get_offset() >= segment.data_offset + segment.memory_size) { 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; return nullptr;
} }
// Calculate the rom address based on this section's offset into the segment and the segment's rom address // 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; section_out.executable = true;
context.executable_section_count++; 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) { if (symtab_section == nullptr) {
// Check if this section is the symbol table and record it if so fmt::print(stderr, "No symtab section found\n");
if (section->get_type() == ELFIO::SHT_SYMTAB) { return nullptr;
symtab_section = section.get(); }
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 &section_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<uint32_t>::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<const uint32_t*>(context.rom.data() + section_out.rom_addr + rel_offset - section_out.ram_addr));
rabbitizer::InstructionCpu instr{ instr_word, static_cast<uint32_t>(rel_offset) };
//context.rom section_out.rom_addr;
reloc_out.address = rel_offset;
reloc_out.symbol_index = rel_symbol;
reloc_out.type = static_cast<RecompPort::RelocType>(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; 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<std::string>& 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) { int main(int argc, char** argv) {
if (argc != 3) { if (argc < 3 || argc > 4) {
fmt::print("Usage: {} [input elf file] [entrypoint RAM address]\n", argv[0]); fmt::print("Usage: {} [input elf file] [entrypoint RAM address] [relocatable sections list file (optional)]\n", argv[0]);
std::exit(EXIT_SUCCESS); std::exit(EXIT_SUCCESS);
} }
@ -765,6 +911,14 @@ int main(int argc, char** argv) {
std::exit(EXIT_FAILURE); std::exit(EXIT_FAILURE);
}; };
std::unordered_set<std::string> 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] }; std::string elf_name{ argv[1] };
if (!elf_file.load(elf_name)) { if (!elf_file.load(elf_name)) {
@ -786,6 +940,7 @@ int main(int argc, char** argv) {
} }
RecompPort::Context context{ elf_file }; 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 // Read all of the sections in the elf and look for the symbol table section
ELFIO::section* symtab_section = read_sections(context, elf_file); 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); "void {}(uint8_t* restrict rdram, recomp_context* restrict ctx);\n", func.name);
//fmt::print(lookup_file, //fmt::print(lookup_file,
// " {{ 0x{:08X}u, {} }},\n", func.vram, func.name); // " {{ 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(); //lookup_file.clear();
fmt::print(stderr, "Error recompiling {}\n", func.name); fmt::print(stderr, "Error recompiling {}\n", func.name);
std::exit(EXIT_FAILURE); 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); 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_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.rom_addr, section.ram_addr, section.size, section_funcs_array_name, section_index);
fmt::print(overlay_file, "static FuncEntry {}[] = {{\n", section_funcs_array_name); fmt::print(overlay_file, "static FuncEntry {}[] = {{\n", section_funcs_array_name);

View file

@ -17,7 +17,8 @@ std::string_view ctx_gpr_prefix(int reg) {
return ""; return "";
} }
bool process_instruction(const RecompPort::Context& context, const RecompPort::Function& func, const RecompPort::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, bool& needs_link_branch, bool& is_branch_likely, std::span<std::vector<uint32_t>> static_funcs_out) { bool process_instruction(const RecompPort::Context& context, const RecompPort::Function& func, const RecompPort::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) {
const auto& section = context.sections[func.section_index];
const auto& instr = instructions[instr_index]; const auto& instr = instructions[instr_index];
needs_link_branch = false; needs_link_branch = false;
is_branch_likely = false; is_branch_likely = false;
@ -37,6 +38,40 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
return true; 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 = [&]() { auto print_indent = [&]() {
fmt::print(output_file, " "); fmt::print(output_file, " ");
}; };
@ -56,7 +91,12 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
if (instr_index < instructions.size() - 1) { if (instr_index < instructions.size() - 1) {
bool dummy_needs_link_branch; bool dummy_needs_link_branch;
bool dummy_is_branch_likely; 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(); print_indent();
fmt::print(output_file, fmt_str, args...); 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) { if (instr_index < instructions.size() - 1) {
bool dummy_needs_link_branch; bool dummy_needs_link_branch;
bool dummy_is_branch_likely; 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::print(output_file, fmt_str, args...); 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(); 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()) { switch (instr.getUniqueId()) {
case InstrId::cpu_nop: case InstrId::cpu_nop:
fmt::print(output_file, "\n"); fmt::print(output_file, "\n");
break; break;
// Arithmetic // Arithmetic
case InstrId::cpu_lui: 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; break;
case InstrId::cpu_add: case InstrId::cpu_add:
case InstrId::cpu_addu: case InstrId::cpu_addu:
@ -132,19 +198,19 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
break; break;
case InstrId::cpu_addi: case InstrId::cpu_addi:
case InstrId::cpu_addiu: 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; break;
case InstrId::cpu_and: case InstrId::cpu_and:
print_line("{}{} = {}{} & {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); print_line("{}{} = {}{} & {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break; break;
case InstrId::cpu_andi: 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; break;
case InstrId::cpu_or: case InstrId::cpu_or:
print_line("{}{} = {}{} | {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); print_line("{}{} = {}{} | {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break; break;
case InstrId::cpu_ori: 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; break;
case InstrId::cpu_nor: case InstrId::cpu_nor:
print_line("{}{} = ~({}{} | {}{})", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); 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); print_line("{}{} = {}{} ^ {}{}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break; break;
case InstrId::cpu_xori: 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; break;
case InstrId::cpu_sll: case InstrId::cpu_sll:
print_line("{}{} = S32({}{}) << {}", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rt), rt, sa); 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); print_line("{}{} = SIGNED({}{}) < SIGNED({}{}) ? 1 : 0", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break; break;
case InstrId::cpu_slti: 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; break;
case InstrId::cpu_sltu: case InstrId::cpu_sltu:
print_line("{}{} = {}{} < {}{} ? 1 : 0", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); print_line("{}{} = {}{} < {}{} ? 1 : 0", ctx_gpr_prefix(rd), rd, ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt);
break; break;
case InstrId::cpu_sltiu: 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; break;
case InstrId::cpu_mult: 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); 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 // Loads
// TODO ld // TODO ld
case InstrId::cpu_lw: 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; break;
case InstrId::cpu_lh: 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; break;
case InstrId::cpu_lb: 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; break;
case InstrId::cpu_lhu: 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; break;
case InstrId::cpu_lbu: 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; break;
// Stores // Stores
case InstrId::cpu_sw: 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; break;
case InstrId::cpu_sh: 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; break;
case InstrId::cpu_sb: 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; break;
// TODO lwl, lwr // TODO lwl, lwr
// examples: // examples:
@ -246,20 +312,20 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
// LWR x + 2 -> 00000000 0189ABCD // LWR x + 2 -> 00000000 0189ABCD
// LWR x + 3 -> FFFFFFFF 89ABCDEF // LWR x + 3 -> FFFFFFFF 89ABCDEF
case InstrId::cpu_lwl: 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("{}{} = do_lwl(rdram, {}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, 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("{}{} = MEM_WL({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
break; break;
case InstrId::cpu_lwr: 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("{}{} = do_lwr(rdram, {}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, 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("//{}{} = MEM_WR({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base);
break; break;
case InstrId::cpu_swl: 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("do_swl(rdram, {}, {}{}, {}{})", signed_imm_string, 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("MEM_WL({}, {}{}) = {}{}", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt);
break; break;
case InstrId::cpu_swr: 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("do_swr(rdram, {}, {}{}, {}{})", signed_imm_string, 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("//MEM_WR({}, {}{}) = {}{}", signed_imm_string, ctx_gpr_prefix(base), base, ctx_gpr_prefix(rt), rt);
break; break;
// Branches // Branches
@ -356,7 +422,12 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
if (jtbl_find_result != stats.jump_tables.end()) { if (jtbl_find_result != stats.jump_tables.end()) {
const RecompPort::JumpTable& cur_jtbl = *jtbl_find_result; const RecompPort::JumpTable& cur_jtbl = *jtbl_find_result;
bool dummy_needs_link_branch, dummy_is_branch_likely; 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(); print_indent();
fmt::print(output_file, "switch (jr_addend_{:08X} >> 2) {{\n", cur_jtbl.jr_vram); 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++) { 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: case InstrId::cpu_lwc1:
if ((ft & 1) == 0) { if ((ft & 1) == 0) {
// even fpr // 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); print_line("NAN_CHECK(ctx->f{}.fl)", ft);
} else { } else {
// odd fpr // 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; break;
case InstrId::cpu_ldc1: case InstrId::cpu_ldc1:
if ((ft & 1) == 0) { 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); print_line("NAN_CHECK(ctx->f{}.d)", ft);
} else { } else {
fmt::print(stderr, "Invalid operand for ldc1: f{}\n", ft); 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: case InstrId::cpu_swc1:
if ((ft & 1) == 0) { if ((ft & 1) == 0) {
// even fpr // 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 { } else {
// odd fpr // 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; break;
case InstrId::cpu_sdc1: case InstrId::cpu_sdc1:
if ((ft & 1) == 0) { 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 { } else {
fmt::print(stderr, "Invalid operand for sdc1: f{}\n", ft); fmt::print(stderr, "Invalid operand for sdc1: f{}\n", ft);
return false; return false;
@ -955,6 +1026,9 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re
int num_likely_branches = 0; int num_likely_branches = 0;
bool needs_link_branch = false; bool needs_link_branch = false;
bool in_likely_delay_slot = 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) { for (size_t instr_index = 0; instr_index < instructions.size(); ++instr_index) {
bool had_link_branch = needs_link_branch; bool had_link_branch = needs_link_branch;
bool is_branch_likely = false; 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); fmt::print(output_file, "L_{:08X}:\n", *cur_label);
++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 // 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); fmt::print(stderr, "Error in recompilation, clearing {}\n", output_path);
output_file.clear(); output_file.clear();
return false; return false;

View file

@ -168,7 +168,6 @@
<ClInclude Include="..\..\recomp.h" /> <ClInclude Include="..\..\recomp.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="test.cpp" />
<_WildCardClCompile Include="..\funcs\*.c" /> <_WildCardClCompile Include="..\funcs\*.c" />
<ClCompile Include="@(_WildCardClCompile)" /> <ClCompile Include="@(_WildCardClCompile)" />
</ItemGroup> </ItemGroup>

View file

@ -15,9 +15,6 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="test.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="@(_WildCardClCompile)" /> <ClCompile Include="@(_WildCardClCompile)" />
<ClCompile Include="@(_WildCardClCompile)" /> <ClCompile Include="@(_WildCardClCompile)" />
<ClCompile Include="@(_WildCardClCompile)" /> <ClCompile Include="@(_WildCardClCompile)" />

View file

@ -27,6 +27,12 @@ void load_overlay(size_t section_table_index, int32_t ram) {
func_map[ram + func.offset] = func.func; func_map[ram + func.offset] = func.func;
} }
loaded_sections.emplace_back(ram, section_table_index); 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) { 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; uint32_t func_address = func.offset + it->loaded_ram_addr;
func_map.erase(func_address); 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 // Remove the section from the loaded section map
it = loaded_sections.erase(it); it = loaded_sections.erase(it);
// Skip incrementing the iterator // Skip incrementing the iterator
@ -79,6 +87,10 @@ extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) {
} }
void init_overlays() { 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 // Sort the executable sections by rom address
std::sort(&section_table[0], &section_table[num_sections], std::sort(&section_table[0], &section_table[num_sections],
[](const SectionTableEntry& a, const SectionTableEntry& b) { [](const SectionTableEntry& a, const SectionTableEntry& b) {