Move mod context fields into base context, move import symbols into separate vector, misc cleanup

This commit is contained in:
Mr-Wiseguy 2024-08-17 13:25:04 -04:00
parent 623013a371
commit 5f1b9a845b
7 changed files with 220 additions and 199 deletions

View file

@ -50,8 +50,6 @@ int main(int argc, const char** argv) {
return EXIT_FAILURE;
}
N64Recomp::ModContext mod_context{};
std::span<const char> symbol_data_span { reinterpret_cast<const char*>(symbol_data.data()), symbol_data.size() };
std::vector<uint8_t> 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<uint32_t, std::vector<size_t>> 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<std::vector<uint32_t>> 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;
}

View file

@ -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<std::string>();
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<N64Recomp::Dependency>&& dependencies, std::vector<size_t>&& import_symbol_dependency_indices, bool& good) {
N64Recomp::ModContext ret{};
N64Recomp::Context build_mod_context(const N64Recomp::Context& input_context, std::vector<N64Recomp::Dependency>&& dependencies, std::vector<size_t>&& 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<uint32_t>{}, // 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<uint32_t*>(ret.base_context.rom.data() + reloc_rom_address);
uint32_t* reloc_word_ptr = reinterpret_cast<uint32_t*>(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<uint8_t> 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<const char*>(symbols_bin.data()), symbols_bin.size());
std::ofstream output_binary_file{ config.output_binary_path, std::ios::binary };
output_binary_file.write(reinterpret_cast<const char*>(mod_context.base_context.rom.data()), mod_context.base_context.rom.size());
output_binary_file.write(reinterpret_cast<const char*>(mod_context.rom.data()), mod_context.rom.size());
return EXIT_SUCCESS;
}

View file

@ -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<std::string> ignored_funcs;
extern const std::unordered_set<std::string> 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<Section> sections;
std::vector<Function> functions;
@ -136,10 +174,17 @@ namespace N64Recomp {
std::vector<ReferenceSection> reference_sections;
// A list of the reference symbols.
std::vector<ReferenceSymbol> reference_symbols;
// Name of every reference symbol in the same order as `reference_symbols`.
std::vector<std::string> reference_symbol_names;
// Mapping of symbol name to reference symbol index.
std::unordered_map<std::string, size_t> reference_symbols_by_name;
std::unordered_map<std::string, SymbolReference> reference_symbols_by_name;
//// Mod dependencies and their symbols
std::vector<Dependency> dependencies;
std::vector<ImportSymbol> import_symbols;
std::vector<HookSymbol> hook_symbols;
// Indices of every exported function.
std::vector<size_t> exported_funcs;
std::vector<FunctionReplacement> 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<std::vector<uint32_t>> 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<FunctionReplacement> replacements;
// Indices of every exported function.
std::vector<size_t> exported_funcs;
// List of dependencies of this mod.
std::vector<Dependency> dependencies;
// Index of the dependency that each imported function belongs to.
// Imported symbols exist at the end of `base_context.reference_symbols`.
std::vector<size_t> import_symbol_dependency_indices;
};
enum class ModSymbolsError {
Good,
NotASymbolFile,
@ -193,8 +236,8 @@ namespace N64Recomp {
FunctionOutOfBounds,
};
ModSymbolsError parse_mod_symbols(std::span<const char> data, std::span<const uint8_t> binary, const std::unordered_map<uint32_t, uint16_t>& sections_by_vrom, const Context& reference_context, ModContext& mod_context_out);
std::vector<uint8_t> symbols_to_bin_v1(const ModContext& mod_context);
ModSymbolsError parse_mod_symbols(std::span<const char> data, std::span<const uint8_t> binary, const std::unordered_map<uint32_t, uint16_t>& sections_by_vrom, const Context& reference_context, Context& context_out);
std::vector<uint8_t> symbols_to_bin_v1(const Context& mod_context);
}
#endif

View file

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

View file

@ -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",

View file

@ -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<const char> data, const std::unordered_map<uint32_t, uint16_t>& sections_by_vrom, N64Recomp::ModContext& mod_context) {
bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uint16_t>& 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<FileSubHeaderV1>(data, offset);
if (subheader == nullptr) {
return false;
@ -114,22 +116,18 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
return false;
}
size_t import_symbol_base_index = base_context.reference_symbols.size();
base_context.sections.resize(num_sections);
mod_context.sections.resize(num_sections);
mod_context.replacements.resize(num_replacements);
mod_context.exported_funcs.resize(num_exports);
mod_context.dependencies.resize(num_dependencies);
mod_context.import_symbol_dependency_indices.resize(num_imports);
base_context.reference_symbols.resize(import_symbol_base_index + num_imports);
base_context.reference_symbol_names.resize(import_symbol_base_index + num_imports);
mod_context.import_symbols.resize(num_imports);
for (size_t section_index = 0; section_index < num_sections; section_index++) {
const SectionHeaderV1* section_header = reinterpret_data<SectionHeaderV1>(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<const char> data, const std::unordered_map<uint32_t, uin
return false;
}
size_t start_func_index = base_context.functions.size();
base_context.functions.resize(base_context.functions.size() + num_funcs);
size_t start_func_index = mod_context.functions.size();
mod_context.functions.resize(mod_context.functions.size() + num_funcs);
cur_section.relocs.resize(num_relocs);
for (size_t func_index = 0; func_index < num_funcs; func_index++) {
@ -169,7 +167,7 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
return false;
}
N64Recomp::Function& cur_func = base_context.functions[start_func_index + func_index];
N64Recomp::Function& cur_func = mod_context.functions[start_func_index + func_index];
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
@ -179,18 +177,23 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
for (size_t reloc_index = 0; reloc_index < num_relocs; reloc_index++) {
N64Recomp::Reloc& cur_reloc = cur_section.relocs[reloc_index];
cur_reloc.address = cur_section.ram_addr + relocs[reloc_index].section_offset;
cur_reloc.type = static_cast<N64Recomp::RelocType>(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<N64Recomp::RelocType>(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<const char> data, const std::unordered_map<uint32_t, uin
reloc_index, section_index, target_section_vrom);
return false;
}
cur_reloc.target_section = find_section_it->second;
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<const char> data, const std::unordered_map<uint32_t, uin
uint32_t name_start = export_in.name_start;
uint32_t name_size = export_in.name_size;
if (func_index >= 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<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.
base_context.functions[func_index].name = std::string_view(string_data + name_start, string_data + name_start + name_size);
mod_context.functions[func_index].name = std::string_view(string_data + name_start, string_data + name_start + name_size);
}
const DependencyV1* dependencies = reinterpret_data<DependencyV1>(data, offset, num_dependencies);
@ -283,7 +291,6 @@ bool parse_v1(std::span<const char> data, const std::unordered_map<uint32_t, uin
uint32_t name_start = import_in.name_start;
uint32_t name_size = import_in.name_size;
uint32_t dependency_index = import_in.dependency;
size_t reference_symbol_index = import_symbol_base_index + import_index;
if (name_start + name_size > 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<const char> data, const std::unordered_map<uint32_t, uin
std::string_view import_name{ string_data + name_start, string_data + name_start + name_size };
base_context.reference_symbols[reference_symbol_index] = N64Recomp::ReferenceSymbol{
.section_index = N64Recomp::SectionImport,
.section_offset = static_cast<uint32_t>(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<const char> data, std::span<const uint8_t> binary, const std::unordered_map<uint32_t, uint16_t>& sections_by_vrom, const Context& reference_context, ModContext& mod_context_out) {
N64Recomp::ModSymbolsError N64Recomp::parse_mod_symbols(std::span<const char> data, std::span<const uint8_t> binary, const std::unordered_map<uint32_t, uint16_t>& sections_by_vrom, const Context& reference_context, Context& mod_context_out) {
size_t offset = 0;
mod_context_out = {};
const FileHeader* header = reinterpret_data<FileHeader>(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<const char> 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<uint8_t>& vec, const std::string& data) {
memcpy(vec.data() + start_size, data.data(), data.size());
}
std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& mod_context) {
std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::Context& context) {
std::vector<uint8_t> 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<uint8_t> 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<uint32_t>(context.sections.size()),
.num_replacements = static_cast<uint32_t>(mod_context.replacements.size()),
.num_replacements = static_cast<uint32_t>(context.replacements.size()),
.num_exports = static_cast<uint32_t>(num_exported_funcs),
.num_dependencies = static_cast<uint32_t>(num_dependencies),
.num_imports = static_cast<uint32_t>(num_imported_funcs),
@ -404,8 +414,8 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m
std::vector<uint32_t> 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<uint32_t>(ret.size() - strings_start);
vec_put(ret, exported_func.name);
@ -415,7 +425,7 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m
std::vector<uint32_t> 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<uint32_t>(ret.size() - strings_start);
vec_put(ret, dependency.mod_id);
@ -425,17 +435,12 @@ std::vector<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m
std::vector<uint32_t> imported_func_name_positions{};
imported_func_name_positions.resize(num_imported_funcs);
std::unordered_map<std::string, uint32_t> 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<uint32_t>(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<uint8_t> 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<uint8_t> N64Recomp::symbols_to_bin_v1(const N64Recomp::ModContext& m
RelocV1 reloc_out {
.section_offset = cur_reloc.address - cur_section.ram_addr,
.type = static_cast<uint32_t>(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<uint8_t> 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<uint8_t> 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<uint32_t>(function_index),
@ -528,7 +533,7 @@ std::vector<uint8_t> 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<uint8_t> 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<uint32_t>(imported_func_name.size()),
.dependency = static_cast<uint32_t>(dependency_index)
.name_size = static_cast<uint32_t>(imported_func.base.name.size()),
.dependency = static_cast<uint32_t>(imported_func.dependency_index)
};
vec_put(ret, &import_out);

View file

@ -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, &section, &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, &section, &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;