/** * @file rex/codegen/decoded_binary.h * * @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. */ #pragma once #include "ppc/instruction.h" #include #include #include #include #include #include namespace rex::codegen { using DecodedInsn = rex::codegen::ppc::Instruction; //============================================================================= // Instruction Range Iterator //============================================================================= class InsnRange { public: class Iterator { public: using iterator_category = std::forward_iterator_tag; using value_type = DecodedInsn*; using difference_type = std::ptrdiff_t; using pointer = DecodedInsn**; using reference = DecodedInsn*&; explicit Iterator(DecodedInsn* ptr) : ptr_(ptr) {} DecodedInsn* operator*() const { return ptr_; } Iterator& operator++() { ++ptr_; return *this; } Iterator operator++(int) { Iterator tmp = *this; ++ptr_; return tmp; } bool operator==(const Iterator& other) const { return ptr_ == other.ptr_; } bool operator!=(const Iterator& other) const { return ptr_ != other.ptr_; } private: DecodedInsn* ptr_; }; InsnRange(DecodedInsn* begin, DecodedInsn* end) : begin_(begin), end_(end) {} Iterator begin() const { return Iterator(begin_); } Iterator end() const { return Iterator(end_); } size_t size() const { return end_ - begin_; } bool empty() const { return begin_ == end_; } DecodedInsn* data() const { return begin_; } private: DecodedInsn* begin_; DecodedInsn* end_; }; //============================================================================= // Decoded Binary - Single-pass instruction decoder //============================================================================= class DecodedBinary { public: explicit DecodedBinary(const BinaryView& binary); // Non-copyable, non-movable (holds reference to binary) DecodedBinary(DecodedBinary&&) = delete; DecodedBinary& operator=(DecodedBinary&&) = delete; DecodedBinary(const DecodedBinary&) = delete; DecodedBinary& operator=(const DecodedBinary&) = delete; /// Decode all executable sections (call once after construction) void decode(); /// Get instruction at address (O(1) lookup) /// Returns nullptr if address not in any executable section DecodedInsn* get(uint32_t addr); const DecodedInsn* get(uint32_t addr) const; /// Get range of instructions [start, end) /// Returns empty range if addresses not in same section InsnRange range(uint32_t start, uint32_t end); /// Get raw data at address (for reading non-instruction data like jump tables) const uint8_t* rawData(uint32_t addr, size_t len) const; /// Read a value at address (with byte-swap for big-endian) template std::optional read(uint32_t addr) const; /// Get all code regions (separated by null padding) const std::vector& codeRegions() const { return codeRegions_; } /// Find code region containing address const CodeRegion* regionContaining(uint32_t addr) const; /// Check if branch from->to crosses a null boundary bool crossesNullBoundary(uint32_t from, uint32_t to) const; /// Check if address is in any code region bool isInCodeRegion(uint32_t addr) const; /// Check if instruction at address is null/invalid bool isNullPadding(uint32_t addr) const; /// Statistics size_t instructionCount() const; size_t sectionCount() const { return sections_.size(); } private: struct Section { uint32_t base; uint32_t size; std::vector instructions; std::vector data; // Copy of raw section data bool contains(uint32_t addr) const { return addr >= base && addr < base + size; } DecodedInsn* get(uint32_t addr) { if (!contains(addr)) return nullptr; uint32_t idx = (addr - base) / 4; if (idx >= instructions.size()) return nullptr; return &instructions[idx]; } const DecodedInsn* get(uint32_t addr) const { if (!contains(addr)) return nullptr; uint32_t idx = (addr - base) / 4; if (idx >= instructions.size()) return nullptr; return &instructions[idx]; } }; const BinaryView& binary_; std::vector
sections_; std::vector codeRegions_; /// Compute code regions by finding null padding boundaries void computeCodeRegions(); /// Find section containing address Section* findSection(uint32_t addr); const Section* findSection(uint32_t addr) const; }; //============================================================================= // Template Implementation //============================================================================= template std::optional DecodedBinary::read(uint32_t addr) const { const uint8_t* data = rawData(addr, sizeof(T)); if (!data) return std::nullopt; T value; std::memcpy(&value, data, sizeof(T)); // Byte-swap for big-endian PPC if constexpr (sizeof(T) == 2) { value = static_cast((value >> 8) | (value << 8)); } else if constexpr (sizeof(T) == 4) { value = static_cast(((value >> 24) & 0xFF) | ((value >> 8) & 0xFF00) | ((value << 8) & 0xFF0000) | ((value << 24) & 0xFF000000)); } else if constexpr (sizeof(T) == 8) { value = static_cast(((value >> 56) & 0xFF) | ((value >> 40) & 0xFF00) | ((value >> 24) & 0xFF0000) | ((value >> 8) & 0xFF000000) | ((value << 8) & 0xFF00000000ULL) | ((value << 24) & 0xFF0000000000ULL) | ((value << 40) & 0xFF000000000000ULL) | ((value << 56) & 0xFF00000000000000ULL)); } return value; } //============================================================================= // Helper functions for branch analysis (using existing arch::ppc) //============================================================================= /// Check if instruction is a branch inline bool isBranch(const DecodedInsn& insn) { return insn.is_branch(); } /// Check if instruction is a call (branch with link) inline bool isCall(const DecodedInsn& insn) { return insn.is_call(); } /// Check if instruction is a return (blr) inline bool isReturn(const DecodedInsn& insn) { return insn.is_return(); } /// Check if instruction is indirect branch (bcctr/bclr) inline bool isIndirect(const DecodedInsn& insn) { return insn.is_indirect_branch(); } /// Check if instruction is a terminator (unconditional branch, return, bctr) inline bool isTerminator(const DecodedInsn& insn) { return rex::codegen::ppc::is_terminator_instruction(insn.opcode); } /// Check if instruction is conditional branch inline bool isConditional(const DecodedInsn& insn) { return insn.is_conditional(); } /// Check if instruction is lis (addis rD, 0, imm) inline bool isLis(const DecodedInsn& insn) { return insn.opcode == rex::codegen::ppc::Opcode::lis; } /// Get branch target address (for direct branches) inline std::optional getBranchTarget(const DecodedInsn& insn) { return insn.branch_target; } /// Check if instruction is null/invalid (null padding) inline bool isInvalid(const DecodedInsn& insn) { uint32_t raw = static_cast(insn.code); return raw == 0x00000000 || raw == 0xFFFFFFFF || insn.opcode == rex::codegen::ppc::Opcode::kUnknown; } } // namespace rex::codegen