diff --git a/PowerAnalyse/CMakeLists.txt b/PowerAnalyse/CMakeLists.txt index 49d0fa6..d928e2d 100644 --- a/PowerAnalyse/CMakeLists.txt +++ b/PowerAnalyse/CMakeLists.txt @@ -2,5 +2,5 @@ project("PowerAnalyse") -add_executable(PowerAnalyse "main.cpp") -target_link_libraries(PowerAnalyse PRIVATE PowerUtils) \ No newline at end of file +add_executable(PowerAnalyse "main.cpp" "function.h" "function.cpp") +target_link_libraries(PowerAnalyse PRIVATE PowerUtils) diff --git a/PowerAnalyse/function.cpp b/PowerAnalyse/function.cpp new file mode 100644 index 0000000..81843cc --- /dev/null +++ b/PowerAnalyse/function.cpp @@ -0,0 +1,138 @@ +#include "function.h" +#include +#include +#include + +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 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; +} diff --git a/PowerAnalyse/function.h b/PowerAnalyse/function.h new file mode 100644 index 0000000..0044d19 --- /dev/null +++ b/PowerAnalyse/function.h @@ -0,0 +1,18 @@ +#pragma once +#include + +struct function +{ + struct block + { + size_t base; + size_t size; + }; + + size_t base{}; + size_t size{}; + std::vector blocks{}; + + size_t SearchBlock(size_t address) const; + static function Analyze(const void* code, size_t size, size_t base); +}; diff --git a/PowerAnalyse/main.cpp b/PowerAnalyse/main.cpp index 5f08e4f..bf1902b 100644 --- a/PowerAnalyse/main.cpp +++ b/PowerAnalyse/main.cpp @@ -1,90 +1,167 @@ +#include #include #include #include -#include +#include "function.h" #include -#include 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 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(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(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(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(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); diff --git a/tests/PowerAnalyse/cond.cpp b/tests/PowerAnalyse/cond.cpp new file mode 100644 index 0000000..3e98fbc --- /dev/null +++ b/tests/PowerAnalyse/cond.cpp @@ -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); +} diff --git a/tests/PowerAnalyse/cond.elf b/tests/PowerAnalyse/cond.elf new file mode 100644 index 0000000..3e8274f Binary files /dev/null and b/tests/PowerAnalyse/cond.elf differ