/** * @file rexcodegen/sig_scanner.cpp * * @copyright Copyright (c) 2026 Tom Clay * All rights reserved. * * @license BSD 3-Clause License * See LICENSE file in the project root for full license text. */ #include #include #include "codegen_logging.h" #include #include using rex::memory::load_and_swap; namespace rex::codegen { SigScanner::SigScanner(const runtime::Module& module) : module_(module) {} std::vector SigScanner::scan(const Signature& sig) { std::vector matches; // Scan all executable sections // TODO(tomc): maybe i wanna scan other sections... for (const auto& section : module_.binary_sections()) { if (!section.host_data) continue; if (!section.executable) continue; auto rangeMatches = scanRange(section.virtual_address, section.virtual_address + section.virtual_size, sig.pattern, sig.mask, sig.entryOffset); matches.insert(matches.end(), rangeMatches.begin(), rangeMatches.end()); } return matches; } std::unordered_map> SigScanner::scanAll( const std::vector& sigs) { std::unordered_map> results; for (const auto& sig : sigs) { results[sig.name] = scan(sig); } return results; } std::vector SigScanner::scanRange(uint32_t start, uint32_t end, const std::vector& pattern, const std::vector& mask, size_t entryOffset) { std::vector matches; if (pattern.empty() || pattern.size() != mask.size()) { return matches; } // Find section containing this range const auto* section = module_.FindSectionByAddress(start); if (!section || !section->host_data) { return matches; } size_t patternBytes = pattern.size() * 4; uint32_t sectionEnd = section->virtual_address + section->virtual_size; uint32_t scanEnd = std::min(end, sectionEnd); if (start + patternBytes > scanEnd) { return matches; } const uint8_t* data = section->host_data; uint32_t sectionBase = section->virtual_address; // Scan through the range (4-byte aligned for PPC) for (uint32_t addr = start; addr + patternBytes <= scanEnd; addr += 4) { bool matched = true; uint32_t offset = addr - sectionBase; for (size_t i = 0; i < pattern.size() && matched; i++) { uint32_t dword = load_and_swap(data + offset + i * 4); uint32_t masked = dword & mask[i]; uint32_t expected = pattern[i] & mask[i]; if (masked != expected) { matched = false; } } if (matched) { uint32_t entryPoint = addr + static_cast(entryOffset * 4); matches.push_back(entryPoint); REXCODEGEN_TRACE("SigScanner: pattern match at 0x{:08X}, entry=0x{:08X}", addr, entryPoint); } } return matches; } std::vector SigScanner::helperSignatures() { std::vector sigs; // __savegprlr_14 pattern: // The save helpers are a sequence of stw rN, offset(r12) instructions // followed by stw r0, 8(r12) and blr // // stw rN, offset(r12) = 0x9180XXXX where XX encodes the offset // For r14: stw r14, -0x48(r12) = 0x91CBFFB8 // // We look for the first instruction of the sequence. // stw r14, -0x48(r12) = 0x91CBFFB8 sigs.push_back({ "__savegprlr_14", {0x91CBFFB8}, // stw r14, -0x48(r12) {0xFFFFFFFF}, // Exact match 0, // Entry at pattern start std::nullopt // Size computed from stride }); // __restgprlr_14 pattern: // lwz rN, offset(r12) followed by eventually mtlr r0 and blr // lwz r14, -0x48(r12) = 0x81CBFFB8 sigs.push_back({"__restgprlr_14", {0x81CBFFB8}, // lwz r14, -0x48(r12) {0xFFFFFFFF}, // Exact match 0, std::nullopt}); // __savefpr_14 pattern: // stfd frN, offset(r12) // stfd fr14, -0x98(r12) = 0xD9CCFF68 sigs.push_back({"__savefpr_14", {0xD9CCFF68}, // stfd fr14, -0x98(r12) {0xFFFFFFFF}, 0, std::nullopt}); // __restfpr_14 pattern: // lfd frN, offset(r12) // lfd fr14, -0x98(r12) = 0xC9CCFF68 sigs.push_back({"__restfpr_14", {0xC9CCFF68}, // lfd fr14, -0x98(r12) {0xFFFFFFFF}, 0, std::nullopt}); return sigs; } std::vector SigScanner::hleSignatures() { // TODO(tomc): mayhaps have signatures memset, memmove, memcpy, strcmp patterns return {}; } } // namespace rex::codegen