rexglue-skate3/src/codegen/sig_scanner.cpp
2026-05-18 22:41:13 +01:00

167 lines
4.7 KiB
C++

/**
* @file rexcodegen/sig_scanner.cpp
*
* @copyright Copyright (c) 2026 Tom Clay <tomc@tctechstuff.com>
* All rights reserved.
*
* @license BSD 3-Clause License
* See LICENSE file in the project root for full license text.
*/
#include <rex/codegen/sig_scanner.h>
#include <rex/logging.h>
#include "codegen_logging.h"
#include <rex/memory/utils.h>
#include <rex/types.h>
using rex::memory::load_and_swap;
namespace rex::codegen {
SigScanner::SigScanner(const runtime::Module& module) : module_(module) {}
std::vector<uint32_t> SigScanner::scan(const Signature& sig) {
std::vector<uint32_t> 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<std::string, std::vector<uint32_t>> SigScanner::scanAll(
const std::vector<Signature>& sigs) {
std::unordered_map<std::string, std::vector<uint32_t>> results;
for (const auto& sig : sigs) {
results[sig.name] = scan(sig);
}
return results;
}
std::vector<uint32_t> SigScanner::scanRange(uint32_t start, uint32_t end,
const std::vector<uint32_t>& pattern,
const std::vector<uint32_t>& mask, size_t entryOffset) {
std::vector<uint32_t> 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<uint32_t>(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<uint32_t>(entryOffset * 4);
matches.push_back(entryPoint);
REXCODEGEN_TRACE("SigScanner: pattern match at 0x{:08X}, entry=0x{:08X}", addr, entryPoint);
}
}
return matches;
}
std::vector<Signature> SigScanner::helperSignatures() {
std::vector<Signature> 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<Signature> SigScanner::hleSignatures() {
// TODO(tomc): mayhaps have signatures memset, memmove, memcpy, strcmp patterns
return {};
}
} // namespace rex::codegen