From 3dac12431a62abc8b0a8a77e853e5b76d51a4a5a Mon Sep 17 00:00:00 2001 From: York Jasper Niebuhr Date: Mon, 6 Apr 2026 20:01:44 +0200 Subject: [PATCH] Updated patchcompile to work with new selfpatch interface --- patchcompile/emit.cpp | 403 +++++++++++++++++++++++++++----------- selfpatch/src/selfpatch.c | 36 ++-- selfpatch/src/targets.c | 15 +- selfpatch/src/targets.h | 4 +- 4 files changed, 323 insertions(+), 135 deletions(-) diff --git a/patchcompile/emit.cpp b/patchcompile/emit.cpp index 1d312d8..0ee15df 100644 --- a/patchcompile/emit.cpp +++ b/patchcompile/emit.cpp @@ -1,144 +1,319 @@ #include "emit.h" #include "accumulation.h" -#include +#include + +#include +#include +#include +#include +#include +#include + +namespace { + +struct TARGET_REC { + uint32_t size; + uint32_t fieldcnt; + uint32_t fieldoff; +}; + +struct TARGET_FIELD_REC { + uint32_t offset; + uint32_t size; + uint32_t alignment; + uint32_t flags; +}; + +struct IPIN_REC { + std::string addr_sym; + uint32_t size; + uint32_t program; +}; + +struct IPIN_OP_REC { + uint32_t code; + uint32_t op0; + uint32_t op1; +}; + +struct DPIN_REC { + std::string addr_sym; + uint32_t target; +}; + +struct PROGRAM_KEY { + uint32_t target; + uint32_t field; + + bool operator==(const PROGRAM_KEY& other) const { + return target == other.target && field == other.field; + } +}; + +struct PROGRAM_KEY_HASH { + std::size_t operator()(const PROGRAM_KEY& k) const { + return (static_cast(k.target) << 32) ^ k.field; + } +}; static bool emit_header(std::ostream& out); -static bool emit_target_header(std::ostream& out, std::size_t uid, std::size_t size, std::size_t fieldcnt); -static bool emit_target_field(std::ostream& out, std::size_t offset, std::size_t size, std::size_t alignment, std::size_t flags); -static bool emit_target_randomize(std::ostream& out, std::size_t uid); -static bool emit_target(std::ostream& out, std::size_t uid, const TARGET& target); -static bool emit_ipin(std::ostream& out, const CU& cu, const IPIN& ipin); -static bool emit_dpin_component(std::ostream& out, std::size_t target, const std::string& sym, std::size_t offset); -static bool emit_dpin(std::ostream& out, const CU& cu, const DPIN& dpin); -static bool emit_exit(std::ostream& out); +static bool emit_u32_object(std::ostream& out, const char* name, uint32_t value); +static bool emit_targets(std::ostream& out, const std::vector& targets); +static bool emit_target_fields(std::ostream& out, const std::vector& fields); +static bool emit_ipins(std::ostream& out, const std::vector& ipins); +static bool emit_ipin_ops(std::ostream& out, const std::vector& ops); +static bool emit_dpins(std::ostream& out, const std::vector& dpins); + +static uint32_t intern_simple_ipin_program( + std::vector& ops, + std::unordered_map& memo, + uint32_t target, + uint32_t field) +{ + PROGRAM_KEY key{target, field}; + + auto it = memo.find(key); + if (it != memo.end()) + return it->second; + + const uint32_t start = static_cast(ops.size()); + + ops.push_back(IPIN_OP_REC{ + .code = SPSLR_IPIN_OP_ADD_OFFSET, + .op0 = target, + .op1 = field, + }); + + ops.push_back(IPIN_OP_REC{ + .code = SPSLR_IPIN_OP_PATCH, + .op0 = 0, + .op1 = 0, + }); + + memo.emplace(key, start); + return start; +} + +static bool emit_header(std::ostream& out) { + out << ".section .spslr,\"a\",@progbits\n"; + out << ".balign 8\n"; + return !!out; +} + +static bool emit_u32_object(std::ostream& out, const char* name, uint32_t value) { + out << ".globl " << name << "\n"; + out << ".type " << name << ", @object\n"; + out << ".balign 4\n"; + out << name << ":\n"; + out << "\t.long " << value << "\n"; + out << ".size " << name << ", 4\n"; + return !!out; +} + +static bool emit_targets(std::ostream& out, const std::vector& targets) { + out << ".globl spslr_targets\n"; + out << ".type spslr_targets, @object\n"; + out << ".balign 4\n"; + out << "spslr_targets:\n"; + + for (const TARGET_REC& t : targets) { + out << "\t.long " << t.size << "\n"; + out << "\t.long " << t.fieldcnt << "\n"; + out << "\t.long " << t.fieldoff << "\n"; + } + + out << ".size spslr_targets, .-spslr_targets\n"; + return !!out; +} + +static bool emit_target_fields(std::ostream& out, const std::vector& fields) { + out << ".globl spslr_target_fields\n"; + out << ".type spslr_target_fields, @object\n"; + out << ".balign 4\n"; + out << "spslr_target_fields:\n"; + + for (const TARGET_FIELD_REC& f : fields) { + out << "\t.long " << f.offset << "\n"; + out << "\t.long " << f.size << "\n"; + out << "\t.long " << f.alignment << "\n"; + out << "\t.long " << f.flags << "\n"; + } + + out << ".size spslr_target_fields, .-spslr_target_fields\n"; + return !!out; +} + +static bool emit_ipins(std::ostream& out, const std::vector& ipins) { + out << ".globl spslr_ipins\n"; + out << ".type spslr_ipins, @object\n"; + out << ".balign 8\n"; + out << "spslr_ipins:\n"; + + for (const IPIN_REC& ip : ipins) { + out << "\t.quad " << ip.addr_sym << "\n"; + out << "\t.long " << ip.size << "\n"; + out << "\t.long " << ip.program << "\n"; + } + + out << ".size spslr_ipins, .-spslr_ipins\n"; + return !!out; +} + +static bool emit_ipin_ops(std::ostream& out, const std::vector& ops) { + out << ".globl spslr_ipin_ops\n"; + out << ".type spslr_ipin_ops, @object\n"; + out << ".balign 4\n"; + out << "spslr_ipin_ops:\n"; + + for (const IPIN_OP_REC& op : ops) { + out << "\t.long " << op.code << "\n"; + out << "\t.long " << op.op0 << "\n"; + out << "\t.long " << op.op1 << "\n"; + } + + out << ".size spslr_ipin_ops, .-spslr_ipin_ops\n"; + return !!out; +} + +static bool emit_dpins(std::ostream& out, const std::vector& dpins) { + out << ".globl spslr_dpins\n"; + out << ".type spslr_dpins, @object\n"; + out << ".balign 8\n"; + out << "spslr_dpins:\n"; + + for (const DPIN_REC& dp : dpins) { + out << "\t.quad " << dp.addr_sym << "\n"; + out << "\t.long " << dp.target << "\n"; + } + + out << ".size spslr_dpins, .-spslr_dpins\n"; + return !!out; +} + +} bool emit_patcher_program_asm(std::ostream& out) { if (!emit_header(out)) return false; - // Dump all targets - for (const auto& [uid, target] : targets) { - if (!emit_target(out, uid, target)) - return false; - } + // Important: target UID == index in spslr_targets[] for the new interface. + std::vector target_recs(targets.size()); + std::vector field_recs; + field_recs.reserve(64); - // Dump all pins - for (const auto& [uid, cu] : units) { - for (const auto& [sym, pin] : cu.ipins) { - if (!emit_ipin(out, cu, pin)) - return false; + for (uint32_t uid = 0; uid < static_cast(targets.size()); ++uid) { + if (!targets.contains(uid)) + return false; + + const TARGET& target = targets.at(uid); + + TARGET_REC trec{}; + trec.size = static_cast(target.size); + trec.fieldoff = static_cast(field_recs.size()); + trec.fieldcnt = static_cast(target.fields.size()); + + for (const auto& [off, field] : target.fields) { + (void)off; + field_recs.push_back(TARGET_FIELD_REC{ + .offset = static_cast(field.offset), + .size = static_cast(field.size), + .alignment = static_cast(field.alignment), + .flags = static_cast(field.flags), + }); } - for (const auto& [sym, pin] : cu.dpins) { - if (!emit_dpin(out, cu, pin)) + target_recs[uid] = trec; + } + + std::vector ipin_recs; + std::vector ipin_ops; + std::unordered_map program_memo; + + std::vector dpin_recs; + + for (const auto& [cu_uid, cu] : units) { + (void)cu_uid; + + for (const auto& [sym, ipin] : cu.ipins) { + const uint32_t global_target = static_cast(cu.local_targets.at(ipin.local_target)); + + if (!targets.contains(global_target)) return false; + + const TARGET& target = targets.at(global_target); + + if (!target.fields.contains(ipin.field_offset)) + return false; + + const FIELD& field = target.fields.at(ipin.field_offset); + + const uint32_t program = intern_simple_ipin_program( + ipin_ops, + program_memo, + global_target, + static_cast(field.idx) + ); + + ipin_recs.push_back(IPIN_REC{ + .addr_sym = ipin.symbol, + .size = static_cast(ipin.imm_size), + .program = program, + }); + } + + for (const auto& [sym, dpin] : cu.dpins) { + std::vector sorted_components(dpin.components.begin(), dpin.components.end()); + + std::sort( + sorted_components.begin(), + sorted_components.end(), + [](const DPIN::COMPONENT& a, const DPIN::COMPONENT& b) { + return a.level > b.level; + } + ); + + for (const DPIN::COMPONENT& component : sorted_components) { + const uint32_t global_target = + static_cast(cu.local_targets.at(component.target)); + + std::string addr = dpin.symbol; + if (component.offset != 0) + addr += " + " + std::to_string(component.offset); + + dpin_recs.push_back(DPIN_REC{ + .addr_sym = std::move(addr), + .target = global_target, + }); + } } } - // Exit patcher program - if (!emit_exit(out)) + if (!emit_u32_object(out, "spslr_target_cnt", static_cast(target_recs.size()))) + return false; + if (!emit_targets(out, target_recs)) return false; - return true; -} - -bool emit_header(std::ostream& out) { - out << ".section .spslr,\"a\",@progbits\n"; - out << ".balign 1\n"; - out << ".globl __spslr_program\n"; - out << ".type __spslr_program, @object\n"; - out << "__spslr_program:\n"; - return !!out; -} - -bool emit_target_header(std::ostream& out, std::size_t uid, std::size_t size, std::size_t fieldcnt) { - out << "\t.byte " << OPCODE_SPSLR_TARGET << "\n"; - out << "\t.long " << uid << "\n"; - out << "\t.long " << size << "\n"; - out << "\t.long " << fieldcnt << "\n"; - return !!out; -} - -bool emit_target_field(std::ostream& out, std::size_t offset, std::size_t size, std::size_t alignment, std::size_t flags) { - out << "\t.byte " << OPCODE_SPSLR_FIELD << "\n"; - out << "\t.long " << offset << "\n"; - out << "\t.long " << size << "\n"; - out << "\t.long " << alignment << "\n"; - out << "\t.long " << flags << "\n"; - return !!out; -} - -bool emit_target_randomize(std::ostream& out, std::size_t uid) { - out << "\t.byte " << OPCODE_SPSLR_RANDOMIZE << "\n"; - out << "\t.long " << uid << "\n"; - return !!out; -} - -bool emit_target(std::ostream& out, std::size_t uid, const TARGET& target) { - if (!emit_target_header(out, uid, target.size, target.fields.size())) + if (!emit_u32_object(out, "spslr_target_field_cnt", static_cast(field_recs.size()))) + return false; + if (!emit_target_fields(out, field_recs)) return false; - for (const auto& [off, field] : target.fields) { - // Note -> Might want to do explicit flag mapping from pinpoint to selfpatch - if (!emit_target_field(out, field.offset, field.size, field.alignment, field.flags)) - return false; - } - - if (!emit_target_randomize(out, uid)) + if (!emit_u32_object(out, "spslr_ipin_cnt", static_cast(ipin_recs.size()))) + return false; + if (!emit_ipins(out, ipin_recs)) return false; - return true; -} - -bool emit_ipin(std::ostream& out, const CU& cu, const IPIN& ipin) { - std::size_t global_target = cu.local_targets.at(ipin.local_target); - - if (!targets.contains(global_target)) + if (!emit_u32_object(out, "spslr_ipin_op_cnt", static_cast(ipin_ops.size()))) + return false; + if (!emit_ipin_ops(out, ipin_ops)) return false; - const TARGET& target = targets.at(global_target); - - if (!target.fields.contains(ipin.field_offset)) + if (!emit_u32_object(out, "spslr_dpin_cnt", static_cast(dpin_recs.size()))) + return false; + if (!emit_dpins(out, dpin_recs)) return false; - const FIELD& field = target.fields.at(ipin.field_offset); - - out << "\t.byte " << OPCODE_SPSLR_IPATCH << "\n"; - out << "\t.quad " << ipin.symbol << "\n"; - out << "\t.long " << ipin.imm_size << "\n"; - out << "\t.long " << global_target << "\n"; - out << "\t.long " << field.idx << "\n"; - return !!out; -} - -bool emit_dpin_component(std::ostream& out, std::size_t target, const std::string& sym, std::size_t offset) { - out << "\t.byte " << OPCODE_SPSLR_DPATCH << "\n"; - out << "\t.quad " << sym; - if (offset != 0) - out << " + " << offset; - out << "\n"; - out << "\t.long " << target << "\n"; - return !!out; -} - -bool emit_dpin(std::ostream& out, const CU& cu, const DPIN& dpin) { - std::list sorted_components = dpin.components; - sorted_components.sort([](const DPIN::COMPONENT& a, const DPIN::COMPONENT& b) { - return a.level > b.level; // Descending order to handle deeper nested first - }); - - for (const DPIN::COMPONENT& component : sorted_components) { - std::size_t global_target = cu.local_targets.at(component.target); - if (!emit_dpin_component(out, global_target, dpin.symbol, component.offset)) - return false; - } - - return true; -} - -bool emit_exit(std::ostream& out) { - out << "\t.byte " << OPCODE_SPSLR_EXIT << "\n"; return !!out; } diff --git a/selfpatch/src/selfpatch.c b/selfpatch/src/selfpatch.c index 5f665f4..8787ca3 100644 --- a/selfpatch/src/selfpatch.c +++ b/selfpatch/src/selfpatch.c @@ -18,6 +18,8 @@ static void reorder_object(void* dst, const void* src, uint32_t target); static int64_t spslr_calculate_ipin_value(uint32_t start); void spslr_selfpatch(void) { + seed_rand_time(); + spslr_selfpatch_load_targets(); spslr_selfpatch_randomize_targets(); spslr_selfpatch_patch_dpins(); @@ -113,7 +115,7 @@ void spslr_selfpatch_patch_ipins(void) { spslr_env_poke_text_8((void*)ip->addr, (uint8_t)value); break; case 2: - spslr_env_poke_text_64((void*)ip->addr, (uint16_t)value); + spslr_env_poke_text_16((void*)ip->addr, (uint16_t)value); break; case 4: spslr_env_poke_text_32((void*)ip->addr, (uint32_t)value); @@ -139,58 +141,64 @@ int64_t spslr_calculate_ipin_value(uint32_t start) { spslr_env_panic("ipin op out of bounds"); #endif + int end_flag = 0; + const struct spslr_ipin_op* op = &spslr_ipin_ops[pc++]; switch (op->code) { case SPSLR_IPIN_OP_PATCH: + end_flag = 1; break; case SPSLR_IPIN_OP_ADD_INITIAL_OFFSET: { uint32_t initial_offset; - if (spslr_get_target_field_ordered(op->op0.add_initial_offset_target, - op->op1.add_initial_offset_field, NULL, NULL, &initial_offset)) + if (spslr_get_randomized_field_offset(op->op0.add_initial_offset_target, + op->op1.add_initial_offset_field, NULL, &initial_offset)) spslr_env_panic("failed to get initial field offset"); res += initial_offset; } - continue; + break; case SPSLR_IPIN_OP_ADD_OFFSET: { uint32_t offset; - if (spslr_get_target_field_ordered(op->op0.add_initial_offset_target, - op->op1.add_initial_offset_field, &offset, NULL, NULL)) + if (spslr_get_randomized_field_offset(op->op0.add_initial_offset_target, + op->op1.add_initial_offset_field, &offset, NULL)) spslr_env_panic("failed to get initial field offset"); res += offset; } - continue; + break; case SPSLR_IPIN_OP_SUB_INITIAL_OFFSET: { uint32_t initial_offset; - if (spslr_get_target_field_ordered(op->op0.add_initial_offset_target, - op->op1.add_initial_offset_field, NULL, NULL, &initial_offset)) + if (spslr_get_randomized_field_offset(op->op0.add_initial_offset_target, + op->op1.add_initial_offset_field, NULL, &initial_offset)) spslr_env_panic("failed to get initial field offset"); res -= initial_offset; } - continue; + break; case SPSLR_IPIN_OP_SUB_OFFSET: { uint32_t offset; - if (spslr_get_target_field_ordered(op->op0.add_initial_offset_target, - op->op1.add_initial_offset_field, &offset, NULL, NULL)) + if (spslr_get_randomized_field_offset(op->op0.add_initial_offset_target, + op->op1.add_initial_offset_field, &offset, NULL)) spslr_env_panic("failed to get initial field offset"); res -= offset; } - continue; + break; case SPSLR_IPIN_OP_ADD_CONST: res += op->op0.add_const_value; - continue; + break; #ifdef SPSLR_SANITY_CHECK default: spslr_env_panic("invalid ipin op"); #endif } + + if (end_flag) + break; } return res; diff --git a/selfpatch/src/targets.c b/selfpatch/src/targets.c index 54325ed..13e9682 100644 --- a/selfpatch/src/targets.c +++ b/selfpatch/src/targets.c @@ -5,7 +5,7 @@ #include #include -static void seed_rand_time() { +void seed_rand_time() { srand(time(NULL)); } @@ -333,8 +333,6 @@ int spslr_randomize(uint32_t target) { if (t->field_count != t->present_field_count) return -1; - seed_rand_time(); // Note -> this is obviously not sufficient - uint32_t shuffle_count = t->field_count * 2; for (uint32_t i = 0; i < shuffle_count; i++) target_shuffle_one(t); @@ -355,9 +353,9 @@ int spslr_randomize(uint32_t target) { return 0; } -int spslr_get_randomized_field_offset(uint32_t target, uint32_t field, uint32_t* offset) { +int spslr_get_randomized_field_offset(uint32_t target, uint32_t field, uint32_t* offset, uint32_t* initial_offset) { const struct Target* t = find_target(target); - if (!t || !offset) + if (!t) return -1; if (field >= t->field_count) @@ -366,7 +364,12 @@ int spslr_get_randomized_field_offset(uint32_t target, uint32_t field, uint32_t* if (!t->final_fields) return -1; - *offset = t->final_fields[field].offset; + if (offset) + *offset = t->final_fields[field].offset; + + if (initial_offset) + *initial_offset = t->final_fields[field].initial_offset; + return 0; } diff --git a/selfpatch/src/targets.h b/selfpatch/src/targets.h index 097788b..bb61441 100644 --- a/selfpatch/src/targets.h +++ b/selfpatch/src/targets.h @@ -3,11 +3,13 @@ #include +void seed_rand_time(); + int spslr_register_target(uint32_t uid, uint32_t size, uint32_t fieldcnt); int spslr_register_target_field(uint32_t target, uint32_t offset, uint32_t size, uint32_t alignment, uint32_t flags); int spslr_randomize(uint32_t target); -int spslr_get_randomized_field_offset(uint32_t target, uint32_t field, uint32_t* offset); +int spslr_get_randomized_field_offset(uint32_t target, uint32_t field, uint32_t* offset, uint32_t* initial_offset); int spslr_get_target_size(uint32_t target, uint32_t* size); int spslr_get_target_fieldcnt(uint32_t target, uint32_t* cnt); int spslr_get_target_field_ordered(uint32_t target, uint32_t field, uint32_t* offset,