Added manual sections input option, fixed bug with multiplications and added mthi/lo instructions

This commit is contained in:
Mr-Wiseguy 2024-04-20 20:00:29 -04:00
parent 72fe4ed79c
commit 50d55bd171
4 changed files with 108 additions and 7 deletions

View file

@ -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<InstructionPatch> instruction_patches;
std::vector<FunctionSize> manual_func_sizes;
std::vector<ManualFunction> manual_functions;
std::string bss_section_suffix;
Config(const char* path);

View file

@ -17,6 +17,30 @@ struct value_error : public toml::exception {
std::string what_;
};
std::vector<RecompPort::ManualFunction> get_manual_funcs(const toml::value& manual_funcs_data) {
std::vector<RecompPort::ManualFunction> 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<std::string>(cur_func_val, "name");
const std::string& section_name = toml::find<std::string>(cur_func_val, "section");
uint32_t vram_in = toml::find<uint32_t>(cur_func_val, "vram");
uint32_t size = toml::find<uint32_t>(cur_func_val, "size");
ret.emplace_back(func_name, section_name, vram_in, size);
}
return ret;
}
std::vector<std::string> get_stubbed_funcs(const toml::value& patches_data) {
std::vector<std::string> stubbed_funcs{};
@ -226,6 +250,12 @@ RecompPort::Config::Config(const char* path) {
single_file_output = toml::find_or<bool>(input_data, "single_file_output", false);
use_absolute_symbols = toml::find_or<bool>(input_data, "use_absolute_symbols", false);
// Manual functions (optional)
const toml::value& manual_functions_data = toml::find_or<toml::value>(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<toml::value>(config_data, "patches", toml::value{});
if (patches_data.type() != toml::value_t::empty) {

View file

@ -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<RecompPort::ManualFunction>& 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<std::string, size_t> 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<uint32_t> words;
words.resize(cur_func_def.size / 4);
const uint32_t* elf_words = reinterpret_cast<const uint32_t*>(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");
}

View file

@ -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);