support non-decoded instructions
This finally makes it possible to disassemble any interval without worrying about potential errors. That's some progress. By the way, now we can fully disassemble fx@3.10. Takes about 6 seconds for the analysis passes, and ~9 seconds for printing on my machine.
This commit is contained in:
parent
c1c1be2d2c
commit
2e58a8850b
|
@ -52,15 +52,22 @@ struct ConcreteInstructionArg
|
|||
/* A loaded and annotated instruction. */
|
||||
struct ConcreteInstruction
|
||||
{
|
||||
ConcreteInstruction(Instruction const &inst);
|
||||
/* Build from instruction, cannot be nullptr. */
|
||||
ConcreteInstruction(Instruction const *inst);
|
||||
/* Build from opcode, if instruction could not be decoded. */
|
||||
ConcreteInstruction(uint16_t opcode);
|
||||
|
||||
/* What instruction this is. Note that this does not determine all the
|
||||
properties below. Placement and delay slots greatly alter them. */
|
||||
Instruction const &inst;
|
||||
properties below. Placement and delay slots greatly alter them.
|
||||
This pointer is nullptr if the instruction could not be decoded. */
|
||||
Instruction const *inst;
|
||||
|
||||
/* Argument information (contains data set by several passes) */
|
||||
ConcreteInstructionArg args[2];
|
||||
|
||||
/* Opcode, valid only if inst==nullptr */
|
||||
uint16_t opcode;
|
||||
|
||||
//---
|
||||
// Data set by the cfg pass
|
||||
//---
|
||||
|
|
|
@ -32,12 +32,23 @@ ConcreteInstructionArg::ConcreteInstructionArg():
|
|||
location = RelConstDomain().bottom();
|
||||
}
|
||||
|
||||
ConcreteInstruction::ConcreteInstruction(Instruction const &inst):
|
||||
inst(inst), args(),
|
||||
ConcreteInstruction::ConcreteInstruction(Instruction const *inst):
|
||||
inst(inst), args(), opcode(),
|
||||
leader(false), delayslot(false),
|
||||
terminal(false), jump(false), condjump(false),
|
||||
jmptarget(0xffffffff)
|
||||
{
|
||||
if(!inst) throw std::logic_error(
|
||||
"ConcreteInstruction built from a null pointer");
|
||||
}
|
||||
|
||||
ConcreteInstruction::ConcreteInstruction(uint16_t opcode):
|
||||
inst(nullptr), args(), opcode(opcode),
|
||||
leader(false), delayslot(false),
|
||||
terminal(false), jump(false), condjump(false),
|
||||
jmptarget(0xffffffff)
|
||||
{
|
||||
inst = nullptr;
|
||||
}
|
||||
|
||||
//---
|
||||
|
@ -89,14 +100,11 @@ ConcreteInstruction &Disassembly::readins(uint32_t 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");
|
||||
}
|
||||
ConcreteInstruction ci(opcode);
|
||||
|
||||
Instruction const &inst = *insmap[opcode];
|
||||
if(insmap[opcode])
|
||||
ci = ConcreteInstruction(&*insmap[opcode]);
|
||||
|
||||
ConcreteInstruction ci(inst);
|
||||
m_instructions.emplace(pc, ci);
|
||||
return m_instructions.at(pc);
|
||||
}
|
||||
|
|
|
@ -19,14 +19,18 @@ CfgPass::CfgPass(Disassembly &disasm):
|
|||
|
||||
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())
|
||||
if(ci.inst->isjump() || ci.inst->iscondjump())
|
||||
{
|
||||
auto &args = ci.inst.args;
|
||||
auto &args = ci.inst->args;
|
||||
|
||||
if(args.size() != 1 || args[0].kind != Argument::PcJump)
|
||||
throw LangError(pc, "invalid jump instruction");
|
||||
|
@ -47,15 +51,15 @@ void CfgPass::analyze(uint32_t pc, ConcreteInstruction &ci)
|
|||
set in the properties. */
|
||||
if(ci.delayslot)
|
||||
{
|
||||
if(!ci.inst.isvaliddelayslot())
|
||||
if(!ci.inst->isvaliddelayslot())
|
||||
throw LangError(pc, "invalid delay slot");
|
||||
}
|
||||
/* Handle normal instructions */
|
||||
else if(!ci.inst.isdelayed())
|
||||
else if(!ci.inst->isdelayed())
|
||||
{
|
||||
ci.terminal = ci.inst.isterminal();
|
||||
ci.jump = ci.inst.isjump();
|
||||
ci.condjump = ci.inst.iscondjump();
|
||||
ci.terminal = ci.inst->isterminal();
|
||||
ci.jump = ci.inst->isjump();
|
||||
ci.condjump = ci.inst->iscondjump();
|
||||
ci.jmptarget = jmptarget;
|
||||
}
|
||||
/* Create a new delay slot */
|
||||
|
@ -64,13 +68,13 @@ void CfgPass::analyze(uint32_t pc, ConcreteInstruction &ci)
|
|||
ConcreteInstruction &slot = m_disasm.readins(pc+2);
|
||||
if(slot.leader)
|
||||
throw LimitError("leader in a delay slot!");
|
||||
if(!slot.inst.isvaliddelayslot())
|
||||
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.terminal = ci.inst->isterminal();
|
||||
slot.jump = ci.inst->isjump();
|
||||
slot.condjump = ci.inst->iscondjump();
|
||||
slot.jmptarget = jmptarget;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,14 +13,16 @@ PcrelPass::PcrelPass(Disassembly &disasm):
|
|||
|
||||
void PcrelPass::analyze(uint32_t pc, ConcreteInstruction &ci)
|
||||
{
|
||||
Instruction const &i = ci.inst;
|
||||
Instruction const *i = ci.inst;
|
||||
if(!i) return;
|
||||
|
||||
for(size_t n = 0; n < i.args.size(); n++)
|
||||
for(size_t n = 0; n < i->args.size(); n++)
|
||||
{
|
||||
Argument const &a = i.args[n];
|
||||
Argument const &a = i->args[n];
|
||||
ConcreteInstructionArg &ca = ci.args[n];
|
||||
|
||||
if(a.kind == Argument::PcRel && (i.opsize==2 || i.opsize==4))
|
||||
if(a.kind == Argument::PcRel &&
|
||||
(i->opsize == 2 || i->opsize == 4))
|
||||
{
|
||||
uint32_t addr = (pc & ~(a.opsize - 1)) + 4 + a.disp;
|
||||
ca.location = RelConstDomain().constant(addr);
|
||||
|
@ -31,8 +33,8 @@ void PcrelPass::analyze(uint32_t pc, ConcreteInstruction &ci)
|
|||
Target &t = m_disasm.target();
|
||||
uint32_t v = -1;
|
||||
|
||||
if(i.opsize == 2) v = t.read_i16(addr);
|
||||
if(i.opsize == 4) v = t.read_i32(addr);
|
||||
if(i->opsize == 2) v = t.read_i16(addr);
|
||||
if(i->opsize == 4) v = t.read_i32(addr);
|
||||
|
||||
ca.value = DataValue(IntegerType::u32);
|
||||
ca.value.write(0, 4, v);
|
||||
|
|
|
@ -25,7 +25,7 @@ PrintPass::PrintPass(Disassembly &disasm,
|
|||
|
||||
void PrintPass::analyze(uint32_t pc, ConcreteInstruction &ci)
|
||||
{
|
||||
Instruction const &i = ci.inst;
|
||||
Instruction const *i = ci.inst;
|
||||
|
||||
/* Preliminary syscall number */
|
||||
|
||||
|
@ -40,22 +40,31 @@ void PrintPass::analyze(uint32_t pc, ConcreteInstruction &ci)
|
|||
printf(">\n");
|
||||
}
|
||||
|
||||
/* Raw data if instruction cannot be decoded */
|
||||
|
||||
printf(" %08x: %04x", pc, (i ? i->opcode : ci.opcode));
|
||||
if(!i)
|
||||
{
|
||||
printf("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Mnemonic */
|
||||
|
||||
static std::map<int, std::string> suffixes = {
|
||||
{ 1, ".b" }, { 2, ".w" }, { 4, ".l" } };
|
||||
|
||||
std::string mnemonic = i.mnemonic + suffixes[i.opsize];
|
||||
if(i.args.size())
|
||||
std::string mnemonic = i->mnemonic + suffixes[i->opsize];
|
||||
if(i->args.size())
|
||||
mnemonic += std::string(8 - mnemonic.size(), ' ');
|
||||
|
||||
printf(" %08x: %04x %s", pc, ci.inst.opcode, mnemonic.c_str());
|
||||
printf(" %s", mnemonic.c_str());
|
||||
|
||||
/* Arguments */
|
||||
|
||||
for(size_t n = 0; n < i.args.size(); n++)
|
||||
for(size_t n = 0; n < i->args.size(); n++)
|
||||
{
|
||||
Argument const &a = i.args[n];
|
||||
Argument const &a = i->args[n];
|
||||
ConcreteInstructionArg const &arg = ci.args[n];
|
||||
|
||||
if(n) printf(", ");
|
||||
|
|
|
@ -16,11 +16,12 @@ void SyscallPass::analyze([[maybe_unused]] uint32_t pc,ConcreteInstruction &ci)
|
|||
/* Nothing to do if no syscall table is provided! */
|
||||
if(!m_os) return;
|
||||
|
||||
Instruction const &i = ci.inst;
|
||||
Instruction const *i = ci.inst;
|
||||
if(!i) return;
|
||||
|
||||
for(size_t n = 0; n < i.args.size(); n++)
|
||||
for(size_t n = 0; n < i->args.size(); n++)
|
||||
{
|
||||
Argument const &a = i.args[n];
|
||||
Argument const &a = i->args[n];
|
||||
ConcreteInstructionArg &ca = ci.args[n];
|
||||
|
||||
bool eligible = false;
|
||||
|
|
Loading…
Reference in New Issue