From 50d55bd171ce61139057f35ef27a5c90ed067761 Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 20 Apr 2024 20:00:29 -0400 Subject: [PATCH] Added manual sections input option, fixed bug with multiplications and added mthi/lo instructions --- include/recomp_port.h | 8 ++++++ src/config.cpp | 30 +++++++++++++++++++++ src/main.cpp | 62 +++++++++++++++++++++++++++++++++++++++++++ src/recompilation.cpp | 15 ++++++----- 4 files changed, 108 insertions(+), 7 deletions(-) diff --git a/include/recomp_port.h b/include/recomp_port.h index 1cdae72..0140d2f 100644 --- a/include/recomp_port.h +++ b/include/recomp_port.h @@ -44,6 +44,13 @@ namespace RecompPort { uint32_t size_bytes; }; + struct ManualFunction { + std::string func_name; + std::string section_name; + uint32_t vram; + uint32_t size; + }; + struct Config { int32_t entrypoint; bool has_entrypoint; @@ -58,6 +65,7 @@ namespace RecompPort { DeclaredFunctionMap declared_funcs; std::vector instruction_patches; std::vector manual_func_sizes; + std::vector manual_functions; std::string bss_section_suffix; Config(const char* path); diff --git a/src/config.cpp b/src/config.cpp index d7422b1..dac6991 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -17,6 +17,30 @@ struct value_error : public toml::exception { std::string what_; }; +std::vector get_manual_funcs(const toml::value& manual_funcs_data) { + std::vector ret; + + if (manual_funcs_data.type() != toml::value_t::array) { + return ret; + } + + // Get the funcs array as an array type. + const toml::array& manual_funcs_array = manual_funcs_data.as_array(); + + // Reserve room for all the funcs in the map. + ret.reserve(manual_funcs_array.size()); + for (const toml::value& cur_func_val : manual_funcs_array) { + const std::string& func_name = toml::find(cur_func_val, "name"); + const std::string& section_name = toml::find(cur_func_val, "section"); + uint32_t vram_in = toml::find(cur_func_val, "vram"); + uint32_t size = toml::find(cur_func_val, "size"); + + ret.emplace_back(func_name, section_name, vram_in, size); + } + + return ret; +} + std::vector get_stubbed_funcs(const toml::value& patches_data) { std::vector stubbed_funcs{}; @@ -226,6 +250,12 @@ RecompPort::Config::Config(const char* path) { single_file_output = toml::find_or(input_data, "single_file_output", false); use_absolute_symbols = toml::find_or(input_data, "use_absolute_symbols", false); + // Manual functions (optional) + const toml::value& manual_functions_data = toml::find_or(input_data, "manual_funcs", toml::value{}); + if (manual_functions_data.type() != toml::value_t::empty) { + manual_functions = get_manual_funcs(manual_functions_data); + } + // Patches section (optional) const toml::value& patches_data = toml::find_or(config_data, "patches", toml::value{}); if (patches_data.type() != toml::value_t::empty) { diff --git a/src/main.cpp b/src/main.cpp index 13fd451..80b527c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -707,6 +707,65 @@ bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, EL return found_entrypoint_func; } +void add_manual_functions(RecompPort::Context& context, const ELFIO::elfio& elf_file, const std::vector& manual_funcs) { + auto exit_failure = [](const std::string& error_str) { + fmt::vprint(stderr, error_str, fmt::make_format_args()); + std::exit(EXIT_FAILURE); + }; + + // Build a lookup from section name to section index. + std::unordered_map section_indices_by_name{}; + section_indices_by_name.reserve(context.sections.size()); + + for (size_t i = 0; i < context.sections.size(); i++) { + section_indices_by_name.emplace(context.sections[i].name, i); + } + + for (const RecompPort::ManualFunction& cur_func_def : manual_funcs) { + const auto section_find_it = section_indices_by_name.find(cur_func_def.section_name); + if (section_find_it == section_indices_by_name.end()) { + exit_failure(fmt::format("Manual function {} specified with section {}, which doesn't exist!\n", cur_func_def.func_name, cur_func_def.section_name)); + } + size_t section_index = section_find_it->second; + + const auto func_find_it = context.functions_by_name.find(cur_func_def.func_name); + if (func_find_it != context.functions_by_name.end()) { + exit_failure(fmt::format("Manual function {} already exists!\n", cur_func_def.func_name)); + } + + if ((cur_func_def.size & 0b11) != 0) { + exit_failure(fmt::format("Manual function {} has a size that isn't divisible by 4!\n", cur_func_def.func_name)); + } + + auto& section = context.sections[section_index]; + uint32_t section_offset = cur_func_def.vram - section.ram_addr; + uint32_t rom_address = section_offset + section.rom_addr; + + std::vector words; + words.resize(cur_func_def.size / 4); + const uint32_t* elf_words = reinterpret_cast(elf_file.sections[section_index]->get_data() + section_offset); + + words.assign(elf_words, elf_words + words.size()); + + size_t function_index = context.functions.size(); + context.functions.emplace_back( + cur_func_def.vram, + rom_address, + std::move(words), + cur_func_def.func_name, + ELFIO::Elf_Half(section_index), + false, + false, + false + ); + + context.section_functions[section_index].push_back(function_index); + section.function_addrs.push_back(function_index); + context.functions_by_vram[cur_func_def.vram].push_back(function_index); + context.functions_by_name[cur_func_def.func_name] = function_index; + } +} + struct SegmentEntry { ELFIO::Elf64_Off data_offset; ELFIO::Elf64_Addr physical_address; @@ -1140,6 +1199,9 @@ int main(int argc, char** argv) { // Read all of the symbols in the elf and look for the entrypoint function bool found_entrypoint_func = read_symbols(context, elf_file, symtab_section, config.entrypoint, config.has_entrypoint, config.use_absolute_symbols); + // Add any manual functions + add_manual_functions(context, elf_file, config.manual_functions); + if (config.has_entrypoint && !found_entrypoint_func) { exit_failure("Could not find entrypoint function\n"); } diff --git a/src/recompilation.cpp b/src/recompilation.cpp index f70c185..77fab21 100644 --- a/src/recompilation.cpp +++ b/src/recompilation.cpp @@ -381,10 +381,10 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C 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); + print_line("result = S64(S32({}{})) * S64(S32({}{})); lo = S32(result >> 0); hi = S32(result >> 32)", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); break; case InstrId::cpu_multu: - print_line("result = U64({}{}) * U64({}{}); lo = S32(result >> 0); hi = S32(result >> 32)", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); + print_line("result = U64(U32({}{})) * U64(U32({}{})); lo = S32(result >> 0); hi = S32(result >> 32)", ctx_gpr_prefix(rs), rs, ctx_gpr_prefix(rt), rt); break; case InstrId::cpu_div: // Cast to 64-bits before division to prevent artihmetic exception for s32(0x80000000) / -1 @@ -399,6 +399,12 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::C case InstrId::cpu_mfhi: print_line("{}{} = hi", ctx_gpr_prefix(rd), rd); break; + case InstrId::cpu_mtlo: + print_line("lo = {}{}", ctx_gpr_prefix(rd), rd); + break; + case InstrId::cpu_mthi: + print_line("hi = {}{}", ctx_gpr_prefix(rd), rd); + break; // Loads case InstrId::cpu_ld: print_line("{}{} = LD({}, {}{})", ctx_gpr_prefix(rt), rt, signed_imm_string, ctx_gpr_prefix(base), base); @@ -1095,11 +1101,6 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re } } - - if (section.name == ".anseq") { - std::this_thread::yield(); - } - // Process the current instruction and check for errors if (process_instruction(context, config, 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 recompiling {}, clearing output file\n", func.name);