Open speachy opened 5 years ago
Thank you for the report. Can you please attach the input executable that you are trying to decompile so we can try to reproduce the issue?
Here you go.
Thanks! I can confirm that retdec-bin2llvmir
fails with the reported error when decompiling the attached executable. @PeterMatula, can you investigate this? It seems that we generate an invalid LLVM IR during decoding as this error is produced by the -verify
pass after -decoder
. The issue seems to be in dec_label_pc_42c3c4
, which contains the following instruction:
br i1 %191, label %dec_label_pc_42c578, label %dec_label_pc_42c3c8
However, it seems that you can get to that instruction without going through a definition of %191
.
Xburst is not to be confused with mips32, since it is "compatible" but afaik it also has got some homebrewed instructions.
xburst is stock mips32r1 (or mips32r2) plus proprietary DSP/SIMD extensions. I do not know if the executable in question utilizes anything that isn't bog-standard mips32.
But be that as it may, one would think if retdec ran into an unknown (ie xburst-specific) instruction it would fail to decode the instruction and complain loudly at that point, rather than silently generate illegal LLVM IR? Either way, it's a bug..
Delay slots - of course, what else would it be? This is a real example of a theoretical flaw in the way we handle delay slots. I knew this could happen, but hoped it won't - and so far it did not. I will try to find a more robust solution, but who knows what else will break - those things (delay slots) are a real pain in the backside.
Yupp, they are tricky to reconstruct. Look at the snowman code and how it does handle them.
Some time ago I designed this solution to MIPS delay slots, but then nearly lost it and stopped working on it. Uploading the picture here for future reference:
The bug in this issue is caused by the fact that the delay slot is the jump target of later instructions. We should insert delay slot just after AsmInstruction instead of before branchCall. For typical delay slot, I think we can copy the delay slot instruction before and after the branch instruction.
Something like this:
void Decoder::handleDelaySlotTypical(
common::Address& addr,
capstone2llvmir::Capstone2LlvmIrTranslator::TranslationResultOne& res,
ByteData& bytes,
llvm::IRBuilder<>& irb)
{
if (!_c2l->hasDelaySlotTypical(res.capstoneInsn->id))
{
return;
}
if (!res.branchCall)
{
return;
}
assert(res.branchCall);
assert(_c2l->getDelaySlot(res.capstoneInsn->id));
assert(_c2l->getDelaySlot(res.capstoneInsn->id) == 1);
AsmInstruction ai(res.branchCall);
if (ai.isInvalid())
{
return;
}
auto oldAddr = addr;
ByteData oldBd = bytes;
auto* oldIp = res.branchCall->getParent()->getTerminator();
irb.SetInsertPoint(ai.getLlvmToAsmInstruction());
std::size_t sz = _c2l->getDelaySlot(res.capstoneInsn->id);
for (std::size_t i = 0; i < sz; ++i)
{
auto r = translate(bytes, addr, irb);
if (r.failed() || r.llvmInsn == nullptr)
{
break;
}
_llvm2capstone->emplace(r.llvmInsn, r.capstoneInsn);
}
// restore
addr = oldAddr;
bytes = oldBd;
irb.SetInsertPoint(oldIp);
}
This might result in many blocks without predecessors, because the delay slot in the original place is not executed after the branchCall. I think this is safer to do this, because there are two situations:
This happens using the latest retdec git code (d57764ab as I write this).
Executable itself:
Here's where it barfs:
The executable targets an Ingenic X1000 (mips32r2 presumably with their Xburst instructions). I can supply the executable or whatever intermediate info is needed; just let me know.