Symbol file toml update (#52)

* Symbol input file mechanism

* Migration to new toml lib

---------

Co-authored-by: dcvz <david@dcvz.io>
This commit is contained in:
Mr-Wiseguy 2024-05-16 22:33:08 -04:00 committed by GitHub
parent 26c5c2cbb8
commit e0e52d1fc3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 748 additions and 367 deletions

View file

@ -1018,7 +1018,6 @@ ELFIO::section* read_sections(RecompPort::Context& context, const RecompPort::Co
reloc_out.address = rel_offset;
reloc_out.symbol_index = rel_symbol;
reloc_out.type = static_cast<RecompPort::RelocType>(rel_type);
reloc_out.needs_relocation = false;
std::string rel_symbol_name;
ELFIO::Elf64_Addr rel_symbol_value;
@ -1033,12 +1032,6 @@ ELFIO::section* read_sections(RecompPort::Context& context, const RecompPort::Co
reloc_out.target_section = rel_symbol_section_index;
bool rel_needs_relocation = false;
if (rel_symbol_section_index < context.sections.size()) {
rel_needs_relocation = context.sections[rel_symbol_section_index].relocatable;
}
// Reloc pairing, see MIPS System V ABI documentation page 4-18 (https://refspecs.linuxfoundation.org/elf/mipsabi.pdf)
if (reloc_out.type == RecompPort::RelocType::R_MIPS_LO16) {
if (prev_hi) {
@ -1212,6 +1205,79 @@ bool recompile_single_function(const RecompPort::Context& context, const RecompP
return true;
}
std::vector<std::string> reloc_names {
"R_MIPS_NONE ",
"R_MIPS_16",
"R_MIPS_32",
"R_MIPS_REL32",
"R_MIPS_26",
"R_MIPS_HI16",
"R_MIPS_LO16",
"R_MIPS_GPREL16",
};
void dump_context(const RecompPort::Context& context, const std::filesystem::path& path) {
std::ofstream context_file {path};
for (size_t section_index = 0; section_index < context.sections.size(); section_index++) {
const RecompPort::Section& section = context.sections[section_index];
const std::vector<size_t>& section_funcs = context.section_functions[section_index];
if (!section_funcs.empty()) {
fmt::print(context_file,
"# Autogenerated from an ELF via N64Recomp\n"
"[[section]]\n"
"name = \"{}\"\n"
"rom = 0x{:08X}\n"
"vram = 0x{:08X}\n"
"size = 0x{:X}\n"
"\n",
section.name, section.rom_addr, section.ram_addr, section.size);
if (!section.relocs.empty()) {
fmt::print(context_file, "relocs = [\n");
for (const RecompPort::Reloc& reloc : section.relocs) {
if (reloc.target_section == section_index || reloc.target_section == section.bss_section_index) {
// TODO allow MIPS32 relocs for TLB mapping support.
if (reloc.type == RecompPort::RelocType::R_MIPS_HI16 || reloc.type == RecompPort::RelocType::R_MIPS_LO16) {
fmt::print(context_file, " {{ type = \"{}\", vram = 0x{:08X}, target_vram = 0x{:08X} }},\n",
reloc_names[static_cast<int>(reloc.type)], reloc.address, reloc.target_address);
}
}
}
fmt::print(context_file, "]\n\n");
}
fmt::print(context_file, "functions = [\n");
for (const size_t& function_index : section_funcs) {
const RecompPort::Function& func = context.functions[function_index];
fmt::print(context_file, " {{ name = \"{}\", vram = 0x{:08X}, size = 0x{:X} }},\n",
func.name, func.vram, func.words.size() * sizeof(func.words[0]));
}
fmt::print(context_file, "]\n\n");
}
}
}
static std::vector<uint8_t> read_file(const std::filesystem::path& path) {
std::vector<uint8_t> ret;
std::ifstream file{ path, std::ios::binary};
if (file.good()) {
file.seekg(0, std::ios::end);
ret.resize(file.tellg());
file.seekg(0, std::ios::beg);
file.read(reinterpret_cast<char*>(ret.data()), ret.size());
}
return ret;
}
int main(int argc, char** argv) {
auto exit_failure = [] (const std::string& error_str) {
fmt::vprint(stderr, error_str, fmt::make_format_args());
@ -1230,7 +1296,6 @@ int main(int argc, char** argv) {
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;
@ -1248,52 +1313,118 @@ int main(int argc, char** argv) {
std::unordered_set<std::string> relocatable_sections{};
relocatable_sections.insert(relocatable_sections_ordered.begin(), relocatable_sections_ordered.end());
if (!elf_file.load(config.elf_path.string())) {
exit_failure("Failed to load provided elf file\n");
RecompPort::Context context{};
if (!config.elf_path.empty() && !config.symbols_file_path.empty()) {
exit_failure("Config file cannot provide both an elf and a symbols file\n");
}
if (elf_file.get_class() != ELFIO::ELFCLASS32) {
exit_failure("Incorrect elf class\n");
// Build a context from the provided elf file.
if (!config.elf_path.empty()) {
ELFIO::elfio elf_file;
if (!elf_file.load(config.elf_path.string())) {
exit_failure("Failed to load provided elf file\n");
}
if (elf_file.get_class() != ELFIO::ELFCLASS32) {
exit_failure("Incorrect elf class\n");
}
if (elf_file.get_encoding() != ELFIO::ELFDATA2MSB) {
exit_failure("Incorrect endianness\n");
}
context = { elf_file };
context.relocatable_sections = std::move(relocatable_sections);
// Read all of the sections in the elf and look for the symbol table section
ELFIO::section* symtab_section = read_sections(context, config, elf_file);
// Search the sections to see if any are overlays or TLB-mapped
analyze_sections(context, elf_file);
// If no symbol table was found then exit
if (symtab_section == nullptr) {
exit_failure("No symbol table section found\n");
}
// Manually sized functions
for (const auto& func_size : config.manual_func_sizes) {
context.manually_sized_funcs.emplace(func_size.func_name, func_size.size_bytes);
}
// 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, config.entrypoint, config.has_entrypoint, config.use_absolute_symbols);
// Add any manual functions
add_manual_functions(context, elf_file, config.manual_functions);
if (config.has_entrypoint && !found_entrypoint_func) {
exit_failure("Could not find entrypoint function\n");
}
}
// Build a context from the provided symbols file.
else if (!config.symbols_file_path.empty()) {
if (config.rom_file_path.empty()) {
exit_failure("A ROM file must be provided when using a symbols file\n");
}
std::vector<uint8_t> rom = read_file(config.rom_file_path);
if (rom.empty()) {
exit_failure("Failed to load ROM file: " + config.rom_file_path.string() + "\n");
}
if (!RecompPort::Context::from_symbol_file(config.symbols_file_path, std::move(rom), context)) {
exit_failure("Failed to load symbols file\n");
}
auto rename_function = [&context](size_t func_index, const std::string& new_name) {
RecompPort::Function& func = context.functions[func_index];
context.functions_by_name.erase(func.name);
func.name = new_name;
context.functions_by_name[func.name] = func_index;
};
for (size_t func_index = 0; func_index < context.functions.size(); func_index++) {
RecompPort::Function& func = context.functions[func_index];
if (reimplemented_funcs.contains(func.name)) {
rename_function(func_index, func.name + "_recomp");
func.reimplemented = true;
func.ignored = true;
} else if (ignored_funcs.contains(func.name)) {
rename_function(func_index, func.name + "_recomp");
func.ignored = true;
} else if (renamed_funcs.contains(func.name)) {
rename_function(func_index, func.name + "_recomp");
func.ignored = false;
}
}
if (config.has_entrypoint) {
bool found_entrypoint = false;
for (uint32_t func_index : context.functions_by_vram[config.entrypoint]) {
auto& func = context.functions[func_index];
if (func.rom == 0x1000) {
rename_function(func_index, "recomp_entrypoint");
found_entrypoint = true;
break;
}
}
if (!found_entrypoint) {
exit_failure("No entrypoint provided in symbol file\n");
}
}
}
else {
exit_failure("Config file must provide either an elf or a symbols file\n");
}
if (elf_file.get_encoding() != ELFIO::ELFDATA2MSB) {
exit_failure("Incorrect endianness\n");
}
RecompPort::Context context{ elf_file };
context.relocatable_sections = std::move(relocatable_sections);
// Read all of the sections in the elf and look for the symbol table section
ELFIO::section* symtab_section = read_sections(context, config, elf_file);
// Search the sections to see if any are overlays or TLB-mapped
analyze_sections(context, elf_file);
// If no symbol table was found then exit
if (symtab_section == nullptr) {
exit_failure("No symbol table section found\n");
}
// Functions that weren't declared properly and thus have no size in the elf
//context.manually_sized_funcs.emplace("guMtxF2L", 0x64);
//context.manually_sized_funcs.emplace("guScaleF", 0x48);
//context.manually_sized_funcs.emplace("guTranslateF", 0x48);
//context.manually_sized_funcs.emplace("guMtxIdentF", 0x48);
//context.manually_sized_funcs.emplace("sqrtf", 0x8);
//context.manually_sized_funcs.emplace("guMtxIdent", 0x4C);
for (const auto& func_size : config.manual_func_sizes) {
context.manually_sized_funcs.emplace(func_size.func_name, func_size.size_bytes);
}
// 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, config.entrypoint, config.has_entrypoint, config.use_absolute_symbols);
// Add any manual functions
add_manual_functions(context, elf_file, config.manual_functions);
if (config.has_entrypoint && !found_entrypoint_func) {
exit_failure("Could not find entrypoint function\n");
}
fmt::print("Function count: {}\n", context.functions.size());
@ -1312,6 +1443,11 @@ int main(int argc, char** argv) {
std::vector<std::vector<uint32_t>> static_funcs_by_section{ context.sections.size() };
// TODO expose a way to dump the context from the command line. Make sure not to rename functions when doing so.
//fmt::print("Dumping context\n");
//dump_context(context, "dump.toml");
//return 0;
fmt::print("Working dir: {}\n", std::filesystem::current_path().string());
// Stub out any functions specified in the config file.