diff --git a/OfflineModRecomp/main.cpp b/OfflineModRecomp/main.cpp index a6a5191..d795d39 100644 --- a/OfflineModRecomp/main.cpp +++ b/OfflineModRecomp/main.cpp @@ -181,7 +181,9 @@ int main(int argc, const char** argv) { output_file << "// Array of this mod's loaded section addresses.\n"; output_file << "RECOMP_EXPORT int32_t section_addresses[" << num_sections << "] = {};\n\n"; - for (const auto& func : mod_context.functions) { + for (size_t func_index = 0; func_index < mod_context.functions.size(); func_index++) { + auto& func = mod_context.functions[func_index]; + func.name = "mod_func_" + std::to_string(func_index); N64Recomp::recompile_function(mod_context, func, output_file, static_funcs_by_section, true); } diff --git a/RecompModTool/main.cpp b/RecompModTool/main.cpp index d5fb79a..c61a813 100644 --- a/RecompModTool/main.cpp +++ b/RecompModTool/main.cpp @@ -39,7 +39,7 @@ static std::vector get_toml_path_array(const toml::array* return ret; } -static bool read_dependency_file(const std::filesystem::path& dependency_path, N64Recomp::Context& context, std::vector& dependencies) { +static bool read_dependency_file(const std::filesystem::path& dependency_path, N64Recomp::Context& context) { toml::table toml_data{}; try { @@ -61,7 +61,7 @@ static bool read_dependency_file(const std::filesystem::path& dependency_path, N throw toml::parse_error("Invalid dependency entry", dependency_node.source()); } - size_t dependency_index = dependencies.size(); + size_t dependency_index = context.dependencies.size(); auto read_number = [](const toml::node& node, const std::string& key, const std::string& name, int64_t min_limit, int64_t max_limit) { toml::node_view mod_id_node = node[toml::path{key}]; @@ -97,14 +97,14 @@ static bool read_dependency_file(const std::filesystem::path& dependency_path, N } const std::string& mod_id = mod_id_node.ref(); - dependencies.emplace_back(N64Recomp::Dependency{ + context.dependencies.emplace_back(N64Recomp::Dependency{ .major_version = major_version, .minor_version = minor_version, .patch_version = patch_version, .mod_id = mod_id }); - // Symbol list + // Function list (optional) toml::node_view functions_data = dependency_node[toml::path{"functions"}]; if (functions_data.is_array()) { const toml::array* functions_array = functions_data.as_array(); @@ -116,14 +116,25 @@ static bool read_dependency_file(const std::filesystem::path& dependency_path, N context.add_import_symbol(function_name, dependency_index); } } - else { - if (functions_data) { - throw toml::parse_error("Mod toml is missing data reference symbol file list", functions_data.node()->source()); - } - else { - throw toml::parse_error("Invalid data reference symbol file list", functions_data.node()->source()); + else if (functions_data) { + throw toml::parse_error("Invalid dependency function list", functions_data.node()->source()); + } + + // Event list (optional) + toml::node_view events_data = dependency_node[toml::path{"events"}]; + if (events_data.is_array()) { + const toml::array* events_array = events_data.as_array(); + for (const auto& event_node : *events_array) { + if (!event_node.is_string()) { + throw toml::parse_error("Invalid dependency event", event_node.source()); + } + const std::string& event_name = event_node.ref(); + context.add_dependency_event(event_name, dependency_index); } } + else if (events_data) { + throw toml::parse_error("Invalid dependency event list", events_data.node()->source()); + } } } @@ -221,7 +232,7 @@ static inline uint32_t round_up_16(uint32_t value) { return (value + 15) & (~15); } -N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, std::vector&& dependencies, bool& good) { +N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, bool& good) { N64Recomp::Context ret{}; good = false; @@ -248,13 +259,16 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, st // TODO avoid a copy here. ret.rom = input_context.rom; - ret.dependencies = std::move(dependencies); + + // Copy the dependency data from the input context. + ret.dependencies = input_context.dependencies; + ret.import_symbols = input_context.import_symbols; + ret.dependency_events = input_context.dependency_events; + ret.dependency_events_by_name = input_context.dependency_events_by_name; uint32_t rom_to_ram = (uint32_t)-1; size_t output_section_index = (size_t)-1; ret.sections.resize(1); - - size_t num_imports = ret.import_symbols.size(); // Mapping of input section to output section for fixing up relocations. std::unordered_map input_section_to_output_section{}; @@ -387,10 +401,7 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, st event_name, cur_func.name); return {}; } - ret.callbacks.emplace_back(N64Recomp::Callback { - output_func_index, - event_index - }); + ret.add_callback(event_index, output_func_index); } ret.section_functions[output_section_index].push_back(output_func_index); @@ -553,9 +564,6 @@ N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, st } } - // 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.copy_reference_sections_from(input_context); @@ -603,11 +611,9 @@ int main(int argc, const char** argv) { } } - // Read the imported symbols, placing them at the end of the reference symbol list. - std::vector dependencies{}; - + // Read the dependency files. for (const std::filesystem::path& dependency_path : config.dependency_paths) { - if (!read_dependency_file(dependency_path, context, dependencies)) { + if (!read_dependency_file(dependency_path, context)) { fmt::print(stderr, "Failed to read dependency file: {}\n", dependency_path.string()); return EXIT_FAILURE; } @@ -638,7 +644,7 @@ int main(int argc, const char** argv) { } bool mod_context_good; - N64Recomp::Context mod_context = build_mod_context(context, std::move(dependencies), mod_context_good); + N64Recomp::Context mod_context = build_mod_context(context, mod_context_good); std::vector symbols_bin = N64Recomp::symbols_to_bin_v1(mod_context); if (symbols_bin.empty()) { fmt::print(stderr, "Failed to create symbol file\n"); diff --git a/include/n64recomp.h b/include/n64recomp.h index 66c2445..2fd9e52 100644 --- a/include/n64recomp.h +++ b/include/n64recomp.h @@ -389,7 +389,7 @@ namespace N64Recomp { return true; } - bool add_dependency_event(size_t dependency_index, const std::string& event_name, size_t& dependency_event_index_out) { + bool add_dependency_event(const std::string& event_name, size_t dependency_index) { size_t dependency_event_index = dependency_events.size(); dependency_events.emplace_back(DependencyEvent{ .dependency_index = dependency_index, @@ -397,7 +397,6 @@ namespace N64Recomp { }); // TODO Check if dependency_events_by_name already contains the name and show a conflict error if so. dependency_events_by_name[event_name] = dependency_event_index; - dependency_event_index_out = dependency_event_index; return true; } @@ -410,30 +409,7 @@ namespace N64Recomp { return true; } - bool add_callback(size_t dependency_index, const std::string& event_name, size_t function_index) { - auto find_it = dependency_events_by_name.find(event_name); - size_t dependency_event_index; - if (find_it == dependency_events_by_name.end()) { - // Event doesn't already exist, so add it. - if (!add_dependency_event(dependency_index, event_name, dependency_event_index)) { - return false; - } - } - else { - dependency_event_index = find_it->second; - // Make sure the event that we found was for this dependency. - if (dependency_events[dependency_event_index].dependency_index != dependency_index) { - return false; - } - } - callbacks.emplace_back(Callback{ - .function_index = function_index, - .dependency_event_index = dependency_event_index - }); - return true; - } - - bool add_callback_by_dependency_event(size_t dependency_event_index, size_t function_index) { + bool add_callback(size_t dependency_event_index, size_t function_index) { callbacks.emplace_back(Callback{ .function_index = function_index, .dependency_event_index = dependency_event_index diff --git a/src/elf.cpp b/src/elf.cpp index 7d6c5da..3d4e387 100644 --- a/src/elf.cpp +++ b/src/elf.cpp @@ -233,6 +233,34 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP std::unordered_map reloc_sections_by_name; std::unordered_map bss_sections_by_name; + // First pass over the sections to find the load addresses and track the minimum load address value. This mimics the objcopy raw binary output behavior. + uint32_t min_load_address = (uint32_t)-1; + for (const auto& section : elf_file.sections) { + auto& section_out = context.sections[section->get_index()]; + ELFIO::Elf_Word type = section->get_type(); + ELFIO::Elf_Xword flags = section->get_flags(); + + // Check if this section will end up in the ROM. It must not be a nobits (NOLOAD) type, must have the alloc flag set and must have a nonzero size. + if (type != ELFIO::SHT_NOBITS && (section->get_flags() & ELFIO::SHF_ALLOC) && section->get_size() != 0) { + std::optional segment_index = get_segment(segments, section_out.size, section->get_offset()); + if (!segment_index.has_value()) { + fmt::print(stderr, "Could not find segment that section {} belongs to!\n", section->get_name()); + return nullptr; + } + + const SegmentEntry& segment = segments[segment_index.value()]; + // Calculate the load address of the section based on that of the segment. + // This will get modified afterwards in the next pass to offset by the minimum load address. + section_out.rom_addr = segment.physical_address + (section->get_offset() - segment.data_offset); + // Track the minimum load address. + min_load_address = std::min(min_load_address, section_out.rom_addr); + } + else { + // Otherwise mark this section as having an invalid rom address + section_out.rom_addr = (uint32_t)-1; + } + } + // Iterate over every section to record rom addresses and find the symbol table for (const auto& section : elf_file.sections) { auto& section_out = context.sections[section->get_index()]; @@ -260,6 +288,7 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP return nullptr; } + // FIXME This should be using SH_INFO to create a reloc section to target section mapping instead of using the name. 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. @@ -280,45 +309,17 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP } } - // 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 - if (type != ELFIO::SHT_NOBITS && section->get_flags() & ELFIO::SHF_ALLOC && section->get_size() != 0) { - //// 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(), - // [](ELFIO::Elf64_Off section_offset, const SegmentEntry& segment) { - // return section_offset < segment.data_offset; - // } - //); - //if (segment_it == segments.begin()) { - // fmt::print(stderr, "Could not find segment that section {} belongs to!\n", section_name.c_str()); - // return nullptr; - //} - //// Upper bound returns the iterator after the element we're looking for, so rewind by one - //// This is safe because we checked if segment_it was segments.begin() already, which is the minimum value it could be - //const SegmentEntry& segment = *(segment_it - 1); - //// Check to be sure that the section is actually in this segment - //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_name.c_str(), segment.data_offset); - // return nullptr; - //} - std::optional segment_index = get_segment(segments, section_out.size, section->get_offset()); - if (!segment_index.has_value()) { - fmt::print(stderr, "Could not find segment that section {} belongs to!\n", section_name.c_str()); - return nullptr; - } - const SegmentEntry& segment = segments[segment_index.value()]; - // Calculate the rom address based on this section's offset into the segment and the segment's rom address - section_out.rom_addr = segment.physical_address + (section->get_offset() - segment.data_offset); - // Resize the output rom if needed to fit this section + // If this section was marked as being in the ROM in the previous pass, copy it into the ROM now. + if (section_out.rom_addr != (uint32_t)-1) { + // Adjust the section's final ROM address to account for the minimum load address. + section_out.rom_addr -= min_load_address; + // Resize the output rom if needed to fit this section. size_t required_rom_size = section_out.rom_addr + section_out.size; if (required_rom_size > context.rom.size()) { context.rom.resize(required_rom_size); } - // Copy this section's data into the rom + // Copy this section's data into the rom. std::copy(section->get_data(), section->get_data() + section->get_size(), &context.rom[section_out.rom_addr]); - } else { - // Otherwise mark this section as having an invalid rom address - section_out.rom_addr = (uint32_t)-1; } // Check if this section is marked as executable, which means it has code in it if (section->get_flags() & ELFIO::SHF_EXECINSTR) { @@ -348,7 +349,11 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP context.bss_section_to_section[section_out.bss_section_index] = section_index; } - if (context.has_reference_symbols() || section_out.relocatable) { + // Check if this section is in the ROM and relocatable. + const ELFIO::section* elf_section = elf_file.sections[section_index]; + bool in_rom = (elf_section->get_type() != ELFIO::SHT_NOBITS) && (elf_section->get_flags() & ELFIO::SHF_ALLOC); + bool is_relocatable = section_out.relocatable || context.has_reference_symbols(); + if (in_rom && is_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()) { diff --git a/src/mod_symbols.cpp b/src/mod_symbols.cpp index 5d385f6..1001fe9 100644 --- a/src/mod_symbols.cpp +++ b/src/mod_symbols.cpp @@ -199,7 +199,6 @@ bool parse_v1(std::span data, const std::unordered_map data, const std::unordered_map(data, offset, num_replacements); @@ -365,8 +363,6 @@ bool parse_v1(std::span data, const std::unordered_map(data, offset, num_callbacks); @@ -390,7 +386,7 @@ bool parse_v1(std::span data, const std::unordered_map