//--- // fxos.passes.cfg: Control Flow Graph construction //--- #include #include #include #include #include using namespace FxOS::Log; namespace FxOS { CfgPass::CfgPass(Disassembly &disasm): DisassemblyPass(disasm, "cfg") { } void CfgPass::analyze(uint32_t pc, ConcreteInstruction &ci) { /* Don't explore successors if the instruction cannot be decoded, not even pc+2. This will prevent wild overshoot. */ if(!ci.inst) return; /* 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(ci.inst->isjump() || ci.inst->iscondjump()) { auto &args = ci.inst->args; if(ci.inst->arg_count != 1 || args[0].kind != Argument::PcJump) throw LangError(pc, "invalid jump instruction"); jmptarget = (pc+4) + args[0].disp; /* Make the target of the jump a leader */ ConcreteInstruction &target = m_disasm.readins(jmptarget); target.leader = true; /* Check that it's not in a delay slot */ if(target.delayslot) throw LimitError("jump into a delay slot!"); } /* 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(ci.delayslot) { if(!ci.inst->isvaliddelayslot()) throw LangError(pc, "invalid delay slot"); } /* Handle normal instructions */ else if(!ci.inst->isdelayed()) { ci.terminal = ci.inst->isterminal(); ci.jump = ci.inst->isjump(); ci.condjump = ci.inst->iscondjump(); ci.jmptarget = jmptarget; } /* Create a new delay slot */ else { ConcreteInstruction &slot = m_disasm.readins(pc+2); if(slot.leader) throw LimitError("leader in a delay slot!"); if(!slot.inst->isvaliddelayslot()) throw LangError(pc+2, "invalid delay slot"); slot.delayslot = true; slot.terminal = ci.inst->isterminal(); slot.jump = ci.inst->isjump(); slot.condjump = ci.inst->iscondjump(); slot.jmptarget = jmptarget; } enqueue_unseen_successors(pc, ci); } } /* namespace FxOS */