mirror of
				https://github.com/hedge-dev/XenonRecomp.git
				synced 2025-11-04 06:47:09 +00:00 
			
		
		
		
	Function branch analysis
This commit is contained in:
		@@ -2,5 +2,5 @@
 | 
			
		||||
 | 
			
		||||
project("PowerAnalyse")
 | 
			
		||||
 | 
			
		||||
add_executable(PowerAnalyse "main.cpp")
 | 
			
		||||
target_link_libraries(PowerAnalyse PRIVATE PowerUtils)
 | 
			
		||||
add_executable(PowerAnalyse "main.cpp" "function.h"  "function.cpp")
 | 
			
		||||
target_link_libraries(PowerAnalyse PRIVATE PowerUtils)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										138
									
								
								PowerAnalyse/function.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								PowerAnalyse/function.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,138 @@
 | 
			
		||||
#include "function.h"
 | 
			
		||||
#include <disasm.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <bit>
 | 
			
		||||
 | 
			
		||||
size_t function::SearchBlock(size_t address) const
 | 
			
		||||
{
 | 
			
		||||
    if (address < base)
 | 
			
		||||
    {
 | 
			
		||||
        return -1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (size_t i = 0; i < blocks.size(); i++)
 | 
			
		||||
    {
 | 
			
		||||
        const auto& block = blocks[i];
 | 
			
		||||
        const auto begin = base + block.base;
 | 
			
		||||
        const auto end = begin + size;
 | 
			
		||||
 | 
			
		||||
        if (address >= begin && address <= end)
 | 
			
		||||
        {
 | 
			
		||||
            return i;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function function::Analyze(const void* code, size_t size, size_t base)
 | 
			
		||||
{
 | 
			
		||||
    function fn{ base, 0 };
 | 
			
		||||
    auto& blocks = fn.blocks;
 | 
			
		||||
    blocks.emplace_back();
 | 
			
		||||
 | 
			
		||||
    const auto* data = (uint32_t*)code;
 | 
			
		||||
    const auto* dataStart = data;
 | 
			
		||||
    const auto* dataEnd = (uint32_t*)((uint8_t*)code + size);
 | 
			
		||||
    std::vector<size_t> blockStack{};
 | 
			
		||||
    blockStack.emplace_back();
 | 
			
		||||
 | 
			
		||||
    // TODO: Branch fallthrough
 | 
			
		||||
    for (; data <= dataEnd ; ++data)
 | 
			
		||||
    {
 | 
			
		||||
        const auto addr = base + ((data - dataStart) * sizeof(*data));
 | 
			
		||||
        if (blockStack.empty())
 | 
			
		||||
        {
 | 
			
		||||
            break; // it's hideover
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const auto instruction = std::byteswap(*data);
 | 
			
		||||
 | 
			
		||||
        const auto op = PPC_OP(instruction);
 | 
			
		||||
        const auto xop = PPC_XOP(instruction);
 | 
			
		||||
        const auto isLink = instruction & 1; // call
 | 
			
		||||
 | 
			
		||||
        ppc_insn insn;
 | 
			
		||||
        ppc::Disassemble(data, addr, insn);
 | 
			
		||||
 | 
			
		||||
        blocks[blockStack.back()].size += 4;
 | 
			
		||||
        if (op == PPC_OP_BC) // conditional branches all originate from one opcode, thanks RISC
 | 
			
		||||
        {
 | 
			
		||||
            // this one ends here
 | 
			
		||||
            blockStack.pop_back();
 | 
			
		||||
 | 
			
		||||
            // true/false paths
 | 
			
		||||
            // left block: false case
 | 
			
		||||
            // right block: true case
 | 
			
		||||
 | 
			
		||||
            const auto lBase = (addr - base) + 4;
 | 
			
		||||
            const auto rBase = insn.operands[1] - base;
 | 
			
		||||
 | 
			
		||||
            // these will be -1 if it's our first time seeing these blocks
 | 
			
		||||
            auto lBlock = fn.SearchBlock(base + lBase);
 | 
			
		||||
 | 
			
		||||
            if (lBlock == -1)
 | 
			
		||||
            {
 | 
			
		||||
                blocks.emplace_back(lBase, 0);
 | 
			
		||||
                lBlock = blocks.size() - 1;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // push this first, this gets overriden by the true case as it'd be further away
 | 
			
		||||
            if (lBlock != -1)
 | 
			
		||||
            {
 | 
			
		||||
                blockStack.emplace_back(lBlock);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!isLink) // not a call, scan this too
 | 
			
		||||
            {
 | 
			
		||||
                auto rBlock = fn.SearchBlock(base + rBase);
 | 
			
		||||
                if (rBlock == -1)
 | 
			
		||||
                {
 | 
			
		||||
                    blocks.emplace_back(insn.operands[1] - base, 0);
 | 
			
		||||
                    rBlock = blocks.size() - 1;
 | 
			
		||||
 | 
			
		||||
                    blockStack.emplace_back(rBlock);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (!blockStack.empty())
 | 
			
		||||
            {
 | 
			
		||||
                data = (dataStart + (blocks[blockStack.back()].base / sizeof(*data))) - 1; // loop will add one
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else if (op == PPC_OP_B || (op == PPC_OP_CTR && xop == 16) || instruction == 0) // b, blr, end padding
 | 
			
		||||
        {
 | 
			
		||||
            if (!isLink)
 | 
			
		||||
            {
 | 
			
		||||
                blockStack.pop_back();
 | 
			
		||||
 | 
			
		||||
                // single block with a branch means it'd be a tail call
 | 
			
		||||
                // we don't have to analyze the target in that case
 | 
			
		||||
                if (op == PPC_OP_B && blocks.size() != 1)
 | 
			
		||||
                {
 | 
			
		||||
                    const auto branchBase = insn.operands[0] - base;
 | 
			
		||||
                    const auto branchBlock = fn.SearchBlock(insn.operands[0]);
 | 
			
		||||
 | 
			
		||||
                    if (branchBlock == -1)
 | 
			
		||||
                    {
 | 
			
		||||
                        blocks.emplace_back(branchBase, 0);
 | 
			
		||||
                        blockStack.emplace_back(blocks.size() - 1);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!blockStack.empty())
 | 
			
		||||
                {
 | 
			
		||||
                    data = (dataStart + (blocks[blockStack.back()].base / sizeof(*data))) - 1;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    for (const auto& block : blocks)
 | 
			
		||||
    {
 | 
			
		||||
        // pick the block furthest away
 | 
			
		||||
        fn.size = std::max(fn.size, block.base + block.size);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return fn;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										18
									
								
								PowerAnalyse/function.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								PowerAnalyse/function.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
struct function
 | 
			
		||||
{
 | 
			
		||||
    struct block
 | 
			
		||||
    {
 | 
			
		||||
        size_t base;
 | 
			
		||||
        size_t size;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    size_t base{};
 | 
			
		||||
    size_t size{};
 | 
			
		||||
    std::vector<block> blocks{};
 | 
			
		||||
 | 
			
		||||
    size_t SearchBlock(size_t address) const;
 | 
			
		||||
    static function Analyze(const void* code, size_t size, size_t base);
 | 
			
		||||
};
 | 
			
		||||
@@ -1,90 +1,167 @@
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <file.h>
 | 
			
		||||
#include <disasm.h>
 | 
			
		||||
#include <image.h>
 | 
			
		||||
#include <format>
 | 
			
		||||
#include "function.h"
 | 
			
		||||
#include <print>
 | 
			
		||||
#include <ppc.h>
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
    const auto file = LoadFile("add.elf");
 | 
			
		||||
    const auto file = LoadFile("cond.elf");
 | 
			
		||||
    auto image = Image::ParseImage(file.data(), file.size()).value();
 | 
			
		||||
    FILE* f = fopen("add.elf.cpp", "w");
 | 
			
		||||
 | 
			
		||||
    for (const auto& section : image.sections)
 | 
			
		||||
    {
 | 
			
		||||
        image.symbols.emplace(section.name, section.base, section.size, Symbol_Section);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // image.symbols.emplace("_start", image.entry_point, 0x30, Symbol_Function);
 | 
			
		||||
    //ppc_insn insn;
 | 
			
		||||
    //uint8_t c[4] = { 0x10, 0x00, 0x59, 0xC3 };
 | 
			
		||||
    //ppc::Disassemble(c, 0x831D6C64, insn);
 | 
			
		||||
    //std::println("{:20}{}", insn.opcode->name, insn.op_str);
 | 
			
		||||
 | 
			
		||||
    std::vector<function> functions;
 | 
			
		||||
    for (const auto& section : image.sections)
 | 
			
		||||
    {
 | 
			
		||||
        if (!(section.flags & SectionFlags_Code))
 | 
			
		||||
        {
 | 
			
		||||
            continue;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        size_t base = section.base;
 | 
			
		||||
        uint8_t* data = section.data;
 | 
			
		||||
        uint8_t* dataEnd = section.data + section.size;
 | 
			
		||||
        while (data < dataEnd)
 | 
			
		||||
        {
 | 
			
		||||
            if (*(uint32_t*)data == 0)
 | 
			
		||||
            {
 | 
			
		||||
                data += 4;
 | 
			
		||||
                base += 4;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            const auto& fn = functions.emplace_back(function::Analyze(data, dataEnd - data, base));
 | 
			
		||||
            data += fn.size;
 | 
			
		||||
            base += fn.size;
 | 
			
		||||
 | 
			
		||||
            image.symbols.emplace(std::format("sub_{:X}", fn.base), fn.base, fn.size, Symbol_Function);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const auto entrySymbol = image.symbols.find(image.entry_point);
 | 
			
		||||
    assert(entrySymbol != image.symbols.end());
 | 
			
		||||
 | 
			
		||||
    const auto entrySize = entrySymbol->size;
 | 
			
		||||
    image.symbols.erase(entrySymbol);
 | 
			
		||||
 | 
			
		||||
    image.symbols.emplace("_start", image.entry_point, entrySize, Symbol_Function);
 | 
			
		||||
 | 
			
		||||
    std::println("FUNCTIONS");
 | 
			
		||||
    for (const auto& fn : functions)
 | 
			
		||||
    {
 | 
			
		||||
        std::println("\tsub_{:X}", fn.base);
 | 
			
		||||
    }
 | 
			
		||||
    std::println("");
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    std::println("SECTIONS");
 | 
			
		||||
    for (const auto& section : image.sections)
 | 
			
		||||
    {
 | 
			
		||||
        std::printf("Section %.8s\n", section.name.c_str());
 | 
			
		||||
        std::printf("\t%X-%X\n", section.base, section.base + section.size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        auto* data = (uint32_t*)section.data;
 | 
			
		||||
        auto base = section.base;
 | 
			
		||||
        const auto end = section.base + section.size;
 | 
			
		||||
    std::println("");
 | 
			
		||||
 | 
			
		||||
        if (section.flags & SectionFlags_Code)
 | 
			
		||||
    FILE* f = fopen("add.elf.cpp", "w");
 | 
			
		||||
    for (const auto& fn : functions)
 | 
			
		||||
    {
 | 
			
		||||
        auto base = fn.base;
 | 
			
		||||
        auto end = base + fn.size;
 | 
			
		||||
        auto* data = (uint32_t*)image.Find(base);
 | 
			
		||||
 | 
			
		||||
        std::string name = "";
 | 
			
		||||
        auto symbol = image.symbols.find(base);
 | 
			
		||||
        if (symbol != image.symbols.end())
 | 
			
		||||
        {
 | 
			
		||||
            ppc_insn insn;
 | 
			
		||||
            while(base < end)
 | 
			
		||||
            {
 | 
			
		||||
                ppc::Disassemble(data, 4, base, insn);
 | 
			
		||||
            name = symbol->name;
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            name = std::format("sub_{:X}", base);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
                base += 4;
 | 
			
		||||
                ++data;
 | 
			
		||||
                if (insn.opcode == nullptr)
 | 
			
		||||
        std::println(f, "void {}() {{", name);
 | 
			
		||||
 | 
			
		||||
        ppc_insn insn;
 | 
			
		||||
        while (base < end)
 | 
			
		||||
        {
 | 
			
		||||
            ppc::Disassemble(data, 4, base, insn);
 | 
			
		||||
 | 
			
		||||
            base += 4;
 | 
			
		||||
            ++data;
 | 
			
		||||
            if (insn.opcode == nullptr)
 | 
			
		||||
            {
 | 
			
		||||
                std::println(f, "\t// {:x} {}", base - 4, insn.op_str);
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
            {
 | 
			
		||||
                std::println(f, "\t// {:x} {} {}", base - 4, insn.opcode->name, insn.op_str);
 | 
			
		||||
                switch (insn.opcode->id)
 | 
			
		||||
                {
 | 
			
		||||
                    std::println(f, "// {:x} {}", base - 4, insn.op_str);
 | 
			
		||||
                }
 | 
			
		||||
                else
 | 
			
		||||
                {
 | 
			
		||||
                    std::println(f, "// {:x} {} {}", base - 4, insn.opcode->name, insn.op_str);
 | 
			
		||||
                    switch (insn.opcode->id)
 | 
			
		||||
                case PPC_INST_ADD:
 | 
			
		||||
                    std::println(f, "\tr{} = r{} + r{};", insn.operands[0], insn.operands[1], insn.operands[2]);
 | 
			
		||||
                    break;
 | 
			
		||||
                case PPC_INST_ADDI:
 | 
			
		||||
                    std::println(f, "\tr{} = r{} + {};", insn.operands[0], insn.operands[1], insn.operands[2]);
 | 
			
		||||
                    break;
 | 
			
		||||
                case PPC_INST_STWU:
 | 
			
		||||
                    std::println(f, "\tea = r{} + {};", insn.operands[2], static_cast<int32_t>(insn.operands[1]));
 | 
			
		||||
                    std::println(f, "\t*ea = byteswap(r{});", insn.operands[0]);
 | 
			
		||||
                    std::println(f, "\tr{} = ea;", insn.operands[2]);
 | 
			
		||||
                    break;
 | 
			
		||||
                case PPC_INST_STW:
 | 
			
		||||
                    std::println(f, "\t*(r{} + {}) = byteswap(r{});", insn.operands[2], static_cast<int32_t>(insn.operands[1]), insn.operands[0]);
 | 
			
		||||
                    break;
 | 
			
		||||
                case PPC_INST_MR:
 | 
			
		||||
                    std::println(f, "\tr{} = r{};", insn.operands[0], insn.operands[1]);
 | 
			
		||||
                    break;
 | 
			
		||||
                case PPC_INST_LWZ:
 | 
			
		||||
                    std::println(f, "\tr{} = *(r{} + {});", insn.operands[0], insn.operands[2], insn.operands[1]);
 | 
			
		||||
                    break;
 | 
			
		||||
                case PPC_INST_LI:
 | 
			
		||||
                    std::println(f, "\tr{} = {};", insn.operands[0], insn.operands[1]);
 | 
			
		||||
                    break;
 | 
			
		||||
                case PPC_INST_MFLR:
 | 
			
		||||
                    std::println(f, "\tr{} = lr;", insn.operands[0]);
 | 
			
		||||
                    break;
 | 
			
		||||
                case PPC_INST_MTLR:
 | 
			
		||||
                    std::println(f, "\tlr = r{};", insn.operands[0]);
 | 
			
		||||
                    break;
 | 
			
		||||
                case PPC_INST_BLR:
 | 
			
		||||
                    std::println(f, "\treturn;");
 | 
			
		||||
                    break;
 | 
			
		||||
                case PPC_INST_BL:
 | 
			
		||||
                    {
 | 
			
		||||
                    case PPC_INST_ADD:
 | 
			
		||||
                        std::println(f, "r{} = r{} + r{};", insn.operands[0], insn.operands[1], insn.operands[2]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PPC_INST_ADDI:
 | 
			
		||||
                        std::println(f, "r{} = r{} + {};", insn.operands[0], insn.operands[1], insn.operands[2]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PPC_INST_STWU:
 | 
			
		||||
                        std::println(f, "ea = r{} + {};", insn.operands[2], static_cast<int32_t>(insn.operands[1]));
 | 
			
		||||
                        std::println(f, "*ea = byteswap(r{});", insn.operands[0]);
 | 
			
		||||
                        std::println(f, "r{} = ea;", insn.operands[2]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PPC_INST_STW:
 | 
			
		||||
                        std::println(f, "*(r{} + {}) = byteswap(r{});", insn.operands[2], static_cast<int32_t>(insn.operands[1]), insn.operands[0]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PPC_INST_MR:
 | 
			
		||||
                        std::println(f, "r{} = r{};", insn.operands[0], insn.operands[1]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PPC_INST_LWZ:
 | 
			
		||||
                        std::println(f, "r{} = *(r{} + {});", insn.operands[0], insn.operands[2], insn.operands[1]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PPC_INST_LI:
 | 
			
		||||
                        std::println(f, "r{} = {};", insn.operands[0], insn.operands[1]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PPC_INST_MFLR:
 | 
			
		||||
                        std::println(f, "r{} = lr;", insn.operands[0]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PPC_INST_MTLR:
 | 
			
		||||
                        std::println(f, "lr = r{};", insn.operands[0]);
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PPC_INST_BLR:
 | 
			
		||||
                        std::println(f, "return;");
 | 
			
		||||
                        break;
 | 
			
		||||
                    case PPC_INST_BL:
 | 
			
		||||
                        std::println(f, "lr = 0x{:x};", insn.operands[0]);
 | 
			
		||||
                        std::println(f, "sub_{:x}();", insn.operands[0]);
 | 
			
		||||
                        std::string targetName = "";
 | 
			
		||||
                        auto targetSymbol = image.symbols.find(insn.operands[0]);
 | 
			
		||||
                        if (targetSymbol != image.symbols.end() && targetSymbol->type == Symbol_Function)
 | 
			
		||||
                        {
 | 
			
		||||
                            targetName = targetSymbol->name;
 | 
			
		||||
                        }
 | 
			
		||||
                        else
 | 
			
		||||
                        {
 | 
			
		||||
                            targetName = std::format("sub_{:X}", insn.operands[0]);
 | 
			
		||||
                        }
 | 
			
		||||
                        std::println(f, "\tlr = 0x{:x};", base);
 | 
			
		||||
                        std::println(f, "\t{}();", targetName);
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::println(f, "}}\n");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(f);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								tests/PowerAnalyse/cond.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								tests/PowerAnalyse/cond.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,18 @@
 | 
			
		||||
int cond(int a)
 | 
			
		||||
{
 | 
			
		||||
    if (a == 1)
 | 
			
		||||
    {
 | 
			
		||||
        return 5;
 | 
			
		||||
    }
 | 
			
		||||
    else if (a == 4)
 | 
			
		||||
    {
 | 
			
		||||
        return 9;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" int _start()
 | 
			
		||||
{
 | 
			
		||||
    return cond(0);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								tests/PowerAnalyse/cond.elf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								tests/PowerAnalyse/cond.elf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user