#include #include #include #include #include #include #include "ultramodern/ultramodern.hpp" #include "recomp.h" #include "recompiler/context.h" #include "overlays.hpp" #include "sections.h" static recomp::overlays::overlay_section_table_data_t sections_info {}; static recomp::overlays::overlays_by_index_t overlays_info {}; static SectionTableEntry* patch_code_sections = nullptr; size_t num_patch_code_sections = 0; static std::vector patch_data; struct LoadedSection { int32_t loaded_ram_addr; size_t section_table_index; LoadedSection(int32_t loaded_ram_addr_, size_t section_table_index_) { loaded_ram_addr = loaded_ram_addr_; section_table_index = section_table_index_; } bool operator<(const LoadedSection& rhs) { return loaded_ram_addr < rhs.loaded_ram_addr; } }; static std::unordered_map code_sections_by_rom{}; static std::unordered_map patch_code_sections_by_rom{}; static std::vector loaded_sections{}; static std::unordered_map func_map{}; static std::unordered_map base_exports{}; static std::unordered_map ext_base_exports{}; static std::unordered_map base_events; static std::unordered_map manual_patch_symbols_by_vram; extern "C" { int32_t* section_addresses = nullptr; } void recomp::overlays::register_overlays(const overlay_section_table_data_t& sections, const overlays_by_index_t& overlays) { sections_info = sections; overlays_info = overlays; } void recomp::overlays::register_patches(const char* patch, std::size_t size, SectionTableEntry* sections, size_t num_sections) { patch_code_sections = sections; num_patch_code_sections = num_sections; patch_data.resize(size); std::memcpy(patch_data.data(), patch, size); patch_code_sections_by_rom.reserve(num_patch_code_sections); for (size_t i = 0; i < num_patch_code_sections; i++) { patch_code_sections_by_rom.emplace(patch_code_sections[i].rom_addr, i); } } void recomp::overlays::register_base_export(const std::string& name, recomp_func_t* func) { base_exports.emplace(name, func); } void recomp::overlays::register_ext_base_export(const std::string& name, recomp_func_ext_t* func) { ext_base_exports.emplace(name, func); } void recomp::overlays::register_base_exports(const FunctionExport* export_list) { std::unordered_map patch_func_vram_map{}; // Iterate over all patch functions to set up a mapping of their vram address. for (size_t patch_section_index = 0; patch_section_index < num_patch_code_sections; patch_section_index++) { const SectionTableEntry* cur_section = &patch_code_sections[patch_section_index]; for (size_t func_index = 0; func_index < cur_section->num_funcs; func_index++) { const FuncEntry* cur_func = &cur_section->funcs[func_index]; patch_func_vram_map.emplace(cur_section->ram_addr + cur_func->offset, cur_func->func); } } // Iterate over exports, using the vram mapping to create a name mapping. for (const FunctionExport* cur_export = &export_list[0]; cur_export->name != nullptr; cur_export++) { auto it = patch_func_vram_map.find(cur_export->ram_addr); if (it == patch_func_vram_map.end()) { assert(false && "Failed to find exported function in patch function sections!"); } base_exports.emplace(cur_export->name, it->second); } } recomp_func_t* recomp::overlays::get_base_export(const std::string& export_name) { auto it = base_exports.find(export_name); if (it == base_exports.end()) { return nullptr; } return it->second; } recomp_func_ext_t* recomp::overlays::get_ext_base_export(const std::string& export_name) { auto it = ext_base_exports.find(export_name); if (it == ext_base_exports.end()) { return nullptr; } return it->second; } void recomp::overlays::register_base_events(char const* const* event_names) { for (size_t event_index = 0; event_names[event_index] != nullptr; event_index++) { base_events.emplace(event_names[event_index], event_index); } } size_t recomp::overlays::get_base_event_index(const std::string& event_name) { auto it = base_events.find(event_name); if (it == base_events.end()) { return (size_t)-1; } return it->second; } size_t recomp::overlays::num_base_events() { return base_events.size(); } const std::unordered_map& recomp::overlays::get_vrom_to_section_map() { return code_sections_by_rom; } uint32_t recomp::overlays::get_section_ram_addr(uint16_t code_section_index) { return sections_info.code_sections[code_section_index].ram_addr; } std::span recomp::overlays::get_section_relocs(uint16_t code_section_index) { if (code_section_index < sections_info.num_code_sections) { const auto& section = sections_info.code_sections[code_section_index]; return std::span{ section.relocs, section.num_relocs }; } assert(false); return {}; } void recomp::overlays::add_loaded_function(int32_t ram, recomp_func_t* func) { func_map[ram] = func; } void load_overlay(size_t section_table_index, int32_t ram) { const SectionTableEntry& section = sections_info.code_sections[section_table_index]; for (size_t function_index = 0; function_index < section.num_funcs; function_index++) { const FuncEntry& func = section.funcs[function_index]; func_map[ram + func.offset] = func.func; } loaded_sections.emplace_back(ram, section_table_index); section_addresses[section.index] = ram; } static void load_special_overlay(const SectionTableEntry& section, int32_t ram) { for (size_t function_index = 0; function_index < section.num_funcs; function_index++) { const FuncEntry& func = section.funcs[function_index]; func_map[ram + func.offset] = func.func; } } static void load_patch_functions() { if (patch_code_sections == nullptr) { debug_printf("[Patch] No patch section was registered\n"); return; } for (size_t i = 0; i < num_patch_code_sections; i++) { load_special_overlay(patch_code_sections[i], patch_code_sections[i].ram_addr); } } void recomp::overlays::read_patch_data(uint8_t* rdram, gpr patch_data_address) { for (size_t i = 0; i < patch_data.size(); i++) { MEM_B(i, patch_data_address) = patch_data[i]; } } extern "C" void load_overlays(uint32_t rom, int32_t ram_addr, uint32_t size) { // Search for the first section that's included in the loaded rom range // Sections were sorted by `init_overlays` so we can use the bounds functions auto lower = std::lower_bound(§ions_info.code_sections[0], §ions_info.code_sections[sections_info.num_code_sections], rom, [](const SectionTableEntry& entry, uint32_t addr) { return entry.rom_addr < addr; } ); auto upper = std::upper_bound(§ions_info.code_sections[0], §ions_info.code_sections[sections_info.num_code_sections], (uint32_t)(rom + size), [](uint32_t addr, const SectionTableEntry& entry) { return addr < entry.size + entry.rom_addr; } ); // Load the overlays that were found for (auto it = lower; it != upper; ++it) { load_overlay(std::distance(§ions_info.code_sections[0], it), it->rom_addr - rom + ram_addr); } } extern "C" void unload_overlay_by_id(uint32_t id) { uint32_t section_table_index = overlays_info.table[id]; const SectionTableEntry& section = sections_info.code_sections[section_table_index]; auto find_it = std::find_if(loaded_sections.begin(), loaded_sections.end(), [section_table_index](const LoadedSection& s) { return s.section_table_index == section_table_index; }); if (find_it != loaded_sections.end()) { // Determine where each function was loaded to and remove that entry from the function map for (size_t func_index = 0; func_index < section.num_funcs; func_index++) { const auto& func = section.funcs[func_index]; uint32_t func_address = func.offset + find_it->loaded_ram_addr; func_map.erase(func_address); } // Reset the section's address in the address table section_addresses[section.index] = section.ram_addr; // Remove the section from the loaded section map loaded_sections.erase(find_it); } } extern "C" void load_overlay_by_id(uint32_t id, uint32_t ram_addr) { uint32_t section_table_index = overlays_info.table[id]; const SectionTableEntry& section = sections_info.code_sections[section_table_index]; int32_t prev_address = section_addresses[section.index]; if (/*ram_addr >= 0x80000000 && ram_addr < 0x81000000) {*/ prev_address == section.ram_addr) { load_overlay(section_table_index, ram_addr); } else { int32_t new_address = prev_address + ram_addr; unload_overlay_by_id(id); load_overlay(section_table_index, new_address); } } extern "C" void unload_overlays(int32_t ram_addr, uint32_t size) { for (auto it = loaded_sections.begin(); it != loaded_sections.end();) { const auto& section = sections_info.code_sections[it->section_table_index]; // Check if the unloaded region overlaps with the loaded section if (ram_addr < (it->loaded_ram_addr + section.size) && (ram_addr + size) >= it->loaded_ram_addr) { // Check if the section isn't entirely in the loaded region if (ram_addr > it->loaded_ram_addr || (ram_addr + size) < (it->loaded_ram_addr + section.size)) { fprintf(stderr, "Cannot partially unload section\n" " rom: 0x%08X size: 0x%08X loaded_addr: 0x%08X\n" " unloaded_ram: 0x%08X unloaded_size : 0x%08X\n", section.rom_addr, section.size, it->loaded_ram_addr, ram_addr, size); assert(false); std::exit(EXIT_FAILURE); } // Determine where each function was loaded to and remove that entry from the function map for (size_t func_index = 0; func_index < section.num_funcs; func_index++) { const auto& func = section.funcs[func_index]; 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] = section.ram_addr; // Remove the section from the loaded section map it = loaded_sections.erase(it); // Skip incrementing the iterator continue; } ++it; } } void recomp::overlays::init_overlays() { func_map.clear(); section_addresses = (int32_t *)calloc(sections_info.total_num_sections, sizeof(int32_t)); // Sort the executable sections by rom address std::sort(§ions_info.code_sections[0], §ions_info.code_sections[sections_info.num_code_sections], [](const SectionTableEntry& a, const SectionTableEntry& b) { return a.rom_addr < b.rom_addr; } ); for (size_t section_index = 0; section_index < sections_info.num_code_sections; section_index++) { SectionTableEntry* code_section = §ions_info.code_sections[section_index]; section_addresses[sections_info.code_sections[section_index].index] = code_section->ram_addr; code_sections_by_rom[code_section->rom_addr] = section_index; } load_patch_functions(); } // Finds a function given a section's index and the function's offset into the section. bool recomp::overlays::get_func_entry_by_section_index_function_offset(uint16_t code_section_index, uint32_t function_offset, FuncEntry& func_out) { if (code_section_index >= sections_info.num_code_sections) { return false; } SectionTableEntry* section = §ions_info.code_sections[code_section_index]; if (function_offset >= section->size) { return false; } // TODO avoid a linear lookup here. for (size_t func_index = 0; func_index < section->num_funcs; func_index++) { if (section->funcs[func_index].offset == function_offset) { func_out = section->funcs[func_index]; return true; } } return false; } void recomp::overlays::register_manual_patch_symbols(const ManualPatchSymbol* manual_patch_symbols) { for (size_t i = 0; manual_patch_symbols[i].func != nullptr; i++) { if (!manual_patch_symbols_by_vram.emplace(manual_patch_symbols[i].ram_addr, manual_patch_symbols[i].func).second) { printf("Duplicate manual patch symbol address: %08X\n", manual_patch_symbols[i].ram_addr); ultramodern::error_handling::message_box("Duplicate manual patch symbol address (syms.ld)!"); assert(false && "Duplicate manual patch symbol address (syms.ld)!"); ultramodern::error_handling::quick_exit(__FILE__, __LINE__, __FUNCTION__); } } } // TODO use N64Recomp::is_manual_patch_symbol instead after updating submodule. bool is_manual_patch_symbol(uint32_t vram) { return vram >= 0x8F000000 && vram < 0x90000000; } // Finds a function given a section's index and the function's offset into the section and returns its native pointer. recomp_func_t* recomp::overlays::get_func_by_section_index_function_offset(uint16_t code_section_index, uint32_t function_offset) { FuncEntry entry; if (get_func_entry_by_section_index_function_offset(code_section_index, function_offset, entry)) { return entry.func; } if (code_section_index == N64Recomp::SectionAbsolute && is_manual_patch_symbol(function_offset)) { auto find_it = manual_patch_symbols_by_vram.find(function_offset); if (find_it != manual_patch_symbols_by_vram.end()) { return find_it->second; } } return nullptr; } // Finds a function given a section's rom address and the function's vram address. recomp_func_t* recomp::overlays::get_func_by_section_rom_function_vram(uint32_t section_rom, uint32_t function_vram) { auto find_section_it = code_sections_by_rom.find(section_rom); if (find_section_it == code_sections_by_rom.end()) { return nullptr; } SectionTableEntry* section = §ions_info.code_sections[find_section_it->second]; int32_t func_offset = function_vram - section->ram_addr; return get_func_by_section_index_function_offset(find_section_it->second, func_offset); } extern "C" recomp_func_t * get_function(int32_t addr) { auto func_find = func_map.find(addr); if (func_find == func_map.end()) { fprintf(stderr, "Failed to find function at 0x%08X\n", addr); assert(false); std::exit(EXIT_FAILURE); } return func_find->second; } std::unordered_map recomp::overlays::get_base_patched_funcs() { std::unordered_map ret{}; // Collect the set of all functions in the patches. std::unordered_map all_patch_funcs{}; for (size_t patch_section_index = 0; patch_section_index < num_patch_code_sections; patch_section_index++) { const auto& patch_section = patch_code_sections[patch_section_index]; for (size_t func_index = 0; func_index < patch_section.num_funcs; func_index++) { all_patch_funcs.emplace(patch_section.funcs[func_index].func, BasePatchedFunction{ .patch_section = patch_section_index, .function_index = func_index }); } } // Check every vanilla function against the full patch function set. // Any functions in both are patched. for (size_t code_section_index = 0; code_section_index < sections_info.num_code_sections; code_section_index++) { const auto& code_section = sections_info.code_sections[code_section_index]; for (size_t func_index = 0; func_index < code_section.num_funcs; func_index++) { recomp_func_t* cur_func = code_section.funcs[func_index].func; // If this function also exists in the patches function set then it's a vanilla function that was patched. auto find_it = all_patch_funcs.find(cur_func); if (find_it != all_patch_funcs.end()) { ret.emplace(cur_func, find_it->second); } } } return ret; } const std::unordered_map& recomp::overlays::get_patch_vrom_to_section_map() { return patch_code_sections_by_rom; } uint32_t recomp::overlays::get_patch_section_ram_addr(uint16_t patch_code_section_index) { if (patch_code_section_index < num_patch_code_sections) { return patch_code_sections[patch_code_section_index].ram_addr; } assert(false); return -1; } uint32_t recomp::overlays::get_patch_section_rom_addr(uint16_t patch_code_section_index) { if (patch_code_section_index < num_patch_code_sections) { return patch_code_sections[patch_code_section_index].rom_addr; } assert(false); return -1; } const FuncEntry* recomp::overlays::get_patch_function_entry(uint16_t patch_code_section_index, size_t function_index) { if (patch_code_section_index < num_patch_code_sections) { const auto& section = patch_code_sections[patch_code_section_index]; if (function_index < section.num_funcs) { return §ion.funcs[function_index]; } } assert(false); return nullptr; } // Finds a base patched function given a patch section's index and the function's offset into the section. bool recomp::overlays::get_patch_func_entry_by_section_index_function_offset(uint16_t patch_code_section_index, uint32_t function_offset, FuncEntry& func_out) { if (patch_code_section_index >= num_patch_code_sections) { return false; } SectionTableEntry* section = &patch_code_sections[patch_code_section_index]; if (function_offset >= section->size) { return false; } // TODO avoid a linear lookup here. for (size_t func_index = 0; func_index < section->num_funcs; func_index++) { if (section->funcs[func_index].offset == function_offset) { func_out = section->funcs[func_index]; return true; } } return false; } std::span recomp::overlays::get_patch_section_relocs(uint16_t patch_code_section_index) { if (patch_code_section_index < num_patch_code_sections) { const auto& section = patch_code_sections[patch_code_section_index]; return std::span{ section.relocs, section.num_relocs }; } assert(false); return {}; } std::span recomp::overlays::get_patch_binary() { return std::span{ reinterpret_cast(patch_data.data()), patch_data.size() }; }