fxos/lib/disassembly.cpp

187 lines
4.2 KiB
C++

//---------------------------------------------------------------------------//
// 1100101 |_ mov #0, r4 __ //
// 11 |_ <0xb380 %5c4> / _|_ _____ ___ //
// 0110 |_ 3.50 -> 3.60 | _\ \ / _ (_-< //
// |_ base# + offset |_| /_\_\___/__/ //
//---------------------------------------------------------------------------//
#include <fxos/disassembly.h>
#include <fxos/util/log.h>
#include <optional>
#include <array>
namespace FxOS {
/* Instruction map */
static std::array<std::optional<AsmInstruction>, 65536> insmap;
void register_instruction(AsmInstruction const &ins)
{
uint16_t opcode = ins.opcode;
if(insmap[opcode])
FxOS_log(ERR, "opcode collision between a %s and a %s at %04x",
insmap[opcode]->mnemonic, ins.mnemonic, opcode);
else
insmap[opcode] = ins;
}
//---
// Concrete (instantiated) arguments and instructions
//---
Argument::Argument()
{
location = RelConstDomain().bottom();
value = location;
syscall_id = -1;
}
Instruction::Instruction(AsmInstruction const *inst):
inst {inst}, args {}, opcode {inst->opcode},
leader {false}, delayslot {false}, terminal {false}, jump {false},
condjump {false}, jmptarget {0xffffffff}
{
}
Instruction::Instruction(uint16_t opcode):
inst {nullptr}, args {}, opcode {opcode},
leader {false}, delayslot {false}, terminal {false}, jump {false},
condjump {false}, jmptarget {0xffffffff}
{
}
//---
// Disassembler interface
//---
Disassembly::Disassembly(VirtualSpace &_space):
instructions {}, space {_space}
{
}
bool Disassembly::hasins(uint32_t pc)
{
return this->instructions.count(pc) > 0;
}
uint32_t Disassembly::minpc()
{
if(this->instructions.empty())
return 0xffffffff;
return this->instructions.cbegin()->first;
}
uint32_t Disassembly::maxpc()
{
if(this->instructions.empty())
return 0xffffffff;
return this->instructions.crbegin()->first;
}
Instruction &Disassembly::readins(uint32_t pc)
{
if(pc & 1) {
FxOS_log(ERR, "reading instruction for disassembly at %08x", pc);
pc &= -2;
}
try {
return this->instructions.at(pc);
}
catch(std::out_of_range &e) {
uint16_t opcode = this->space.read_u16(pc);
Instruction ci(opcode);
if(insmap[opcode])
ci = Instruction(&*insmap[opcode]);
this->instructions.emplace(pc, ci);
return this->instructions.at(pc);
}
}
//---
// Base pass
//---
DisassemblyPass::DisassemblyPass(Disassembly &disasm, std::string name):
m_disasm {disasm}, m_name {name}
{
}
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, Instruction &i)
{
if(!i.terminal && !i.jump) {
if(!m_seen.count(pc + 2)) enqueue(pc + 2);
}
if(i.jump || i.condjump) {
if(!m_seen.count(i.jmptarget)) enqueue(i.jmptarget);
}
}
void DisassemblyPass::enqueue_all_successors(uint32_t pc, Instruction &i)
{
if(!i.terminal && !i.jump)
enqueue(pc + 2);
if(i.jump || i.condjump)
enqueue(i.jmptarget);
}
bool 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));
Instruction &ci = m_disasm.readins(pc);
if(!analyze(pc, ci))
return false;
m_seen.insert(pc);
}
return true;
}
//---
// Base instruction-level pass
//---
InstructionDisassemblyPass::InstructionDisassemblyPass(Disassembly &disasm,
std::string name): DisassemblyPass(disasm, name)
{
}
bool InstructionDisassemblyPass::run()
{
for(auto &pair: m_disasm.instructions) {
if(!analyze(pair.first, pair.second))
return false;
}
return true;
}
} /* namespace FxOS */