mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2026-04-28 04:51:43 +00:00
Add strict mode and ability to generate exports for normal recompilation (for patches)
This commit is contained in:
parent
8fcf73de4d
commit
623013a371
8 changed files with 103 additions and 5 deletions
|
|
@ -139,9 +139,11 @@ int main(int argc, const char** argv) {
|
||||||
RabbitizerConfig_Cfg.pseudos.pseudoNot = false;
|
RabbitizerConfig_Cfg.pseudos.pseudoNot = false;
|
||||||
RabbitizerConfig_Cfg.pseudos.pseudoBal = false;
|
RabbitizerConfig_Cfg.pseudos.pseudoBal = false;
|
||||||
|
|
||||||
|
std::string recomp_include = "#include \"librecomp/recomp.h\"";
|
||||||
|
|
||||||
bool should_write_header = true;
|
bool should_write_header = true;
|
||||||
for (const auto& func : mod_context.base_context.functions) {
|
for (const auto& func : mod_context.base_context.functions) {
|
||||||
N64Recomp::recompile_function(mod_context.base_context, func, output_file, static_funcs_by_section, should_write_header);
|
N64Recomp::recompile_function(mod_context.base_context, func, recomp_include, output_file, static_funcs_by_section, should_write_header);
|
||||||
should_write_header = false;
|
should_write_header = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -307,9 +307,9 @@ N64Recomp::ModContext build_mod_context(const N64Recomp::Context& input_context,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for special section names.
|
// Check for special section names.
|
||||||
bool patch_section = cur_section.name == ".recomp_patch";
|
bool patch_section = cur_section.name == N64Recomp::PatchSectionName;
|
||||||
bool force_patch_section = cur_section.name == ".recomp_force_patch";
|
bool force_patch_section = cur_section.name == N64Recomp::ForcedPatchSectionName;
|
||||||
bool export_section = cur_section.name == ".recomp_export";
|
bool export_section = cur_section.name == N64Recomp::ExportSectionName;
|
||||||
|
|
||||||
// Add the functions from the current input section to the current output section.
|
// Add the functions from the current input section to the current output section.
|
||||||
auto& section_out = ret.base_context.sections[output_section_index];
|
auto& section_out = ret.base_context.sections[output_section_index];
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@ namespace N64Recomp {
|
||||||
constexpr uint16_t SectionSelf = (uint16_t)-1;
|
constexpr uint16_t SectionSelf = (uint16_t)-1;
|
||||||
constexpr uint16_t SectionAbsolute = (uint16_t)-2;
|
constexpr uint16_t SectionAbsolute = (uint16_t)-2;
|
||||||
constexpr uint16_t SectionImport = (uint16_t)-3; // Imported symbols for mods
|
constexpr uint16_t SectionImport = (uint16_t)-3; // Imported symbols for mods
|
||||||
|
constexpr std::string_view PatchSectionName = ".recomp_patch";
|
||||||
|
constexpr std::string_view ForcedPatchSectionName = ".recomp_force_patch";
|
||||||
|
constexpr std::string_view ExportSectionName = ".recomp_export";
|
||||||
struct Section {
|
struct Section {
|
||||||
uint32_t rom_addr = 0;
|
uint32_t rom_addr = 0;
|
||||||
uint32_t ram_addr = 0;
|
uint32_t ram_addr = 0;
|
||||||
|
|
|
||||||
|
|
@ -408,6 +408,25 @@ N64Recomp::Config::Config(const char* path) {
|
||||||
const toml::array* array = data_reference_syms_file_data.as_array();
|
const toml::array* array = data_reference_syms_file_data.as_array();
|
||||||
data_reference_syms_file_paths = get_data_syms_paths(array, basedir);
|
data_reference_syms_file_paths = get_data_syms_paths(array, basedir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Control whether the recompiler emits exported symbol data.
|
||||||
|
std::optional<bool> allow_exports_opt = input_data["allow_exports"].value<bool>();
|
||||||
|
if (allow_exports_opt.has_value()) {
|
||||||
|
allow_exports = allow_exports_opt.value();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
allow_exports = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable patch recompilation strict mode, which ensures that patch functions are marked and that other functions are not marked as patches.
|
||||||
|
std::optional<bool> strict_patch_mode_opt = input_data["strict_patch_mode"].value<bool>();
|
||||||
|
if (strict_patch_mode_opt.has_value()) {
|
||||||
|
strict_patch_mode = strict_patch_mode_opt.value();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Default to strict patch mode if a function reference symbol file was provided.
|
||||||
|
strict_patch_mode = !func_reference_syms_file_path.empty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (const toml::parse_error& err) {
|
catch (const toml::parse_error& err) {
|
||||||
std::cerr << "Syntax error parsing toml: " << *err.source().path << " (" << err.source().begin << "):\n" << err.description() << std::endl;
|
std::cerr << "Syntax error parsing toml: " << *err.source().path << " (" << err.source().begin << "):\n" << err.description() << std::endl;
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@ namespace N64Recomp {
|
||||||
bool single_file_output;
|
bool single_file_output;
|
||||||
bool use_absolute_symbols;
|
bool use_absolute_symbols;
|
||||||
bool unpaired_lo16_warnings;
|
bool unpaired_lo16_warnings;
|
||||||
|
bool allow_exports;
|
||||||
|
bool strict_patch_mode;
|
||||||
std::filesystem::path elf_path;
|
std::filesystem::path elf_path;
|
||||||
std::filesystem::path symbols_file_path;
|
std::filesystem::path symbols_file_path;
|
||||||
std::filesystem::path func_reference_syms_file_path;
|
std::filesystem::path func_reference_syms_file_path;
|
||||||
|
|
|
||||||
16
src/elf.cpp
16
src/elf.cpp
|
|
@ -431,7 +431,21 @@ ELFIO::section* read_sections(N64Recomp::Context& context, const N64Recomp::ElfP
|
||||||
else {
|
else {
|
||||||
reloc_out.reference_symbol = false;
|
reloc_out.reference_symbol = false;
|
||||||
reloc_out.target_section = rel_symbol_section_index;
|
reloc_out.target_section = rel_symbol_section_index;
|
||||||
rel_section_vram = context.sections[rel_symbol_section_index].ram_addr;
|
// Handle special sections.
|
||||||
|
if (rel_symbol_section_index >= context.sections.size()) {
|
||||||
|
switch (rel_symbol_section_index) {
|
||||||
|
case ELFIO::SHN_ABS:
|
||||||
|
rel_section_vram = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fmt::print(stderr, "Reloc {} references symbol {} which is in an unknown section 0x{:04X}!\n",
|
||||||
|
i, rel_symbol_name, rel_symbol_section_index);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rel_section_vram = context.sections[rel_symbol_section_index].ram_addr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reloc pairing, see MIPS System V ABI documentation page 4-18 (https://refspecs.linuxfoundation.org/elf/mipsabi.pdf)
|
// Reloc pairing, see MIPS System V ABI documentation page 4-18 (https://refspecs.linuxfoundation.org/elf/mipsabi.pdf)
|
||||||
|
|
|
||||||
57
src/main.cpp
57
src/main.cpp
|
|
@ -568,6 +568,10 @@ int main(int argc, char** argv) {
|
||||||
open_new_output_file();
|
open_new_output_file();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<size_t> export_function_indices{};
|
||||||
|
|
||||||
|
bool failed_strict_mode = false;
|
||||||
|
|
||||||
//#pragma omp parallel for
|
//#pragma omp parallel for
|
||||||
for (size_t i = 0; i < context.functions.size(); i++) {
|
for (size_t i = 0; i < context.functions.size(); i++) {
|
||||||
const auto& func = context.functions[i];
|
const auto& func = context.functions[i];
|
||||||
|
|
@ -576,6 +580,33 @@ int main(int argc, char** argv) {
|
||||||
fmt::print(func_header_file,
|
fmt::print(func_header_file,
|
||||||
"void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name);
|
"void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name);
|
||||||
bool result;
|
bool result;
|
||||||
|
const auto& func_section = context.sections[func.section_index];
|
||||||
|
// Apply strict patch mode validation if enabled.
|
||||||
|
if (config.strict_patch_mode) {
|
||||||
|
bool in_normal_patch_section = func_section.name == N64Recomp::PatchSectionName;
|
||||||
|
bool in_force_patch_section = func_section.name == N64Recomp::ForcedPatchSectionName;
|
||||||
|
bool in_patch_section = in_normal_patch_section || in_force_patch_section;
|
||||||
|
bool reference_symbol_found = context.reference_symbols_by_name.contains(func.name);
|
||||||
|
|
||||||
|
// This is a patch function, but no corresponding symbol was found in the original symbol list.
|
||||||
|
if (in_patch_section && !reference_symbol_found) {
|
||||||
|
fmt::print(stderr, "Function {} is marked as a replacement, but no function with the same name was found in the reference symbols!\n", func.name);
|
||||||
|
failed_strict_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// This is not a patch function, but it has the same name as a function in the original symbol list.
|
||||||
|
else if (!in_patch_section && reference_symbol_found) {
|
||||||
|
fmt::print(stderr, "Function {} is not marked as a replacement, but a function with the same name was found in the reference symbols!\n", func.name);
|
||||||
|
failed_strict_mode = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if this is an export and add it to the list if exports are enabled.
|
||||||
|
if (config.allow_exports && func_section.name == N64Recomp::ExportSectionName) {
|
||||||
|
export_function_indices.push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recompile the function.
|
||||||
if (config.single_file_output || config.functions_per_output_file > 1) {
|
if (config.single_file_output || config.functions_per_output_file > 1) {
|
||||||
result = N64Recomp::recompile_function(context, func, config.recomp_include, current_output_file, static_funcs_by_section, false);
|
result = N64Recomp::recompile_function(context, func, config.recomp_include, current_output_file, static_funcs_by_section, false);
|
||||||
if (!config.single_file_output) {
|
if (!config.single_file_output) {
|
||||||
|
|
@ -598,6 +629,15 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (failed_strict_mode) {
|
||||||
|
if (config.single_file_output || config.functions_per_output_file > 1) {
|
||||||
|
current_output_file.close();
|
||||||
|
std::error_code ec;
|
||||||
|
std::filesystem::remove(config.output_func_path / config.elf_path.stem().replace_extension(".c"), ec);
|
||||||
|
}
|
||||||
|
exit_failure("Strict mode validation failed!\n");
|
||||||
|
}
|
||||||
|
|
||||||
for (size_t section_index = 0; section_index < context.sections.size(); section_index++) {
|
for (size_t section_index = 0; section_index < context.sections.size(); section_index++) {
|
||||||
auto& section = context.sections[section_index];
|
auto& section = context.sections[section_index];
|
||||||
auto& section_funcs = section.function_addrs;
|
auto& section_funcs = section.function_addrs;
|
||||||
|
|
@ -789,6 +829,23 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt::print(overlay_file, "}};\n");
|
fmt::print(overlay_file, "}};\n");
|
||||||
|
|
||||||
|
if (config.allow_exports) {
|
||||||
|
fmt::print(overlay_file,
|
||||||
|
"\n"
|
||||||
|
"static FunctionExport export_table[] = {{\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (size_t func_index : export_function_indices) {
|
||||||
|
const auto& func = context.functions[func_index];
|
||||||
|
fmt::print(overlay_file, " {{ \"{}\", 0x{:08X} }},\n", func.name, func.vram);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a dummy element at the end to ensure the array has a valid length because C doesn't allow zero-size arrays.
|
||||||
|
fmt::print(overlay_file, " {{ NULL, 0 }}\n");
|
||||||
|
|
||||||
|
fmt::print(overlay_file, "}};\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config.output_binary_path.empty()) {
|
if (!config.output_binary_path.empty()) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ struct FileSubHeaderV1 {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SectionHeaderV1 {
|
struct SectionHeaderV1 {
|
||||||
|
uint32_t flags;
|
||||||
uint32_t file_offset;
|
uint32_t file_offset;
|
||||||
uint32_t vram;
|
uint32_t vram;
|
||||||
uint32_t rom_size;
|
uint32_t rom_size;
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue