177 lines
4.6 KiB
C++
177 lines
4.6 KiB
C++
//---
|
|
// fxos.disassembly: Disassembler
|
|
//---
|
|
|
|
#ifndef LIBFXOS_DISASSEMBLY_H
|
|
#define LIBFXOS_DISASSEMBLY_H
|
|
|
|
#include <fxos/lang.h>
|
|
#include <fxos/target.h>
|
|
#include <fxos/semantics.h>
|
|
|
|
#include <set>
|
|
#include <map>
|
|
#include <queue>
|
|
#include <vector>
|
|
#include <optional>
|
|
|
|
namespace FxOS {
|
|
|
|
/* Register an instruction.
|
|
@inst Instruction with [opcode] set to the binary pattern
|
|
|
|
Typically this is called by loader functions from data tables describing
|
|
instructions with parameters, not manually. See <fxos/load.h>. */
|
|
void register_instruction(Instruction ins);
|
|
|
|
/* An argument for a concrete instruction. */
|
|
struct ConcreteInstructionArg
|
|
{
|
|
ConcreteInstructionArg();
|
|
|
|
//---
|
|
// Data set by the abstract interpretation passes
|
|
//---
|
|
|
|
/* Location in CPU or memory, if that can be determined */
|
|
std::optional<Location> loc;
|
|
/* Alternatively, data type, which can sometimes be determined uniquely
|
|
even if the location is not constant */
|
|
std::optional<DataType> type;
|
|
|
|
//---
|
|
// Data set by the syscall and regs passes
|
|
//---
|
|
|
|
/* If the value is a syscall address, the syscall's id */
|
|
int syscall_id;
|
|
/* If the value is a peripheral register, its address */
|
|
uint32_t reg_address;
|
|
};
|
|
|
|
/* A loaded and annotated instruction. */
|
|
struct ConcreteInstruction
|
|
{
|
|
ConcreteInstruction(Instruction &inst);
|
|
|
|
/* What instruction it is */
|
|
Instruction &inst;
|
|
|
|
/* Argument information (contains data set by several passes) */
|
|
ConcreteInstructionArg args[2];
|
|
|
|
//---
|
|
// Data set by the pcrel pass
|
|
//---
|
|
|
|
/* Jump targets, used for jump instructions only. The target might
|
|
either be that of an inconditional jump, or the non-trivial target
|
|
of a conditional jump. In many situations the jump is forced on a
|
|
general instruction by a preceding delated branch. */
|
|
uint32_t jmptarget;
|
|
/* Whether the instruction is terminal. Be careful, as this attribute
|
|
is often forced onto delayed slot instructions. It is thus NOT the
|
|
same as isterminal(), which tells whether the mnemonic implies a
|
|
function exit. Said exit is generally delayed. */
|
|
bool terminal;
|
|
|
|
//---
|
|
// Data set by the cfg pass
|
|
//---
|
|
|
|
/* Whether this instruction is a basic block leader */
|
|
bool leader;
|
|
|
|
//---
|
|
// Methods and utilities
|
|
//---
|
|
|
|
bool isterminal() const noexcept { return inst.isterminal(); }
|
|
bool isjump() const noexcept { return inst.isjump(); }
|
|
bool iscondjump() const noexcept { return inst.iscondjump(); }
|
|
bool isdelayed() const noexcept { return inst.isdelayed(); }
|
|
};
|
|
|
|
/* Short aliases */
|
|
using CI = ConcreteInstruction;
|
|
using CIArg = ConcreteInstructionArg;
|
|
|
|
/* Disassembly interface that automatically loads code from a target */
|
|
class Disassembly
|
|
{
|
|
public:
|
|
Disassembly(Target &target);
|
|
|
|
/* Check whether an instruction has been visited so far */
|
|
bool hasins(uint32_t pc);
|
|
/* Get the minimum and maximum loaded instruction addresses */
|
|
uint32_t minpc();
|
|
uint32_t maxpc();
|
|
|
|
/* Get the storage to any concrete instruction. The instruction will be
|
|
loaded and initialized if it had not been read before. */
|
|
ConcreteInstruction &readins(uint32_t pc);
|
|
|
|
private:
|
|
/* Underlying target */
|
|
Target &m_target;
|
|
/* Loaded instructions by address */
|
|
std::map<uint32_t, ConcreteInstruction> m_instructions;
|
|
};
|
|
|
|
//---
|
|
// Disassembler passes
|
|
//---
|
|
|
|
class DisassemblyPass
|
|
{
|
|
public:
|
|
DisassemblyPass(Disassembly &disasm);
|
|
|
|
/* Analyze a single instruction, probably updating the annotations and
|
|
the state of the pass itself. Should return true if the state of the
|
|
instruction changed. */
|
|
virtual void analyze(uint32_t pc, ConcreteInstruction &inst) = 0;
|
|
|
|
/* Run the pass from the given entry point */
|
|
void run(uint32_t entry_pc);
|
|
|
|
protected:
|
|
/* Add an instruction to the queue to analyze next */
|
|
void enqueue(uint32_t pc);
|
|
/* Add the next loaded instruction in address space */
|
|
void enqueue_next(uint32_t pc);
|
|
/* Enqueue the unseen successors of this instruction */
|
|
void enqueue_unseen_successors(uint32_t pc, ConcreteInstruction &inst);
|
|
/* Enqueue all the success of this instruction */
|
|
void enqueue_all_successors(uint32_t pc, ConcreteInstruction &inst);
|
|
|
|
/* Underlying disassembly */
|
|
Disassembly &m_disasm;
|
|
|
|
private:
|
|
/* Blocks to visit next, ordered for uniqueness */
|
|
std::set<uint32_t> m_next;
|
|
std::priority_queue<uint32_t> m_queue;
|
|
|
|
/* Visited blocks */
|
|
std::set<uint32_t> m_seen;
|
|
};
|
|
|
|
|
|
class PcrelPass: public DisassemblyPass
|
|
{
|
|
PcrelPass(Disassembly &disasm);
|
|
void analyze(uint32_t pc, ConcreteInstruction &inst) override;
|
|
};
|
|
|
|
class PrintPass: public DisassemblyPass
|
|
{
|
|
PrintPass(Disassembly &disasm);
|
|
void analyze(uint32_t pc, ConcreteInstruction &inst) override;
|
|
};
|
|
|
|
} /* namespace FxOS */
|
|
|
|
#endif /* LIBFXOS_DISASSEMBLY_H */
|