fxos/lib/disassembly.cpp

170 lines
2.8 KiB
C++

#include <fxos/disassembly.h>
#include <optional>
#include <array>
namespace FxOS {
/* Instruction map */
static std::array<std::optional<Instruction>,65536> insmap;
/* Register an instruction at a given opcode. */
void register_instruction(Instruction ins)
{
uint16_t opcode = ins.opcode;
if(insmap[opcode])
{
throw std::logic_error("opcode collision");
}
insmap[opcode] = ins;
}
//---
// Concrete (instantiated) arguments and instructions
//---
ConcreteInstructionArg::ConcreteInstructionArg():
loc {}, type {}, syscall_id {-1}
{
reg_address = -1;
}
ConcreteInstruction::ConcreteInstruction(Instruction &inst):
inst {inst}, jmptarget {}, leader {false}
{
}
//---
// Disassembler interface
//---
Disassembly::Disassembly(Target &target):
m_target {target}, m_instructions {}
{
}
bool Disassembly::hasins(uint32_t pc)
{
return m_instructions.count(pc) > 0;
}
uint32_t Disassembly::minpc()
{
uint32_t min = 0xffffffff;
for(auto &it: m_instructions)
{
if(it.first < min) min = it.first;
}
return min;
}
uint32_t Disassembly::maxpc()
{
uint32_t max = 0x00000000;
for(auto &it: m_instructions)
{
if(it.first > max) max = it.first;
}
return max;
}
ConcreteInstruction &Disassembly::readins(uint32_t pc)
{
if(pc & 1) throw std::runtime_error("Disassembly::ins_read at odd PC");
try
{
return m_instructions.at(pc);
}
catch(std::out_of_range &e)
{
uint16_t opcode = m_target.read_u16(pc);
if(!insmap[opcode])
{
throw std::runtime_error("No instruction for opcode");
}
Instruction &inst = *insmap[opcode];
ConcreteInstruction ci(inst);
m_instructions.emplace(pc, ci);
return m_instructions.at(pc);
}
}
//---
// Base pass
//---
DisassemblyPass::DisassemblyPass(Disassembly &disasm):
m_disasm(disasm)
{
}
void DisassemblyPass::enqueue(uint32_t pc)
{
if(m_next.count(pc)) return;
m_next.insert(pc);
m_queue.push(pc);
}
void DisassemblyPass::enqueue_next(uint32_t pc)
{
/* TODO: DisassemblyPass::enqueue_next is inefficient */
do pc += 2;
while(!m_disasm.hasins(pc));
enqueue(pc);
}
void DisassemblyPass::enqueue_unseen_successors(uint32_t pc,
ConcreteInstruction &inst)
{
if(!inst.isterminal() && !inst.isjump())
{
if(!m_seen.count(pc + 2)) enqueue(pc + 2);
}
if(inst.isjump() || inst.iscondjump())
{
if(!m_seen.count(inst.jmptarget)) enqueue(inst.jmptarget);
}
}
void DisassemblyPass::enqueue_all_successors(uint32_t pc,
ConcreteInstruction &inst)
{
if(!inst.isterminal() && !inst.isjump())
{
enqueue(pc + 2);
}
if(inst.isjump() || inst.iscondjump())
{
enqueue(inst.jmptarget);
}
}
void DisassemblyPass::run(uint32_t entry_pc)
{
enqueue(entry_pc);
while(m_queue.size())
{
uint32_t pc = m_queue.top();
m_queue.pop();
m_next.erase(m_next.find(pc));
ConcreteInstruction &ci = m_disasm.readins(pc);
analyze(pc, ci);
}
}
} /* namespace FxOS */