fxos/lib/passes/cfg.cpp

145 lines
4.4 KiB
C++

//---------------------------------------------------------------------------//
// 1100101 |_ mov #0, r4 __ //
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
// |_ base# + offset |_| /_\_\___/__/ //
//---------------------------------------------------------------------------//
#include <fxos/passes/cfg.h>
#include <fxos/disassembly.h>
#include <fxos/util/log.h>
#include <cassert>
namespace FxOS {
CfgPass::CfgPass(Disassembly &disasm):
InstructionPass(disasm), m_claimedInstructions {}
{
this->setAllowDiscovery(true);
}
bool CfgPass::analyzeInstruction(uint32_t pc, Instruction &i)
{
/* Don't explore successors if the instruction cannot be decoded, not
even pc+2. This will prevent wild overshoot. */
if(!i.inst) {
FxOS_log(ERR, "invalid instruction at 0x%08x: 0x%04x", pc, i.opcode);
return false;
}
m_claimedInstructions.insert(pc);
/* Compute the jump target for jump instructions. This is easy because
they are all trivially computable. (...If they are not we dub them
"terminal" to avoid the computation!) */
uint32_t jmptarget = 0xffffffff;
if(i.inst->isjump() || i.inst->iscondjump()) {
auto &args = i.inst->args;
if(i.inst->arg_count != 1 || args[0].kind != AsmArgument::PcJump) {
FxOS_log(ERR, "invalid jump instruction at 0x%08x", pc);
return false;
}
jmptarget = (pc + 4) + args[0].disp;
/* Make the target of the jump a leader */
Instruction &target = *m_disasm.getInstructionAt(jmptarget, true);
target.leader = true;
/* Check that it's not in a delay slot */
if(target.delayslot)
throw std::logic_error(format(
"0x%08x jumps into 0x%08x, which is "
"a delay slot - this is unsupported by fxos and will produce "
"garbage analysis! (x_x)",
pc, jmptarget));
}
/* If this instruction is in a delay slot, check its type. A valid
delay slot has no branching properties on its own, so nothing new to
set in the properties. */
if(i.delayslot) {
if(!i.inst->isvaliddelayslot()) {
FxOS_log(ERR, "invalid delay slot at 0x%08x", pc);
return false;
}
}
/* If it has a delay slot, create it at the next instruction */
else if(i.inst->isdelayed()) {
Instruction &slot = *m_disasm.getInstructionAt(pc + 2, true);
if(slot.leader)
throw std::logic_error(format(
"0x%08x is a leader and also a delay"
" slot - this is unsupported by fxos and will produce garbage "
"analysis! (x_x)",
pc + 2));
if(!slot.inst->isvaliddelayslot()) {
FxOS_log(ERR, "invalid delay slot at 0x%08x", pc + 2);
return false;
}
slot.delayslot = true;
slot.terminal = i.inst->isterminal();
slot.jump = i.inst->isjump();
slot.condjump = i.inst->iscondjump();
slot.jmptarget = jmptarget;
}
/* Otherwise, use standard properties */
else if(!i.inst->isdelayed()) {
i.terminal = i.inst->isterminal();
i.jump = i.inst->isjump();
i.condjump = i.inst->iscondjump();
i.jmptarget = jmptarget;
}
return true;
}
bool CfgPass::exploreFunction(uint32_t pc)
{
m_lastFunction = pc;
m_claimedInstructions.clear();
if(!m_disasm.hasFunctionAt(pc)) {
// TODO: Have proper function creation methods in Disassembly
Function func = {.address = pc, .callTargets = {}};
m_disasm.functions[pc] = func;
}
return this->analyzeFunction(pc);
}
std::set<Claim> CfgPass::resultClaims()
{
Claim base;
base.address = 0xffffffff;
base.size = 0;
base.type = Claim::Function;
base.owner = m_lastFunction;
std::set<Claim> set;
Claim c = base;
for(uint32_t pc: m_claimedInstructions) {
if(pc == c.address + c.size) {
c.size += 2;
}
else {
if(c.size > 0)
set.insert(c);
c = base;
c.address = pc;
c.size = 2;
}
}
if(c.size > 0)
set.insert(c);
return set;
}
} /* namespace FxOS */