diff --git a/OfflineModRecomp/main.cpp b/OfflineModRecomp/main.cpp index 80fba1f..ff4b70f 100644 --- a/OfflineModRecomp/main.cpp +++ b/OfflineModRecomp/main.cpp @@ -50,8 +50,6 @@ int main(int argc, const char** argv) { return EXIT_FAILURE; } - N64Recomp::ModContext mod_context{}; - std::span symbol_data_span { reinterpret_cast(symbol_data.data()), symbol_data.size() }; std::vector dummy_rom{}; @@ -73,6 +71,8 @@ int main(int argc, const char** argv) { sections_by_vrom[reference_context.sections[section_index].rom_addr] = section_index; } + N64Recomp::Context mod_context; + N64Recomp::ModSymbolsError error = N64Recomp::parse_mod_symbols(symbol_data_span, rom_data, sections_by_vrom, reference_context, mod_context); if (error != N64Recomp::ModSymbolsError::Good) { fprintf(stderr, "Error parsing mod symbols: %d\n", (int)error); @@ -81,35 +81,30 @@ int main(int argc, const char** argv) { // Populate R_MIPS_26 reloc symbol indices. Start by building a map of vram address to matching reference symbols. std::unordered_map> reference_symbols_by_vram{}; - for (size_t reference_symbol_index = 0; reference_symbol_index < mod_context.base_context.reference_symbols.size(); reference_symbol_index++) { - const auto& sym = mod_context.base_context.reference_symbols[reference_symbol_index]; + for (size_t reference_symbol_index = 0; reference_symbol_index < mod_context.reference_symbols.size(); reference_symbol_index++) { + const auto& sym = mod_context.reference_symbols[reference_symbol_index]; uint16_t section_index = sym.section_index; - if (section_index != N64Recomp::SectionAbsolute && section_index != N64Recomp::SectionSelf && section_index != N64Recomp::SectionImport) { - const auto& section = mod_context.base_context.reference_sections[section_index]; + if (section_index != N64Recomp::SectionAbsolute && section_index != N64Recomp::SectionSelf) { + const auto& section = mod_context.reference_sections[section_index]; uint32_t vram = section.ram_addr + sym.section_offset; reference_symbols_by_vram[vram].push_back(reference_symbol_index); } } - - size_t import_symbols_start = mod_context.base_context.reference_symbols.size() - mod_context.import_symbol_dependency_indices.size(); - + // Use the mapping to populate the symbol index for every R_MIPS_26 reference symbol reloc. - for (auto& section : mod_context.base_context.sections) { + for (auto& section : mod_context.sections) { for (auto& reloc : section.relocs) { if (reloc.type == N64Recomp::RelocType::R_MIPS_26 && reloc.reference_symbol) { - if (reloc.target_section == N64Recomp::SectionImport) { - reloc.symbol_index = reloc.target_section_offset + import_symbols_start; - } - else { - const auto& target_section = mod_context.base_context.reference_sections[reloc.target_section]; + if (mod_context.is_regular_reference_section(reloc.target_section)) { + const auto& target_section = mod_context.reference_sections[reloc.target_section]; uint32_t target_vram = target_section.ram_addr + reloc.target_section_offset; auto find_funcs_it = reference_symbols_by_vram.find(target_vram); bool found = false; if (find_funcs_it != reference_symbols_by_vram.end()) { for (size_t symbol_index : find_funcs_it->second) { - const auto& cur_symbol = mod_context.base_context.reference_symbols[symbol_index]; + const auto& cur_symbol = mod_context.get_reference_symbol(reloc.target_section, symbol_index); if (cur_symbol.section_index == reloc.target_section) { reloc.symbol_index = symbol_index; found = true; @@ -126,10 +121,10 @@ int main(int argc, const char** argv) { } } - mod_context.base_context.rom = std::move(rom_data); + mod_context.rom = std::move(rom_data); std::vector> static_funcs_by_section{}; - static_funcs_by_section.resize(mod_context.base_context.sections.size()); + static_funcs_by_section.resize(mod_context.sections.size()); std::ofstream output_file { argv[3] }; @@ -142,8 +137,8 @@ int main(int argc, const char** argv) { std::string recomp_include = "#include \"librecomp/recomp.h\""; bool should_write_header = true; - for (const auto& func : mod_context.base_context.functions) { - N64Recomp::recompile_function(mod_context.base_context, func, recomp_include, output_file, static_funcs_by_section, should_write_header); + for (const auto& func : mod_context.functions) { + N64Recomp::recompile_function(mod_context, func, recomp_include, output_file, static_funcs_by_section, should_write_header); should_write_header = false; } diff --git a/RecompModTool/main.cpp b/RecompModTool/main.cpp index dd860fe..32b1ded 100644 --- a/RecompModTool/main.cpp +++ b/RecompModTool/main.cpp @@ -113,13 +113,19 @@ static bool read_dependency_file(const std::filesystem::path& dependency_path, N throw toml::parse_error("Invalid dependency function", function_node.source()); } const std::string& function_name = function_node.ref(); - context.reference_symbol_names.emplace_back(function_name); - context.reference_symbols_by_name[function_name] = context.reference_symbols.size(); - context.reference_symbols.emplace_back( - N64Recomp::ReferenceSymbol { - .section_index = N64Recomp::SectionImport, - .section_offset = 0, - .is_function = true + context.reference_symbols_by_name[function_name] = N64Recomp::SymbolReference { + .section_index = N64Recomp::SectionImport, + .symbol_index = import_symbol_dependency_indices.size() + }; + context.import_symbols.emplace_back( + N64Recomp::ImportSymbol { + .base = N64Recomp::ReferenceSymbol { + .name = function_name, + .section_index = N64Recomp::SectionImport, + .section_offset = 0, + .is_function = true + }, + .dependency_index = dependency_index, } ); import_symbol_dependency_indices.emplace_back(dependency_index); @@ -230,8 +236,8 @@ static inline uint32_t round_up_16(uint32_t value) { return (value + 15) & (~15); } -N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, std::vector&& dependencies, std::vector&& import_symbol_dependency_indices, bool& good) { - N64Recomp::ModContext ret{}; +N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, std::vector&& dependencies, std::vector&& import_symbol_dependency_indices, bool& good) { + N64Recomp::Context ret{}; good = false; // Make a vector containing 0, 1, 2, ... section count - 1 @@ -254,16 +260,14 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, ); // TODO avoid a copy here. - ret.base_context.rom = input_context.rom; + ret.rom = input_context.rom; ret.dependencies = std::move(dependencies); - ret.import_symbol_dependency_indices = std::move(import_symbol_dependency_indices); uint32_t rom_to_ram = (uint32_t)-1; size_t output_section_index = (size_t)-1; - ret.base_context.sections.resize(1); + ret.sections.resize(1); - size_t num_imports = ret.import_symbol_dependency_indices.size(); - size_t import_symbol_start_index = input_context.reference_symbols.size() - num_imports; + size_t num_imports = ret.import_symbols.size(); // Iterate over the input sections in their sorted order. for (uint16_t section_index : section_order) { @@ -275,7 +279,7 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, // If so, check if it has a vram address directly after the current output section. If it does, then add this // section's size to the output section's bss size. if (output_section_index != -1 && cur_section.size != 0) { - auto& section_out = ret.base_context.sections[output_section_index]; + auto& section_out = ret.sections[output_section_index]; uint32_t output_section_bss_start = section_out.ram_addr + section_out.size; uint32_t output_section_bss_end = output_section_bss_start + section_out.bss_size; // Check if the current section starts at the end of the output section, allowing for a range of matches to account for 16 byte section alignment. @@ -289,18 +293,18 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, // Check if this section matches up with the previous section to merge them together. if (rom_to_ram == cur_rom_to_ram) { - auto& section_out = ret.base_context.sections[output_section_index]; + auto& section_out = ret.sections[output_section_index]; uint32_t cur_section_end = cur_section.rom_addr + cur_section.size; section_out.size = cur_section_end - section_out.rom_addr; } // Otherwise, create a new output section and advance to it. else { output_section_index++; - ret.base_context.sections.resize(output_section_index + 1); - ret.base_context.section_functions.resize(output_section_index + 1); + ret.sections.resize(output_section_index + 1); + ret.section_functions.resize(output_section_index + 1); rom_to_ram = cur_rom_to_ram; - auto& new_section = ret.base_context.sections[output_section_index]; + auto& new_section = ret.sections[output_section_index]; new_section.rom_addr = cur_section.rom_addr; new_section.ram_addr = cur_section.ram_addr; new_section.size = cur_section.size; @@ -312,13 +316,12 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, bool export_section = cur_section.name == N64Recomp::ExportSectionName; // Add the functions from the current input section to the current output section. - auto& section_out = ret.base_context.sections[output_section_index]; - - size_t starting_function_index = ret.base_context.functions.size(); + auto& section_out = ret.sections[output_section_index]; + const auto& cur_section_funcs = input_context.section_functions[section_index]; for (size_t section_function_index = 0; section_function_index < cur_section_funcs.size(); section_function_index++) { - size_t output_func_index = ret.base_context.functions.size(); + size_t output_func_index = ret.functions.size(); size_t input_func_index = cur_section_funcs[section_function_index]; const auto& cur_func = input_context.functions[input_func_index]; @@ -333,7 +336,9 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, original_func_exists = false; } // Ignore reference symbols in the import section, as those are imports and not original symbols. - else if (input_context.reference_symbols[find_sym_it->second].section_index == N64Recomp::SectionImport) { + else if ( + find_sym_it->second.section_index == N64Recomp::SectionImport || + find_sym_it->second.section_index == N64Recomp::SectionHook) { original_func_exists = false; } else { @@ -347,7 +352,7 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, } // Check that the reference symbol is actually a function. - const auto& reference_symbol = input_context.reference_symbols[find_sym_it->second]; + const auto& reference_symbol = input_context.get_reference_symbol(find_sym_it->second.section_index, find_sym_it->second.symbol_index); if (!reference_symbol.is_function) { fmt::print("Function {0} is marked as a patch, but {0} was a variable in the original ROM!\n", cur_func.name); return {}; @@ -374,11 +379,11 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, name_out = cur_func.name; } - ret.base_context.section_functions[output_section_index].push_back(output_func_index); + ret.section_functions[output_section_index].push_back(output_func_index); // Add this function to the output context. - ret.base_context.functions.emplace_back( + ret.functions.emplace_back( cur_func.vram, cur_func.rom, std::vector{}, // words @@ -390,7 +395,7 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, ); // Resize the words vector so the function has the correct size. No need to copy the words, as they aren't used when making a mod symbol file. - ret.base_context.functions[output_func_index].words.resize(cur_func.words.size()); + ret.functions[output_func_index].words.resize(cur_func.words.size()); } // Copy relocs and patch HI16/LO16/26 relocs for non-relocatable reference symbols @@ -402,10 +407,7 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, } // Reloc to an imported symbol. if (cur_reloc.target_section == N64Recomp::SectionImport) { - // Copy the reloc and set the target section offset to be the import symbol index. - N64Recomp::Reloc reloc_out = cur_reloc; - reloc_out.symbol_index = reloc_out.symbol_index - import_symbol_start_index; - section_out.relocs.emplace_back(reloc_out); + section_out.relocs.emplace_back(cur_reloc); } // Reloc to a reference symbol. else if (cur_reloc.reference_symbol) { @@ -415,7 +417,7 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, uint32_t reloc_target_address = reloc_section.ram_addr + cur_reloc.target_section_offset; uint32_t reloc_rom_address = cur_reloc.address - cur_section.ram_addr + cur_section.rom_addr; - uint32_t* reloc_word_ptr = reinterpret_cast(ret.base_context.rom.data() + reloc_rom_address); + uint32_t* reloc_word_ptr = reinterpret_cast(ret.rom.data() + reloc_rom_address); uint32_t reloc_word = byteswap(*reloc_word_ptr); switch (cur_reloc.type) { case N64Recomp::RelocType::R_MIPS_32: @@ -471,27 +473,11 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context, } } - // Copy the import reference symbols from the end of the input context to this context. - ret.base_context.reference_symbols.resize(num_imports); - ret.base_context.reference_symbol_names.resize(num_imports); - for (size_t import_symbol_index = 0; import_symbol_index < num_imports; import_symbol_index++) { - size_t reference_symbol_index = import_symbol_index + import_symbol_start_index; - const auto& reference_symbol = input_context.reference_symbols[reference_symbol_index]; - const std::string& reference_symbol_name = input_context.reference_symbol_names[reference_symbol_index]; - - if (reference_symbol.section_index != N64Recomp::SectionImport) { - fmt::print("Import symbol index {} (reference symbol index {}, name {}) is not in the import section!\n", - import_symbol_index, reference_symbol_index, reference_symbol_name); - return {}; - } - - ret.base_context.reference_symbols[import_symbol_index] = reference_symbol; - ret.base_context.reference_symbol_names[import_symbol_index] = reference_symbol_name; - ret.base_context.reference_symbols_by_name[reference_symbol_name] = import_symbol_index; - } + // Copy the import reference symbols from the input context as-is to this context. + ret.import_symbols = input_context.import_symbols; // Copy the reference sections from the input context as-is for resolving reference symbol relocations. - ret.base_context.reference_sections = input_context.reference_sections; + ret.reference_sections = input_context.reference_sections; good = true; return ret; @@ -570,14 +556,14 @@ int main(int argc, const char** argv) { } bool mod_context_good; - N64Recomp::ModContext mod_context = build_mod_context(context, std::move(dependencies), std::move(import_symbol_dependency_indices), mod_context_good); + N64Recomp::Context mod_context = build_mod_context(context, std::move(dependencies), std::move(import_symbol_dependency_indices), mod_context_good); std::vector symbols_bin = N64Recomp::symbols_to_bin_v1(mod_context); std::ofstream output_syms_file{ config.output_syms_path, std::ios::binary }; output_syms_file.write(reinterpret_cast(symbols_bin.data()), symbols_bin.size()); std::ofstream output_binary_file{ config.output_binary_path, std::ios::binary }; - output_binary_file.write(reinterpret_cast(mod_context.base_context.rom.data()), mod_context.base_context.rom.size()); + output_binary_file.write(reinterpret_cast(mod_context.rom.data()), mod_context.rom.size()); return EXIT_SUCCESS; } diff --git a/include/n64recomp.h b/include/n64recomp.h index f919624..857e3b0 100644 --- a/include/n64recomp.h +++ b/include/n64recomp.h @@ -60,6 +60,7 @@ namespace N64Recomp { constexpr uint16_t SectionSelf = (uint16_t)-1; constexpr uint16_t SectionAbsolute = (uint16_t)-2; constexpr uint16_t SectionImport = (uint16_t)-3; // Imported symbols for mods + constexpr uint16_t SectionHook = (uint16_t)-4; constexpr std::string_view PatchSectionName = ".recomp_patch"; constexpr std::string_view ForcedPatchSectionName = ".recomp_force_patch"; constexpr std::string_view ExportSectionName = ".recomp_export"; @@ -85,6 +86,7 @@ namespace N64Recomp { }; struct ReferenceSymbol { + std::string name; uint16_t section_index; uint32_t section_offset; bool is_function; @@ -116,6 +118,42 @@ namespace N64Recomp { extern const std::unordered_set ignored_funcs; extern const std::unordered_set renamed_funcs; + struct Dependency { + uint8_t major_version; + uint8_t minor_version; + uint8_t patch_version; + std::string mod_id; + }; + + struct ImportSymbol { + ReferenceSymbol base; + size_t dependency_index; + }; + + struct HookSymbol { + ReferenceSymbol base; + size_t dependency_index; + }; + + struct SymbolReference { + // Reference symbol section index, or one of the special section indices such as SectionImport. + uint16_t section_index; + size_t symbol_index; + }; + + enum class ReplacementFlags : uint32_t { + Force = 1 << 0, + }; + inline ReplacementFlags operator&(ReplacementFlags lhs, ReplacementFlags rhs) { return ReplacementFlags(uint32_t(lhs) & uint32_t(rhs)); } + inline ReplacementFlags operator|(ReplacementFlags lhs, ReplacementFlags rhs) { return ReplacementFlags(uint32_t(lhs) | uint32_t(rhs)); } + + struct FunctionReplacement { + uint32_t func_index; + uint32_t original_section_vrom; + uint32_t original_vram; + ReplacementFlags flags; + }; + struct Context { std::vector
sections; std::vector functions; @@ -136,10 +174,17 @@ namespace N64Recomp { std::vector reference_sections; // A list of the reference symbols. std::vector reference_symbols; - // Name of every reference symbol in the same order as `reference_symbols`. - std::vector reference_symbol_names; // Mapping of symbol name to reference symbol index. - std::unordered_map reference_symbols_by_name; + std::unordered_map reference_symbols_by_name; + + //// Mod dependencies and their symbols + std::vector dependencies; + std::vector import_symbols; + std::vector hook_symbols; + // Indices of every exported function. + std::vector exported_funcs; + + std::vector replacements; // Imports sections and function symbols from a provided context into this context's reference sections and reference functions. void import_reference_context(const Context& reference_context); @@ -150,41 +195,39 @@ namespace N64Recomp { static bool from_elf_file(const std::filesystem::path& elf_file_path, Context& out, const ElfParsingConfig& flags, bool for_dumping_context, DataSymbolMap& data_syms_out, bool& found_entrypoint_out); Context() = default; + + bool has_reference_symbols() const { + return !reference_symbols.empty() || !import_symbols.empty() || !hook_symbols.empty(); + } + + bool is_regular_reference_section(uint16_t section_index) const { + return section_index != SectionImport && section_index != SectionHook; + } + + const ReferenceSymbol& get_reference_symbol(uint16_t section_index, size_t symbol_index) const { + if (section_index == SectionImport) { + return import_symbols[symbol_index].base; + } + else if (section_index == SectionHook) { + return hook_symbols[symbol_index].base; + } + return reference_symbols[symbol_index]; + } + + const ReferenceSymbol& get_reference_symbol(const SymbolReference& ref) const { + return get_reference_symbol(ref.section_index, ref.symbol_index); + } + + bool is_reference_section_relocatable(uint16_t section_index) const { + if (section_index == SectionImport || section_index == SectionHook) { + return true; + } + return reference_sections[section_index].relocatable; + } }; bool recompile_function(const Context& context, const Function& func, const std::string& recomp_include, std::ofstream& output_file, std::span> static_funcs, bool write_header); - - enum class ReplacementFlags : uint32_t { - Force = 1 << 0, - }; - inline ReplacementFlags operator&(ReplacementFlags lhs, ReplacementFlags rhs) { return ReplacementFlags(uint32_t(lhs) & uint32_t(rhs)); } - inline ReplacementFlags operator|(ReplacementFlags lhs, ReplacementFlags rhs) { return ReplacementFlags(uint32_t(lhs) | uint32_t(rhs)); } - - struct FunctionReplacement { - uint32_t func_index; - uint32_t original_section_vrom; - uint32_t original_vram; - ReplacementFlags flags; - }; - struct Dependency { - uint8_t major_version; - uint8_t minor_version; - uint8_t patch_version; - std::string mod_id; - }; - - struct ModContext { - Context base_context; - std::vector replacements; - // Indices of every exported function. - std::vector exported_funcs; - // List of dependencies of this mod. - std::vector dependencies; - // Index of the dependency that each imported function belongs to. - // Imported symbols exist at the end of `base_context.reference_symbols`. - std::vector import_symbol_dependency_indices; - }; enum class ModSymbolsError { Good, NotASymbolFile, @@ -193,8 +236,8 @@ namespace N64Recomp { FunctionOutOfBounds, }; - ModSymbolsError parse_mod_symbols(std::span data, std::span binary, const std::unordered_map& sections_by_vrom, const Context& reference_context, ModContext& mod_context_out); - std::vector symbols_to_bin_v1(const ModContext& mod_context); + ModSymbolsError parse_mod_symbols(std::span data, std::span binary, const std::unordered_map& sections_by_vrom, const Context& reference_context, Context& context_out); + std::vector symbols_to_bin_v1(const Context& mod_context); } #endif diff --git a/src/config.cpp b/src/config.cpp index d96d7d8..397260b 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -614,7 +614,6 @@ bool N64Recomp::Context::from_symbol_file(const std::filesystem::path& symbol_fi void N64Recomp::Context::import_reference_context(const N64Recomp::Context& reference_context) { reference_sections.resize(reference_context.sections.size()); reference_symbols.reserve(reference_context.functions.size()); - reference_symbol_names.reserve(reference_context.functions.size()); // Copy the reference context's sections into the real context's reference sections. for (size_t section_index = 0; section_index < reference_context.sections.size(); section_index++) { @@ -632,14 +631,17 @@ void N64Recomp::Context::import_reference_context(const N64Recomp::Context& refe const N64Recomp::Section& func_section = reference_context.sections[func_in.section_index]; // TODO Check if reference_symbols_by_name already contains the name and show a conflict error if so. - reference_symbols_by_name.emplace(func_in.name, reference_symbols.size()); + reference_symbols_by_name.emplace(func_in.name, N64Recomp::SymbolReference{ + .section_index = func_in.section_index, + .symbol_index = reference_symbols.size() + }); reference_symbols.emplace_back(N64Recomp::ReferenceSymbol{ + .name = func_in.name, .section_index = func_in.section_index, .section_offset = func_in.vram - static_cast(func_section.ram_addr), .is_function = true }); - reference_symbol_names.emplace_back(func_in.name); } } @@ -729,16 +731,19 @@ bool N64Recomp::Context::read_data_reference_syms(const std::filesystem::path& d } // TODO Check if reference_symbols_by_name already contains the name and show a conflict error if so. - this->reference_symbols_by_name.emplace(name.value(), reference_symbols.size()); + this->reference_symbols_by_name.emplace(name.value(), SymbolReference { + .section_index = ref_section_index, + .symbol_index = reference_symbols.size() + }); this->reference_symbols.emplace_back( ReferenceSymbol { + .name = name.value(), .section_index = ref_section_index, .section_offset = vram_addr.value() - ref_section_vram, .is_function = false } ); - this->reference_symbol_names.emplace_back(name.value()); } else { throw toml::parse_error("Invalid data symbol entry", data_sym_el.source()); diff --git a/src/elf.cpp b/src/elf.cpp index e828174..f993660 100644 --- a/src/elf.cpp +++ b/src/elf.cpp @@ -345,7 +345,7 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP section_out.bss_size = bss_find->second->get_size(); } - if (!context.reference_symbols.empty() || section_out.relocatable) { + if (context.has_reference_symbols() || section_out.relocatable) { // 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()) { @@ -405,22 +405,13 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP reloc_out.reference_symbol = true; // Replace the reloc's symbol index with the index into the reference symbol array. - reloc_out.symbol_index = sym_find_it->second; rel_section_vram = 0; - rel_symbol_offset = context.reference_symbols[reloc_out.symbol_index].section_offset; - reloc_out.target_section = context.reference_symbols[reloc_out.symbol_index].section_index; + reloc_out.target_section = sym_find_it->second.section_index; + reloc_out.symbol_index = sym_find_it->second.symbol_index; + const auto& reference_symbol = context.get_reference_symbol(reloc_out.target_section, reloc_out.symbol_index); + rel_symbol_offset = reference_symbol.section_offset; - bool target_section_relocatable = false; - - if (reloc_out.target_section == N64Recomp::SectionImport) { - target_section_relocatable = true; - } - else if (reloc_out.target_section == N64Recomp::SectionAbsolute) { - target_section_relocatable = false; - } - else if (context.reference_sections[reloc_out.target_section].relocatable) { - target_section_relocatable = true; - } + bool target_section_relocatable = context.is_reference_section_relocatable(reloc_out.target_section); if (reloc_out.type == N64Recomp::RelocType::R_MIPS_32 && target_section_relocatable) { fmt::print(stderr, "Cannot reference {} in a statically initialized variable as it's defined in a relocatable section!\n", diff --git a/src/mod_symbols.cpp b/src/mod_symbols.cpp index 0fb79b7..fdb8dbf 100644 --- a/src/mod_symbols.cpp +++ b/src/mod_symbols.cpp @@ -30,12 +30,15 @@ struct FuncV1 { }; constexpr uint32_t SectionSelfVromV1 = 0xFFFFFFFF; + +// Special sections constexpr uint32_t SectionImportVromV1 = 0xFFFFFFFE; +constexpr uint32_t SectionHookVromV1 = 0xFFFFFFFD; struct RelocV1 { uint32_t section_offset; uint32_t type; - uint32_t target_section_offset; // If this reloc references an import symbol, this indicates the import symbol index instead + uint32_t target_section_offset_or_index; // If this reloc references a special section (see above), this indicates the section's symbol index instead uint32_t target_section_vrom; }; @@ -89,9 +92,8 @@ static inline uint32_t round_up_4(uint32_t value) { return (value + 3) & (~3); } -bool parse_v1(std::span data, const std::unordered_map& sections_by_vrom, N64Recomp::ModContext& mod_context) { +bool parse_v1(std::span data, const std::unordered_map& sections_by_vrom, N64Recomp::Context& mod_context) { size_t offset = sizeof(FileHeader); - N64Recomp::Context& base_context = mod_context.base_context; const FileSubHeaderV1* subheader = reinterpret_data(data, offset); if (subheader == nullptr) { return false; @@ -114,22 +116,18 @@ bool parse_v1(std::span data, const std::unordered_map(data, offset); if (section_header == nullptr) { return false; } - N64Recomp::Section& cur_section = base_context.sections[section_index]; + N64Recomp::Section& cur_section = mod_context.sections[section_index]; cur_section.rom_addr = section_header->file_offset; cur_section.ram_addr = section_header->vram; @@ -153,8 +151,8 @@ bool parse_v1(std::span data, const std::unordered_map data, const std::unordered_map data, const std::unordered_map(relocs[reloc_index].type); - cur_reloc.target_section_offset = relocs[reloc_index].target_section_offset; - uint32_t target_section_vrom = relocs[reloc_index].target_section_vrom; + const RelocV1& reloc_in = relocs[reloc_index]; + cur_reloc.address = cur_section.ram_addr + reloc_in.section_offset; + cur_reloc.type = static_cast(reloc_in.type); + uint32_t target_section_vrom = reloc_in.target_section_vrom; + uint16_t reloc_target_section; + uint32_t reloc_target_section_offset; + uint32_t reloc_symbol_index; if (target_section_vrom == SectionSelfVromV1) { - cur_reloc.target_section = N64Recomp::SectionSelf; + reloc_target_section = N64Recomp::SectionSelf; + reloc_target_section_offset = reloc_in.target_section_offset_or_index; + reloc_symbol_index = 0; // Not used for normal relocs. } else if (target_section_vrom == SectionImportVromV1) { - cur_reloc.target_section = N64Recomp::SectionImport; + reloc_target_section = N64Recomp::SectionImport; + reloc_target_section_offset = 0; // Not used for imports or reference symbols. + reloc_symbol_index = reloc_in.target_section_offset_or_index; cur_reloc.reference_symbol = true; - // Import symbol relocs encode the import symbol index into the target_section_offset field. - cur_reloc.symbol_index = cur_reloc.target_section_offset + import_symbol_base_index; } else { // TODO lookup by section index by original vrom @@ -200,9 +203,14 @@ bool parse_v1(std::span data, const std::unordered_mapsecond; + reloc_target_section = find_section_it->second; + reloc_target_section_offset = reloc_in.target_section_offset_or_index; + reloc_symbol_index = 0; // Not used for normal relocs. cur_reloc.reference_symbol = true; } + cur_reloc.target_section = reloc_target_section; + cur_reloc.target_section_offset = reloc_target_section_offset; + cur_reloc.symbol_index = reloc_symbol_index; } } @@ -233,9 +241,9 @@ bool parse_v1(std::span data, const std::unordered_map= base_context.functions.size()) { + if (func_index >= mod_context.functions.size()) { printf("Export %zu has a function index of %u, but the symbol file only has %zu functions\n", - export_index, func_index, base_context.functions.size()); + export_index, func_index, mod_context.functions.size()); } if (name_start + name_size > string_data_size) { @@ -246,7 +254,7 @@ bool parse_v1(std::span data, const std::unordered_map(data, offset, num_dependencies); @@ -283,7 +291,6 @@ bool parse_v1(std::span data, const std::unordered_map string_data_size) { printf("Import %zu has a name start of %u and size of %u, which extend beyond the string data's total size of %zu\n", @@ -297,25 +304,29 @@ bool parse_v1(std::span data, const std::unordered_map(import_index), - .is_function = true, + mod_context.import_symbols[import_index] = N64Recomp::ImportSymbol{ + .base = { + .name = std::string{import_name}, + .section_index = N64Recomp::SectionImport, + .section_offset = 0, + .is_function = true, + }, + .dependency_index = dependency_index, }; - base_context.reference_symbol_names[reference_symbol_index] = import_name; - base_context.reference_symbols_by_name[std::string{import_name}] = reference_symbol_index; - mod_context.import_symbol_dependency_indices[import_index] = dependency_index; + auto& symbol_reference = mod_context.reference_symbols_by_name[std::string{import_name}]; + symbol_reference.section_index = N64Recomp::SectionImport; + symbol_reference.symbol_index = import_index; } return offset == data.size(); } -N64Recomp::ModSymbolsError N64Recomp::parse_mod_symbols(std::span data, std::span binary, const std::unordered_map& sections_by_vrom, const Context& reference_context, ModContext& mod_context_out) { +N64Recomp::ModSymbolsError N64Recomp::parse_mod_symbols(std::span data, std::span binary, const std::unordered_map& sections_by_vrom, const Context& reference_context, Context& mod_context_out) { size_t offset = 0; mod_context_out = {}; const FileHeader* header = reinterpret_data(data, offset); - mod_context_out.base_context.import_reference_context(reference_context); + mod_context_out.import_reference_context(reference_context); if (header == nullptr) { return ModSymbolsError::NotASymbolFile; @@ -341,7 +352,7 @@ N64Recomp::ModSymbolsError N64Recomp::parse_mod_symbols(std::span da } // Fill in the words for each function. - for (auto& cur_func : mod_context_out.base_context.functions) { + for (auto& cur_func : mod_context_out.functions) { if (cur_func.rom + cur_func.words.size() * sizeof(cur_func.words[0]) > binary.size()) { mod_context_out = {}; return ModSymbolsError::FunctionOutOfBounds; @@ -368,10 +379,9 @@ void vec_put(std::vector& vec, const std::string& data) { memcpy(vec.data() + start_size, data.data(), data.size()); } -std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& mod_context) { +std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& context) { std::vector ret{}; ret.reserve(1024); - const N64Recomp::Context& context = mod_context.base_context; const static FileHeader header { .magic = {'N', '6', '4', 'R', 'S', 'Y', 'M', 'S'}, @@ -380,13 +390,13 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m vec_put(ret, &header); - size_t num_exported_funcs = mod_context.exported_funcs.size(); - size_t num_dependencies = mod_context.dependencies.size(); - size_t num_imported_funcs = mod_context.import_symbol_dependency_indices.size(); + size_t num_exported_funcs = context.exported_funcs.size(); + size_t num_dependencies = context.dependencies.size(); + size_t num_imported_funcs = context.import_symbols.size(); FileSubHeaderV1 sub_header { .num_sections = static_cast(context.sections.size()), - .num_replacements = static_cast(mod_context.replacements.size()), + .num_replacements = static_cast(context.replacements.size()), .num_exports = static_cast(num_exported_funcs), .num_dependencies = static_cast(num_dependencies), .num_imports = static_cast(num_imported_funcs), @@ -404,8 +414,8 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m std::vector exported_func_name_positions{}; exported_func_name_positions.resize(num_exported_funcs); for (size_t export_index = 0; export_index < num_exported_funcs; export_index++) { - size_t function_index = mod_context.exported_funcs[export_index]; - const Function& exported_func = mod_context.base_context.functions[function_index]; + size_t function_index = context.exported_funcs[export_index]; + const Function& exported_func = context.functions[function_index]; exported_func_name_positions[export_index] = static_cast(ret.size() - strings_start); vec_put(ret, exported_func.name); @@ -415,7 +425,7 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m std::vector dependency_name_positions{}; dependency_name_positions.resize(num_dependencies); for (size_t dependency_index = 0; dependency_index < num_dependencies; dependency_index++) { - const Dependency& dependency = mod_context.dependencies[dependency_index]; + const Dependency& dependency = context.dependencies[dependency_index]; dependency_name_positions[dependency_index] = static_cast(ret.size() - strings_start); vec_put(ret, dependency.mod_id); @@ -425,17 +435,12 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m std::vector imported_func_name_positions{}; imported_func_name_positions.resize(num_imported_funcs); std::unordered_map mod_id_name_positions{}; - // Calculate the index of the first import symbol. Import symbols are all grouped at the end of the reference symbol list. - size_t import_symbol_base_index = mod_context.base_context.reference_symbols.size() - num_imported_funcs; for (size_t import_index = 0; import_index < num_imported_funcs; import_index++) { - // Get the index of the reference symbol for this import. - size_t reference_symbol_index = import_symbol_base_index + import_index; - const ReferenceSymbol& imported_func = mod_context.base_context.reference_symbols[reference_symbol_index]; - const std::string& imported_func_name = mod_context.base_context.reference_symbol_names[reference_symbol_index]; + const ImportSymbol& imported_func = context.import_symbols[import_index]; // Write this import's name into the strings data. imported_func_name_positions[import_index] = static_cast(ret.size() - strings_start); - vec_put(ret, imported_func_name); + vec_put(ret, imported_func.base.name); } // Align the data after the strings to 4 bytes. @@ -470,13 +475,13 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m for (const Reloc& cur_reloc : cur_section.relocs) { uint32_t target_section_vrom; - uint32_t target_section_offset = cur_reloc.target_section_offset; + uint32_t target_section_offset_or_index = cur_reloc.target_section_offset; if (cur_reloc.target_section == SectionSelf) { target_section_vrom = SectionSelfVromV1; } else if (cur_reloc.target_section == SectionImport) { target_section_vrom = SectionImportVromV1; - target_section_offset = cur_reloc.symbol_index; + target_section_offset_or_index = cur_reloc.symbol_index; } else if (cur_reloc.reference_symbol) { target_section_vrom = context.reference_sections[cur_reloc.target_section].rom_addr; @@ -487,7 +492,7 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m RelocV1 reloc_out { .section_offset = cur_reloc.address - cur_section.ram_addr, .type = static_cast(cur_reloc.type), - .target_section_offset = target_section_offset, + .target_section_offset_or_index = target_section_offset_or_index, .target_section_vrom = target_section_vrom }; @@ -496,7 +501,7 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m } // Write the function replacements. - for (const FunctionReplacement& cur_replacement : mod_context.replacements) { + for (const FunctionReplacement& cur_replacement : context.replacements) { uint32_t flags = 0; if ((cur_replacement.flags & ReplacementFlags::Force) == ReplacementFlags::Force) { flags |= 0x1; @@ -514,8 +519,8 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m // Write the exported functions. for (size_t export_index = 0; export_index < num_exported_funcs; export_index++) { - size_t function_index = mod_context.exported_funcs[export_index]; - const Function& exported_func = mod_context.base_context.functions[function_index]; + size_t function_index = context.exported_funcs[export_index]; + const Function& exported_func = context.functions[function_index]; ExportV1 export_out { .func_index = static_cast(function_index), @@ -528,7 +533,7 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m // Write the dependencies. for (size_t dependency_index = 0; dependency_index < num_dependencies; dependency_index++) { - const Dependency& dependency = mod_context.dependencies[dependency_index]; + const Dependency& dependency = context.dependencies[dependency_index]; DependencyV1 dependency_out { .major_version = dependency.major_version, @@ -544,15 +549,12 @@ std::vector N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m // Write the imported functions. for (size_t import_index = 0; import_index < num_imported_funcs; import_index++) { // Get the index of the reference symbol for this import. - size_t reference_symbol_index = import_symbol_base_index + import_index; - const ReferenceSymbol& imported_func = mod_context.base_context.reference_symbols[reference_symbol_index]; - const std::string& imported_func_name = mod_context.base_context.reference_symbol_names[reference_symbol_index]; - size_t dependency_index = mod_context.import_symbol_dependency_indices[import_index]; + const ImportSymbol& imported_func = context.import_symbols[import_index]; ImportV1 import_out { .name_start = imported_func_name_positions[import_index], - .name_size = static_cast(imported_func_name.size()), - .dependency = static_cast(dependency_index) + .name_size = static_cast(imported_func.base.name.size()), + .dependency = static_cast(imported_func.dependency_index) }; vec_put(ret, &import_out); diff --git a/src/recompilation.cpp b/src/recompilation.cpp index 2724179..474fbb0 100644 --- a/src/recompilation.cpp +++ b/src/recompilation.cpp @@ -238,13 +238,12 @@ bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Fun return true; }; - auto print_func_call = [reloc_target_section_offset, reloc_reference_symbol, reloc_type, &context, §ion, &func, &static_funcs_out, &needs_link_branch, &print_unconditional_branch] + auto print_func_call = [reloc_target_section_offset, reloc_section, reloc_reference_symbol, reloc_type, &context, §ion, &func, &static_funcs_out, &needs_link_branch, &print_unconditional_branch] (uint32_t target_func_vram, bool link_branch = true, bool indent = false) { std::string jal_target_name{}; if (reloc_reference_symbol != (size_t)-1) { - const auto& ref_symbol = context.reference_symbols[reloc_reference_symbol]; - const std::string& ref_symbol_name = context.reference_symbol_names[reloc_reference_symbol]; + const auto& ref_symbol = context.get_reference_symbol(reloc_section, reloc_reference_symbol); if (reloc_type != N64Recomp::RelocType::R_MIPS_26) { fmt::print(stderr, "Unsupported reloc type {} on jal instruction in {}\n", (int)reloc_type, func.name); @@ -256,7 +255,7 @@ bool process_instruction(const N64Recomp::Context& context, const N64Recomp::Fun return false; } - jal_target_name = ref_symbol_name; + jal_target_name = ref_symbol.name; } else { size_t matched_func_index = 0;