Implement application of single-instruction patches
This commit is contained in:
parent
9949813018
commit
302dd091c2
|
@ -72,7 +72,7 @@ namespace RecompPort {
|
||||||
struct Function {
|
struct Function {
|
||||||
uint32_t vram;
|
uint32_t vram;
|
||||||
uint32_t rom;
|
uint32_t rom;
|
||||||
const std::span<const uint32_t> words;
|
std::vector<uint32_t> words;
|
||||||
std::string name;
|
std::string name;
|
||||||
ELFIO::Elf_Half section_index;
|
ELFIO::Elf_Half section_index;
|
||||||
bool ignored;
|
bool ignored;
|
||||||
|
|
|
@ -258,7 +258,7 @@ bool RecompPort::analyze_function(const RecompPort::Context& context, const Reco
|
||||||
uint32_t rom_addr = vram + func.rom - func.vram;
|
uint32_t rom_addr = vram + func.rom - func.vram;
|
||||||
uint32_t jtbl_word = byteswap(*reinterpret_cast<const uint32_t*>(&context.rom[rom_addr]));
|
uint32_t jtbl_word = byteswap(*reinterpret_cast<const uint32_t*>(&context.rom[rom_addr]));
|
||||||
// Check if the entry is a valid address in the current function
|
// Check if the entry is a valid address in the current function
|
||||||
if (jtbl_word < func.vram || jtbl_word > func.vram + func.words.size_bytes()) {
|
if (jtbl_word < func.vram || jtbl_word > func.vram + func.words.size() * sizeof(func.words[0])) {
|
||||||
// If it's not then this is the end of the jump table
|
// If it's not then this is the end of the jump table
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,19 @@
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
#include "recomp_port.h"
|
#include "recomp_port.h"
|
||||||
|
|
||||||
|
// Error type for invalid values in the config file.
|
||||||
|
struct value_error : public toml::exception {
|
||||||
|
public:
|
||||||
|
explicit value_error(const std::string& what_arg, const toml::source_location& loc)
|
||||||
|
: exception(loc), what_(what_arg) {
|
||||||
|
}
|
||||||
|
virtual ~value_error() noexcept override = default;
|
||||||
|
virtual const char* what() const noexcept override { return what_.c_str(); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string what_;
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<std::string> get_stubbed_funcs(const toml::value& patches_data) {
|
std::vector<std::string> get_stubbed_funcs(const toml::value& patches_data) {
|
||||||
std::vector<std::string> stubbed_funcs{};
|
std::vector<std::string> stubbed_funcs{};
|
||||||
|
|
||||||
|
@ -100,6 +113,17 @@ std::vector<RecompPort::InstructionPatch> get_instruction_patches(const toml::va
|
||||||
for (size_t patch_idx = 0; patch_idx < insn_patch_array.size(); patch_idx++) {
|
for (size_t patch_idx = 0; patch_idx < insn_patch_array.size(); patch_idx++) {
|
||||||
const toml::value& cur_patch = insn_patch_array[patch_idx];
|
const toml::value& cur_patch = insn_patch_array[patch_idx];
|
||||||
|
|
||||||
|
// Get the vram and make sure it's 4-byte aligned.
|
||||||
|
const toml::value& vram_value = toml::find<toml::value>(cur_patch, "vram");
|
||||||
|
int32_t vram = toml::get<int32_t>(vram_value);
|
||||||
|
if (vram & 0b11) {
|
||||||
|
// Not properly aligned, so throw an error (and make it look like a normal toml one).
|
||||||
|
throw value_error(toml::detail::format_underline(
|
||||||
|
std::string{ std::source_location::current().function_name() } + ": instruction vram is not 4-byte aligned!", {
|
||||||
|
{vram_value.location(), ""}
|
||||||
|
}), vram_value.location());
|
||||||
|
}
|
||||||
|
|
||||||
ret[patch_idx].func_name = toml::find<std::string>(cur_patch, "func");
|
ret[patch_idx].func_name = toml::find<std::string>(cur_patch, "func");
|
||||||
ret[patch_idx].vram = toml::find<int32_t>(cur_patch, "vram");
|
ret[patch_idx].vram = toml::find<int32_t>(cur_patch, "vram");
|
||||||
ret[patch_idx].value = toml::find<uint32_t>(cur_patch, "value");
|
ret[patch_idx].value = toml::find<uint32_t>(cur_patch, "value");
|
||||||
|
@ -153,6 +177,10 @@ RecompPort::Config::Config(const char* path) {
|
||||||
fmt::print(stderr, "Incorrect type in config file on line {}, full error:\n{}\n", err.location().line(), err.what());
|
fmt::print(stderr, "Incorrect type in config file on line {}, full error:\n{}\n", err.location().line(), err.what());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
catch (const value_error& err) {
|
||||||
|
fmt::print(stderr, "Invalid value in config file on line {}, full error:\n{}\n", err.location().line(), err.what());
|
||||||
|
return;
|
||||||
|
}
|
||||||
catch (const std::out_of_range& err) {
|
catch (const std::out_of_range& err) {
|
||||||
fmt::print(stderr, "Missing value in config file, full error:\n{}\n", err.what());
|
fmt::print(stderr, "Missing value in config file, full error:\n{}\n", err.what());
|
||||||
return;
|
return;
|
||||||
|
|
36
src/main.cpp
36
src/main.cpp
|
@ -666,10 +666,14 @@ bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, EL
|
||||||
context.section_functions[section_index].push_back(context.functions.size());
|
context.section_functions[section_index].push_back(context.functions.size());
|
||||||
}
|
}
|
||||||
context.functions_by_name[name] = context.functions.size();
|
context.functions_by_name[name] = context.functions.size();
|
||||||
|
|
||||||
|
std::vector<uint32_t> insn_words(num_instructions);
|
||||||
|
insn_words.assign(words, words + num_instructions);
|
||||||
|
|
||||||
context.functions.emplace_back(
|
context.functions.emplace_back(
|
||||||
vram,
|
vram,
|
||||||
rom_address,
|
rom_address,
|
||||||
std::span{ words, num_instructions },
|
std::move(insn_words),
|
||||||
std::move(name),
|
std::move(name),
|
||||||
section_index,
|
section_index,
|
||||||
ignored,
|
ignored,
|
||||||
|
@ -682,7 +686,7 @@ bool read_symbols(RecompPort::Context& context, const ELFIO::elfio& elf_file, EL
|
||||||
context.functions.emplace_back(
|
context.functions.emplace_back(
|
||||||
vram,
|
vram,
|
||||||
0,
|
0,
|
||||||
std::span<const uint32_t>{},
|
std::vector<uint32_t>{},
|
||||||
std::move(name),
|
std::move(name),
|
||||||
section_index,
|
section_index,
|
||||||
ignored,
|
ignored,
|
||||||
|
@ -1083,6 +1087,29 @@ int main(int argc, char** argv) {
|
||||||
context.functions[func_find->second].stubbed = true;
|
context.functions[func_find->second].stubbed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Apply any single-instruction patches.
|
||||||
|
for (const RecompPort::InstructionPatch& patch : config.instruction_patches) {
|
||||||
|
// Check if the specified function exists.
|
||||||
|
auto func_find = context.functions_by_name.find(patch.func_name);
|
||||||
|
if (func_find == context.functions_by_name.end()) {
|
||||||
|
// Function doesn't exist, present an error to the user instead of silently failing to stub it out.
|
||||||
|
// This helps prevent typos in the config file or functions renamed between versions from causing issues.
|
||||||
|
exit_failure(fmt::format("Function {} has an instruction patch but does not exist!", patch.func_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
RecompPort::Function& func = context.functions[func_find->second];
|
||||||
|
int32_t func_vram = func.vram;
|
||||||
|
|
||||||
|
// Check that the function actually contains this vram address.
|
||||||
|
if (patch.vram < func_vram || patch.vram >= func_vram + func.words.size() * sizeof(func.words[0])) {
|
||||||
|
exit_failure(fmt::format("Function {} has an instruction patch for vram 0x{:08X} but doesn't contain that vram address!", patch.vram));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the instruction index and modify the instruction.
|
||||||
|
size_t instruction_index = (static_cast<size_t>(patch.vram) - func_vram) / sizeof(uint32_t);
|
||||||
|
func.words[instruction_index] = byteswap(patch.value);
|
||||||
|
}
|
||||||
|
|
||||||
//#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];
|
||||||
|
@ -1145,10 +1172,13 @@ int main(int argc, char** argv) {
|
||||||
uint32_t rom_addr = static_cast<uint32_t>(static_func_addr - section.ram_addr + section.rom_addr);
|
uint32_t rom_addr = static_cast<uint32_t>(static_func_addr - section.ram_addr + section.rom_addr);
|
||||||
const uint32_t* func_rom_start = reinterpret_cast<const uint32_t*>(context.rom.data() + rom_addr);
|
const uint32_t* func_rom_start = reinterpret_cast<const uint32_t*>(context.rom.data() + rom_addr);
|
||||||
|
|
||||||
|
std::vector<uint32_t> insn_words((cur_func_end - static_func_addr) / sizeof(uint32_t));
|
||||||
|
insn_words.assign(func_rom_start, func_rom_start + insn_words.size());
|
||||||
|
|
||||||
RecompPort::Function func {
|
RecompPort::Function func {
|
||||||
static_func_addr,
|
static_func_addr,
|
||||||
rom_addr,
|
rom_addr,
|
||||||
std::span{ func_rom_start, (cur_func_end - static_func_addr) / sizeof(uint32_t) },
|
std::move(insn_words),
|
||||||
fmt::format("static_{}_{:08X}", section_index, static_func_addr),
|
fmt::format("static_{}_{:08X}", section_index, static_func_addr),
|
||||||
static_cast<ELFIO::Elf_Half>(section_index),
|
static_cast<ELFIO::Elf_Half>(section_index),
|
||||||
false
|
false
|
||||||
|
|
Loading…
Reference in a new issue