Added toml11 and implemented initial config file parsing, replaces command-line arg inputs
This commit is contained in:
parent
f4324ee599
commit
fba0085946
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -7,3 +7,6 @@
|
|||
[submodule "lib/fmt"]
|
||||
path = lib/fmt
|
||||
url = https://github.com/fmtlib/fmt
|
||||
[submodule "lib/toml11"]
|
||||
path = lib/toml11
|
||||
url = https://github.com/ToruNiina/toml11
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(SolutionDir)lib\toml11;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<OpenMPSupport>true</OpenMPSupport>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
@ -94,7 +94,7 @@
|
|||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(SolutionDir)lib\toml11;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<OpenMPSupport>true</OpenMPSupport>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
@ -109,7 +109,7 @@
|
|||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(SolutionDir)lib\toml11;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<OpenMPSupport>true</OpenMPSupport>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
@ -120,7 +120,7 @@
|
|||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<LanguageStandard>stdcpp20</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)lib\rabbitizer\include;$(SolutionDir)lib\rabbitizer\cplusplus\include;$(SolutionDir)lib\ELFIO;$(SolutionDir)lib\fmt\include;$(SolutionDir)lib\toml11;$(ProjectDir)include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<OpenMPSupport>true</OpenMPSupport>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
|
@ -138,6 +138,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\analysis.cpp" />
|
||||
<ClCompile Include="src\config.cpp" />
|
||||
<ClCompile Include="src\main.cpp" />
|
||||
<ClCompile Include="src\recompilation.cpp" />
|
||||
</ItemGroup>
|
||||
|
|
|
@ -24,6 +24,9 @@
|
|||
<ClCompile Include="src\analysis.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\config.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="lib\ELFIO\elfio\elfio.hpp">
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
#include <unordered_map>
|
||||
#include <span>
|
||||
#include <unordered_set>
|
||||
#include <filesystem>
|
||||
#include "rabbitizer.hpp"
|
||||
#include "elfio/elfio.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
|
@ -22,6 +24,29 @@ constexpr uint32_t byteswap(uint32_t val) {
|
|||
|
||||
namespace RecompPort {
|
||||
|
||||
// Potential argument types for function declarations
|
||||
enum class FunctionArgType {
|
||||
u32,
|
||||
s32,
|
||||
};
|
||||
|
||||
// Mapping of function name to argument types
|
||||
using DeclaredFunctionMap = std::unordered_map<std::string, std::vector<FunctionArgType>>;
|
||||
|
||||
struct Config {
|
||||
int32_t entrypoint;
|
||||
std::filesystem::path elf_path;
|
||||
std::filesystem::path output_func_path;
|
||||
std::filesystem::path relocatable_sections_path;
|
||||
std::vector<std::string> stubbed_funcs;
|
||||
DeclaredFunctionMap declared_funcs;
|
||||
|
||||
Config(const char* path);
|
||||
bool good() { return !bad; }
|
||||
private:
|
||||
bool bad;
|
||||
};
|
||||
|
||||
struct JumpTable {
|
||||
uint32_t vram;
|
||||
uint32_t addend_reg;
|
||||
|
@ -68,14 +93,14 @@ namespace RecompPort {
|
|||
};
|
||||
|
||||
struct Section {
|
||||
ELFIO::Elf_Xword rom_addr;
|
||||
ELFIO::Elf64_Addr ram_addr;
|
||||
ELFIO::Elf_Xword size;
|
||||
ELFIO::Elf_Xword rom_addr = 0;
|
||||
ELFIO::Elf64_Addr ram_addr = 0;
|
||||
ELFIO::Elf_Xword size = 0;
|
||||
std::vector<uint32_t> function_addrs;
|
||||
std::vector<Reloc> relocs;
|
||||
std::string name;
|
||||
bool executable;
|
||||
bool relocatable;
|
||||
bool executable = false;
|
||||
bool relocatable = false;
|
||||
};
|
||||
|
||||
struct FunctionStats {
|
||||
|
@ -106,7 +131,7 @@ namespace RecompPort {
|
|||
};
|
||||
|
||||
bool analyze_function(const Context& context, const Function& function, const std::vector<rabbitizer::InstructionCpu>& instructions, FunctionStats& stats);
|
||||
bool recompile_function(const Context& context, const Function& func, std::string_view output_path, std::span<std::vector<uint32_t>> static_funcs);
|
||||
bool recompile_function(const Context& context, const Function& func, const std::filesystem::path& output_path, std::span<std::vector<uint32_t>> static_funcs);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
1
lib/toml11
Submodule
1
lib/toml11
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit d47fe788bcb08c9d0d2a73954a0dfaf512964fdc
|
126
src/config.cpp
Normal file
126
src/config.cpp
Normal file
|
@ -0,0 +1,126 @@
|
|||
#include <source_location>
|
||||
|
||||
#include "toml.hpp"
|
||||
#include "fmt/format.h"
|
||||
#include "recomp_port.h"
|
||||
|
||||
void get_stubbed_funcs(std::vector<std::string>& stubbed_funcs, const toml::value& patches_data) {
|
||||
// Check if the stubs array exists.
|
||||
const auto& stubs_data = toml::find_or<toml::value>(patches_data, "stubs", toml::value{});
|
||||
|
||||
if (stubs_data.type() == toml::value_t::empty) {
|
||||
// No stubs, nothing to do here.
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the stubs array as an array type.
|
||||
const toml::array& stubs_array = stubs_data.as_array();
|
||||
|
||||
// Make room for all the stubs in the array.
|
||||
stubbed_funcs.resize(stubs_array.size());
|
||||
|
||||
// Gather the stubs and place them into the array.
|
||||
for (size_t stub_idx = 0; stub_idx < stubs_array.size(); stub_idx++) {
|
||||
// Copy the entry into the stubbed function list.
|
||||
stubbed_funcs[stub_idx] = stubs_array[stub_idx].as_string();
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, RecompPort::FunctionArgType> arg_type_map{
|
||||
{"u32", RecompPort::FunctionArgType::u32},
|
||||
{"s32", RecompPort::FunctionArgType::s32},
|
||||
};
|
||||
|
||||
std::vector<RecompPort::FunctionArgType> parse_args(const toml::array& args_in) {
|
||||
std::vector<RecompPort::FunctionArgType> ret(args_in.size());
|
||||
|
||||
for (size_t arg_idx = 0; arg_idx < args_in.size(); arg_idx++) {
|
||||
const toml::value& arg_val = args_in[arg_idx];
|
||||
const std::string& arg_str = arg_val.as_string();
|
||||
|
||||
// Check if the argument type string is valid.
|
||||
auto type_find = arg_type_map.find(arg_str);
|
||||
if (type_find == arg_type_map.end()) {
|
||||
// It's not, so throw an error (and make it look like a normal toml one).
|
||||
throw toml::type_error(toml::detail::format_underline(
|
||||
std::string{ std::source_location::current().function_name() } + ": invalid function arg type", {
|
||||
{arg_val.location(), ""}
|
||||
}), arg_val.location());
|
||||
}
|
||||
ret[arg_idx] = type_find->second;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void get_declared_funcs(RecompPort::DeclaredFunctionMap& declared_funcs, const toml::value& patches_data) {
|
||||
// Check if the func array exists.
|
||||
const toml::value& funcs_data = toml::find_or<toml::value>(patches_data, "func", toml::value{});
|
||||
if (funcs_data.type() == toml::value_t::empty) {
|
||||
// No func array, nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the funcs array as an array type.
|
||||
const toml::array& funcs_array = funcs_data.as_array();
|
||||
|
||||
// Reserve room for all the funcs in the map.
|
||||
declared_funcs.reserve(funcs_data.size());
|
||||
for (const toml::value& cur_func_val : funcs_array) {
|
||||
const std::string& func_name = toml::find<std::string>(cur_func_val, "name");
|
||||
const toml::array& args_in = toml::find<toml::array>(cur_func_val, "args");
|
||||
|
||||
declared_funcs.emplace(func_name, parse_args(args_in));
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path concat_if_not_empty(const std::filesystem::path& parent, const std::filesystem::path& child) {
|
||||
if (!child.empty()) {
|
||||
return parent / child;
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
RecompPort::Config::Config(const char* path) {
|
||||
// Start this config out as bad so that it has to finish parsing without errors to be good.
|
||||
entrypoint = 0;
|
||||
bad = true;
|
||||
|
||||
try {
|
||||
const toml::value config_data = toml::parse(path);
|
||||
std::filesystem::path basedir = std::filesystem::path{ path }.parent_path();
|
||||
|
||||
// Input section (required)
|
||||
const toml::value& input_data = toml::find<toml::value>(config_data, "input");
|
||||
|
||||
entrypoint = toml::find<int32_t>(input_data, "entrypoint");
|
||||
elf_path = concat_if_not_empty(basedir, toml::find<std::string>(input_data, "elf_path"));
|
||||
output_func_path = concat_if_not_empty(basedir, toml::find<std::string>(input_data, "output_func_path"));
|
||||
relocatable_sections_path = concat_if_not_empty(basedir, toml::find_or<std::string>(input_data, "relocatable_sections_path", ""));
|
||||
|
||||
// Patches section (optional)
|
||||
const toml::value& patches_data = toml::find_or<toml::value>(config_data, "patches", toml::value{});
|
||||
if (patches_data.type() == toml::value_t::empty) {
|
||||
// Stubs array (optional)
|
||||
get_stubbed_funcs(stubbed_funcs, patches_data);
|
||||
|
||||
// Functions (optional)
|
||||
get_declared_funcs(declared_funcs, patches_data);
|
||||
}
|
||||
}
|
||||
catch (const toml::syntax_error& err) {
|
||||
fmt::print(stderr, "Syntax error in config file on line {}, full error:\n{}\n", err.location().line(), err.what());
|
||||
return;
|
||||
}
|
||||
catch (const toml::type_error& err) {
|
||||
fmt::print(stderr, "Incorrect type in config file on line {}, full error:\n{}\n", err.location().line(), err.what());
|
||||
return;
|
||||
}
|
||||
catch (const std::out_of_range& err) {
|
||||
fmt::print(stderr, "Missing value in config file, full error:\n{}\n", err.what());
|
||||
return;
|
||||
}
|
||||
|
||||
// No errors occured, so mark this config file as good.
|
||||
bad = false;
|
||||
}
|
59
src/main.cpp
59
src/main.cpp
|
@ -10,7 +10,6 @@
|
|||
#include "fmt/ostream.h"
|
||||
|
||||
#include "recomp_port.h"
|
||||
#include "main.h"
|
||||
#include <set>
|
||||
|
||||
std::unordered_set<std::string> reimplemented_funcs{
|
||||
|
@ -964,7 +963,7 @@ void analyze_sections(RecompPort::Context& context, const ELFIO::elfio& elf_file
|
|||
);
|
||||
}
|
||||
|
||||
bool read_list_file(const char* filename, std::unordered_set<std::string>& entries_out) {
|
||||
bool read_list_file(const std::filesystem::path& filename, std::unordered_set<std::string>& entries_out) {
|
||||
std::ifstream input_file{ filename };
|
||||
if (!input_file.good()) {
|
||||
return false;
|
||||
|
@ -980,47 +979,41 @@ bool read_list_file(const char* filename, std::unordered_set<std::string>& entri
|
|||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 4 || argc > 5) {
|
||||
fmt::print("Usage: {} [input elf file] [entrypoint RAM address] [output path] [relocatable sections list file (optional)]\n", argv[0]);
|
||||
auto exit_failure = [] (const std::string& error_str) {
|
||||
fmt::print(stderr, error_str);
|
||||
std::exit(EXIT_FAILURE);
|
||||
};
|
||||
|
||||
if (argc != 2) {
|
||||
fmt::print("Usage: {} [config file]\n", argv[0]);
|
||||
std::exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
const char* config_path = argv[1];
|
||||
|
||||
RecompPort::Config config{ config_path };
|
||||
if (!config.good()) {
|
||||
exit_failure(fmt::format("Failed to load config file: {}\n", config_path));
|
||||
}
|
||||
|
||||
ELFIO::elfio elf_file;
|
||||
RabbitizerConfig_Cfg.pseudos.pseudoMove = false;
|
||||
RabbitizerConfig_Cfg.pseudos.pseudoBeqz = false;
|
||||
RabbitizerConfig_Cfg.pseudos.pseudoBnez = false;
|
||||
RabbitizerConfig_Cfg.pseudos.pseudoNot = false;
|
||||
|
||||
auto exit_failure = [] (const std::string& error_str) {
|
||||
fmt::print(stderr, error_str);
|
||||
std::exit(EXIT_FAILURE);
|
||||
};
|
||||
|
||||
std::unordered_set<std::string> relocatable_sections{};
|
||||
|
||||
if (argc == 5) {
|
||||
if (!read_list_file(argv[4], relocatable_sections)) {
|
||||
if (!config.relocatable_sections_path.empty()) {
|
||||
if (!read_list_file(config.relocatable_sections_path, relocatable_sections)) {
|
||||
exit_failure("Failed to load the relocatable section list file: " + std::string(argv[4]) + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
std::string output_dir{ argv[3] };
|
||||
std::string elf_name{ argv[1] };
|
||||
|
||||
if (!output_dir.ends_with('/')) {
|
||||
output_dir += "/";
|
||||
}
|
||||
|
||||
if (!elf_file.load(elf_name)) {
|
||||
if (!elf_file.load(config.elf_path.string())) {
|
||||
exit_failure("Failed to load provided elf file\n");
|
||||
}
|
||||
|
||||
char* end;
|
||||
const uint32_t entrypoint = (uint32_t)strtoul(argv[2], &end, 0);
|
||||
if (argv[2] == end) {
|
||||
exit_failure("Invalid entrypoint value: " + std::string(argv[2]) + "\n");
|
||||
}
|
||||
|
||||
if (elf_file.get_class() != ELFIO::ELFCLASS32) {
|
||||
exit_failure("Incorrect elf class\n");
|
||||
}
|
||||
|
@ -1044,7 +1037,7 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
|
||||
// Read all of the symbols in the elf and look for the entrypoint function
|
||||
bool found_entrypoint_func = read_symbols(context, elf_file, symtab_section, entrypoint);
|
||||
bool found_entrypoint_func = read_symbols(context, elf_file, symtab_section, config.entrypoint);
|
||||
|
||||
if (!found_entrypoint_func) {
|
||||
exit_failure("Could not find entrypoint function\n");
|
||||
|
@ -1052,8 +1045,8 @@ int main(int argc, char** argv) {
|
|||
|
||||
fmt::print("Function count: {}\n", context.functions.size());
|
||||
|
||||
std::ofstream lookup_file{ output_dir + "lookup.cpp" };
|
||||
std::ofstream func_header_file{ output_dir + "funcs.h" };
|
||||
std::ofstream lookup_file{ config.output_func_path / "lookup.cpp" };
|
||||
std::ofstream func_header_file{ config.output_func_path / "funcs.h" };
|
||||
|
||||
fmt::print(lookup_file,
|
||||
//"#include <utility>\n"
|
||||
|
@ -1085,7 +1078,7 @@ int main(int argc, char** argv) {
|
|||
"void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name);
|
||||
//fmt::print(lookup_file,
|
||||
// " {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
||||
if (RecompPort::recompile_function(context, func, output_dir + func.name + ".c", static_funcs_by_section) == false) {
|
||||
if (RecompPort::recompile_function(context, func, config.output_func_path / (func.name + ".c"), static_funcs_by_section) == false) {
|
||||
//lookup_file.clear();
|
||||
fmt::print(stderr, "Error recompiling {}\n", func.name);
|
||||
std::exit(EXIT_FAILURE);
|
||||
|
@ -1151,7 +1144,7 @@ int main(int argc, char** argv) {
|
|||
"void {}(uint8_t* rdram, recomp_context* ctx);\n", func.name);
|
||||
//fmt::print(lookup_file,
|
||||
// " {{ 0x{:08X}u, {} }},\n", func.vram, func.name);
|
||||
if (RecompPort::recompile_function(context, func, output_dir + func.name + ".c", static_funcs_by_section) == false) {
|
||||
if (RecompPort::recompile_function(context, func, config.output_func_path / (func.name + ".c"), static_funcs_by_section) == false) {
|
||||
//lookup_file.clear();
|
||||
fmt::print(stderr, "Error recompiling {}\n", func.name);
|
||||
std::exit(EXIT_FAILURE);
|
||||
|
@ -1167,8 +1160,8 @@ int main(int argc, char** argv) {
|
|||
"\n"
|
||||
"const char* get_rom_name() {{ return \"{}\"; }}\n"
|
||||
"\n",
|
||||
entrypoint,
|
||||
std::filesystem::path{ elf_name }.filename().replace_extension(".z64").string()
|
||||
config.entrypoint,
|
||||
config.elf_path.filename().replace_extension(".z64").string()
|
||||
);
|
||||
|
||||
fmt::print(func_header_file,
|
||||
|
@ -1179,7 +1172,7 @@ int main(int argc, char** argv) {
|
|||
);
|
||||
|
||||
{
|
||||
std::ofstream overlay_file(output_dir + "recomp_overlays.inl");
|
||||
std::ofstream overlay_file(config.output_func_path / "recomp_overlays.inl");
|
||||
std::string section_load_table = "static SectionTableEntry section_table[] = {\n";
|
||||
|
||||
fmt::print(overlay_file,
|
||||
|
|
|
@ -976,14 +976,14 @@ bool process_instruction(const RecompPort::Context& context, const RecompPort::F
|
|||
return true;
|
||||
}
|
||||
|
||||
bool RecompPort::recompile_function(const RecompPort::Context& context, const RecompPort::Function& func, std::string_view output_path, std::span<std::vector<uint32_t>> static_funcs_out) {
|
||||
bool RecompPort::recompile_function(const RecompPort::Context& context, const RecompPort::Function& func, const std::filesystem::path& output_path, std::span<std::vector<uint32_t>> static_funcs_out) {
|
||||
//fmt::print("Recompiling {}\n", func.name);
|
||||
std::vector<rabbitizer::InstructionCpu> instructions;
|
||||
|
||||
// Open the output file and write the file header
|
||||
std::ofstream output_file{ output_path.data() };
|
||||
std::ofstream output_file{ output_path };
|
||||
if (!output_file.good()) {
|
||||
fmt::print(stderr, "Failed to open file for writing: {}\n", output_path);
|
||||
fmt::print(stderr, "Failed to open file for writing: {}\n", output_path.string() );
|
||||
return false;
|
||||
}
|
||||
fmt::print(output_file,
|
||||
|
@ -1065,7 +1065,7 @@ bool RecompPort::recompile_function(const RecompPort::Context& context, const Re
|
|||
|
||||
// Process the current instruction and check for errors
|
||||
if (process_instruction(context, func, stats, skipped_insns, instr_index, instructions, output_file, false, needs_link_branch, num_link_branches, reloc_index, needs_link_branch, is_branch_likely, static_funcs_out) == false) {
|
||||
fmt::print(stderr, "Error in recompilation, clearing {}\n", output_path);
|
||||
fmt::print(stderr, "Error in recompilation, clearing {}\n", output_path.string() );
|
||||
output_file.clear();
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue