mirror of
https://github.com/N64Recomp/N64Recomp.git
synced 2025-05-18 17:34:57 +00:00
Mod function hooking (#124)
* Add function hooks to mod symbol format * Add function sizes to section function tables * Add support for function hooks in live generator * Add an option to the context to force function lookup for all non-relocated function calls * Include relocs in overlay data * Include R_MIPS_26 relocs in symbol file dumping/parsing * Add manual patch symbols (syms.ld) to the output overlay file and relocs * Fix which relocs were being emitted for patch sections * Fix sign extension issue with mfc1, add TODO for banker's rounding
This commit is contained in:
parent
36b5d9ae33
commit
38df8e3ddc
11 changed files with 269 additions and 41 deletions
|
@ -85,6 +85,8 @@ namespace N64Recomp {
|
|||
constexpr std::string_view EventSectionName = ".recomp_event";
|
||||
constexpr std::string_view ImportSectionPrefix = ".recomp_import.";
|
||||
constexpr std::string_view CallbackSectionPrefix = ".recomp_callback.";
|
||||
constexpr std::string_view HookSectionPrefix = ".recomp_hook.";
|
||||
constexpr std::string_view HookReturnSectionPrefix = ".recomp_hook_return.";
|
||||
|
||||
// Special dependency names.
|
||||
constexpr std::string_view DependencySelf = ".";
|
||||
|
@ -183,6 +185,19 @@ namespace N64Recomp {
|
|||
ReplacementFlags flags;
|
||||
};
|
||||
|
||||
enum class HookFlags : uint32_t {
|
||||
AtReturn = 1 << 0,
|
||||
};
|
||||
inline HookFlags operator&(HookFlags lhs, HookFlags rhs) { return HookFlags(uint32_t(lhs) & uint32_t(rhs)); }
|
||||
inline HookFlags operator|(HookFlags lhs, HookFlags rhs) { return HookFlags(uint32_t(lhs) | uint32_t(rhs)); }
|
||||
|
||||
struct FunctionHook {
|
||||
uint32_t func_index;
|
||||
uint32_t original_section_vrom;
|
||||
uint32_t original_vram;
|
||||
HookFlags flags;
|
||||
};
|
||||
|
||||
class Context {
|
||||
private:
|
||||
//// Reference symbols (used for populating relocations for patches)
|
||||
|
@ -208,6 +223,8 @@ namespace N64Recomp {
|
|||
std::vector<uint8_t> rom;
|
||||
// Whether reference symbols should be validated when emitting function calls during recompilation.
|
||||
bool skip_validating_reference_symbols = true;
|
||||
// Whether all function calls (excluding reference symbols) should go through lookup.
|
||||
bool use_lookup_for_all_function_calls = false;
|
||||
|
||||
//// Only used by the CLI, TODO move this to a struct in the internal headers.
|
||||
// A mapping of function name to index in the functions vector
|
||||
|
@ -236,6 +253,8 @@ namespace N64Recomp {
|
|||
std::vector<Callback> callbacks;
|
||||
// List of symbols from events, which contains the names of events that this context provides.
|
||||
std::vector<EventSymbol> event_symbols;
|
||||
// List of hooks, which contains the original function to hook and the function index to call at the hook.
|
||||
std::vector<FunctionHook> hooks;
|
||||
|
||||
// Causes functions to print their name to the console the first time they're called.
|
||||
bool trace_mode;
|
||||
|
@ -546,6 +565,7 @@ namespace N64Recomp {
|
|||
void set_all_reference_sections_relocatable() {
|
||||
all_reference_sections_relocatable = true;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class Generator;
|
||||
|
@ -562,6 +582,12 @@ namespace N64Recomp {
|
|||
|
||||
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, Context& context_out);
|
||||
std::vector<uint8_t> symbols_to_bin_v1(const Context& mod_context);
|
||||
|
||||
inline bool is_manual_patch_symbol(uint32_t vram) {
|
||||
// Zero-sized symbols between 0x8F000000 and 0x90000000 are manually specified symbols for use with patches.
|
||||
// TODO make this configurable or come up with a more sensible solution for dealing with manual symbols for patches.
|
||||
return vram >= 0x8F000000 && vram < 0x90000000;
|
||||
}
|
||||
|
||||
inline bool validate_mod_id(std::string_view str) {
|
||||
// Disallow empty ids.
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace N64Recomp {
|
|||
virtual void emit_case(int case_index, const std::string& target_label) const = 0;
|
||||
virtual void emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const = 0;
|
||||
virtual void emit_switch_close() const = 0;
|
||||
virtual void emit_return(const Context& context) const = 0;
|
||||
virtual void emit_return(const Context& context, size_t func_index) const = 0;
|
||||
virtual void emit_check_fr(int fpr) const = 0;
|
||||
virtual void emit_check_nan(int fpr, bool is_double) const = 0;
|
||||
virtual void emit_cop0_status_read(int reg) const = 0;
|
||||
|
@ -85,7 +85,7 @@ namespace N64Recomp {
|
|||
void emit_case(int case_index, const std::string& target_label) const final;
|
||||
void emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const final;
|
||||
void emit_switch_close() const final;
|
||||
void emit_return(const Context& context) const final;
|
||||
void emit_return(const Context& context, size_t func_index) const final;
|
||||
void emit_check_fr(int fpr) const final;
|
||||
void emit_check_nan(int fpr, bool is_double) const final;
|
||||
void emit_cop0_status_read(int reg) const final;
|
||||
|
|
|
@ -78,6 +78,11 @@ namespace N64Recomp {
|
|||
void (*trigger_event)(uint8_t* rdram, recomp_context* ctx, uint32_t event_index);
|
||||
int32_t *reference_section_addresses;
|
||||
int32_t *local_section_addresses;
|
||||
void (*run_hook)(uint8_t* rdram, recomp_context* ctx, size_t hook_table_index);
|
||||
// Maps function index in recompiler context to function's entry hook slot.
|
||||
std::unordered_map<size_t, size_t> entry_func_hooks;
|
||||
// Maps function index in recompiler context to function's return hook slot.
|
||||
std::unordered_map<size_t, size_t> return_func_hooks;
|
||||
};
|
||||
class LiveGenerator final : public Generator {
|
||||
public:
|
||||
|
@ -109,7 +114,7 @@ namespace N64Recomp {
|
|||
void emit_case(int case_index, const std::string& target_label) const final;
|
||||
void emit_switch_error(uint32_t instr_vram, uint32_t jtbl_vram) const final;
|
||||
void emit_switch_close() const final;
|
||||
void emit_return(const Context& context) const final;
|
||||
void emit_return(const Context& context, size_t func_index) const final;
|
||||
void emit_check_fr(int fpr) const final;
|
||||
void emit_check_nan(int fpr, bool is_double) const final;
|
||||
void emit_cop0_status_read(int reg) const final;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue