mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2026-04-28 21:12:24 +00:00
Fix handling of section load addresses to match objcopy behavior, added event parsing to dependency tomls, minor cleanup
This commit is contained in:
parent
eb8aba6550
commit
49c3628a76
5 changed files with 79 additions and 94 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ static std::vector<std::filesystem::path> 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<N64Recomp::Dependency>& 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<std::string>();
|
||||
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<std::string>();
|
||||
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<N64Recomp::Dependency>&& 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<uint16_t, uint16_t> 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<N64Recomp::Dependency> 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<uint8_t> symbols_bin = N64Recomp::symbols_to_bin_v1(mod_context);
|
||||
if (symbols_bin.empty()) {
|
||||
fmt::print(stderr, "Failed to create symbol file\n");
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
75
src/elf.cpp
75
src/elf.cpp
|
|
@ -233,6 +233,34 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP
|
|||
std::unordered_map<std::string, ELFIO::section*> reloc_sections_by_name;
|
||||
std::unordered_map<std::string, ELFIO::section*> 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<size_t> 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<size_t> 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()) {
|
||||
|
|
|
|||
|
|
@ -199,7 +199,6 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
|||
cur_func.vram = cur_section.ram_addr + funcs[func_index].section_offset;
|
||||
cur_func.rom = cur_section.rom_addr + funcs[func_index].section_offset;
|
||||
cur_func.words.resize(funcs[func_index].size / sizeof(uint32_t)); // Filled in later
|
||||
cur_func.name = "mod_func_" + std::to_string(start_func_index + func_index);
|
||||
cur_func.section_index = section_index;
|
||||
}
|
||||
|
||||
|
|
@ -322,8 +321,7 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
|||
|
||||
std::string_view dependency_event_name{ string_data + name_start, string_data + name_start + name_size };
|
||||
|
||||
size_t dummy_dependency_event_index;
|
||||
mod_context.add_dependency_event(dependency_index, std::string{dependency_event_name}, dummy_dependency_event_index);
|
||||
mod_context.add_dependency_event(std::string{dependency_event_name}, dependency_index);
|
||||
}
|
||||
|
||||
const ReplacementV1* replacements = reinterpret_data<ReplacementV1>(data, offset, num_replacements);
|
||||
|
|
@ -365,8 +363,6 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
|||
|
||||
// Add the function to the exported function list.
|
||||
mod_context.exported_funcs[export_index] = func_index;
|
||||
// Populate the exported function's name from the string data.
|
||||
mod_context.functions[func_index].name = std::string_view(string_data + name_start, string_data + name_start + name_size);
|
||||
}
|
||||
|
||||
const CallbackV1* callbacks = reinterpret_data<CallbackV1>(data, offset, num_callbacks);
|
||||
|
|
@ -390,7 +386,7 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
|
|||
callback_index, function_index, mod_context.functions.size());
|
||||
}
|
||||
|
||||
if (!mod_context.add_callback_by_dependency_event(dependency_event_index, function_index)) {
|
||||
if (!mod_context.add_callback(dependency_event_index, function_index)) {
|
||||
printf("Failed to add callback %zu\n", callback_index);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue